freeswitch/libs/sofia-sip/libsofia-sip-ua/http/http_basic.c

1635 lines
46 KiB
C

/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2005 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@CFILE http_basic.c
* @brief HTTP basic header
*
* The file @b http_basic.c contains implementation of header classes for
* basic HTTP headers, like request and status lines, payload, @b Call-ID, @b
* CSeq, @b Contact, @b Content-Length, @b Date, @b Expires, @b From, @b
* Route, @b Record-Route, @b To, and @b Via.
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
*
* @date Created: Tue Jun 13 02:57:51 2000 ppessi
*/
#include "config.h"
/* Avoid casting http_t to msg_pub_t and http_header_t to msg_header_t */
#define MSG_PUB_T struct http_s
#define MSG_HDR_T union http_header_u
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/http_parser.h>
#include <sofia-sip/http_header.h>
#include <sofia-sip/http_status.h>
#include <sofia-sip/msg_mime_protos.h>
#include <sofia-sip/msg_date.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
/* ====================================================================== */
/**@HTTP_HEADER http_request HTTP request line.
*
* The HTTP request line contains the method, URL, and an optional HTTP
* protocol version. The missing version indicates version 0.9 without any
* request headers.
*/
/**
* Parse request line of a HTTP message.
*
* The function @c http_request_d() parses the request line from a a HTTP
* message.
*/
issize_t http_request_d(su_home_t *home, http_header_t *h, char *s, isize_t slen)
{
http_request_t *rq = h->sh_request;
char *uri, *version;
if (msg_firstline_d(s, &uri, &version) < 0 ||
(rq->rq_method = http_method_d(&s, &rq->rq_method_name)) < 0 || *s ||
url_d(rq->rq_url, uri) < 0 ||
(http_version_d(&version, &rq->rq_version) < 0 || version[0]))
return -1;
return 0;
}
/**
* Encode a HTTP request line.
*
* The function @c http_request_e() prints a HTTP request line.
*/
issize_t http_request_e(char b[], isize_t bsiz, http_header_t const *h, int flags)
{
http_request_t const *rq = h->sh_request;
return snprintf(b, bsiz, "%s " URL_FORMAT_STRING "%s%s" CRLF,
rq->rq_method_name,
URL_PRINT_ARGS(rq->rq_url),
rq->rq_version ? " " : "",
rq->rq_version ? rq->rq_version : "");
}
isize_t http_request_dup_xtra(http_header_t const *h, isize_t offset)
{
http_request_t const *rq = h->sh_request;
offset += url_xtra(rq->rq_url);
if (!rq->rq_method)
offset += MSG_STRING_SIZE(rq->rq_method_name);
if (rq->rq_version)
offset += http_version_xtra(rq->rq_version);
return offset;
}
/** Duplicate one request header. */
char *http_request_dup_one(http_header_t *dst, http_header_t const *src,
char *b, isize_t xtra)
{
http_request_t *rq = dst->sh_request;
http_request_t const *o = src->sh_request;
char *end = b + xtra;
URL_DUP(b, end, rq->rq_url, o->rq_url);
if (!(rq->rq_method = o->rq_method))
MSG_STRING_DUP(b, rq->rq_method_name, o->rq_method_name);
else
rq->rq_method_name = o->rq_method_name;
http_version_dup(&b, &rq->rq_version, o->rq_version);
assert(b <= end);
return b;
}
/** Create a request line object.
*
* Note that version string is not copied; it @b MUST remain constant during
* lifetime of the @c http_request_t object. You can use constants
* http_version_1_1 or http_version_1_0 declared in <http_header.h>.
*/
http_request_t *http_request_create(su_home_t *home,
http_method_t method, char const *name,
url_string_t const *url,
char const *version)
{
size_t xtra;
http_request_t *rq;
if (method)
name = http_method_name(method, name);
if (!name)
return NULL;
xtra = url_xtra(url->us_url) + (method ? 0 : strlen(name) + 1);
rq = msg_header_alloc(home, http_request_class, (isize_t)xtra)->sh_request;
if (rq) {
char *b = (char *)(rq + 1), *end = b + xtra;
rq->rq_method = method;
rq->rq_method_name = name;
if (!method)
MSG_STRING_DUP(b, rq->rq_method_name, name);
URL_DUP(b, end, rq->rq_url, url->us_url);
rq->rq_version = version ? version : HTTP_VERSION_CURRENT;
assert(b == end);
}
return rq;
}
msg_hclass_t http_request_class[] =
HTTP_HEADER_CLASS(request, NULL, rq_common, single_critical, request);
/* ====================================================================== */
/**@HTTP_HEADER http_status HTTP status line.
*
* The HTTP status line contains the HTTP protocol version, a reason code
* (100..599) and reason phrase.
*/
/** Parse status line */
issize_t http_status_d(su_home_t *home, http_header_t *h, char *s, isize_t slen)
{
http_status_t *st = h->sh_status;
char *status, *phrase;
uint32_t code;
if (msg_firstline_d(s, &status, &phrase) < 0 ||
http_version_d(&s, &st->st_version) < 0 || *s ||
msg_uint32_d(&status, &code) == -1 ||
status[0])
return -1;
st->st_status = code;
st->st_phrase = phrase;
return 0;
}
issize_t http_status_e(char b[], isize_t bsiz, http_header_t const *h, int flags)
{
http_status_t const *st = h->sh_status;
char const *phrase = st->st_phrase;
if (phrase == NULL)
phrase = "";
if (st->st_version)
return snprintf(b, bsiz, "%s %03u %s" CRLF,
st->st_version,
st->st_status,
phrase);
else
return snprintf(b, bsiz, "%03u %s" CRLF,
st->st_status,
phrase);
}
/** Extra size of a http_status_t object. */
isize_t http_status_dup_xtra(http_header_t const *h, isize_t offset)
{
if (h->sh_status->st_version)
offset += http_version_xtra(h->sh_status->st_version);
offset += MSG_STRING_SIZE(h->sh_status->st_phrase);
return offset;
}
/** Duplicate one status header. */
char *http_status_dup_one(http_header_t *dst, http_header_t const *src,
char *b, isize_t xtra)
{
http_status_t *st = dst->sh_status;
http_status_t const *o = src->sh_status;
char *end = b + xtra;
if (o->st_version)
http_version_dup(&b, &st->st_version, o->st_version);
st->st_status = o->st_status;
MSG_STRING_DUP(b, st->st_phrase, o->st_phrase);
assert(b <= end); (void)end;
return b;
}
/** Create a status line object.
*
* Note that version is not copied; it @b MUST remain constant during
* lifetime of the @c http_status_t object.
*/
http_status_t *http_status_create(su_home_t *home,
unsigned status,
char const *phrase,
char const *version)
{
http_status_t *st;
if (phrase == NULL && (phrase = http_status_phrase(status)) == NULL)
return NULL;
if ((st = msg_header_alloc(home, http_status_class, 0)->sh_status)) {
st->st_status = status;
st->st_phrase = phrase;
st->st_version = version ? version : HTTP_VERSION_CURRENT;
}
return st;
}
msg_hclass_t http_status_class[] =
HTTP_HEADER_CLASS(status, NULL, st_common, single_critical, status);
/* ====================================================================== */
/**@HTTP_HEADER http_accept Accept header.
*
* We use MIME Accept header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_accept_charset Accept-Charset header.
*
* We use MIME Accept-Charset header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_accept_encoding Accept-Encoding header.
*
* We use MIME Accept-Encoding header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_accept_language Accept-Language header.
*
* We use MIME Accept-Language header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_accept_ranges Accept-Ranges header. */
#define http_accept_ranges_d msg_list_d
#define http_accept_ranges_e msg_list_e
msg_hclass_t http_accept_ranges_class[] =
HTTP_HEADER_CLASS_LIST(accept_ranges, "Accept-Ranges", list);
/* ====================================================================== */
/**@HTTP_HEADER http_age Age header. */
#define http_age_d msg_numeric_d
#define http_age_e msg_numeric_e
#define http_age_dup_xtra msg_default_dup_xtra
#define http_age_dup_one msg_default_dup_one
msg_hclass_t http_age_class[] =
HTTP_HEADER_CLASS(age, "Age", x_common, single, age);
/* ====================================================================== */
/**@HTTP_HEADER http_allow Allow header. */
#define http_allow_d msg_list_d
#define http_allow_e msg_list_e
msg_hclass_t http_allow_class[] =
HTTP_HEADER_CLASS_LIST(allow, "Allow", list);
/* ====================================================================== */
/**@HTTP_HEADER http_authentication_info Authentication-Info header.
* @sa RFC 2617
*/
#define http_authentication_info_d msg_list_d
#define http_authentication_info_e msg_list_e
#define http_authentication_info_dup_xtra msg_list_dup_xtra
#define http_authentication_info_dup_one msg_list_dup_one
msg_hclass_t http_authentication_info_class[] =
HTTP_HEADER_CLASS(authentication_info, "Authentication-Info",
ai_params, list, authentication_info);
/* ====================================================================== */
/**@HTTP_HEADER http_authorization Authorization header.
*
* We use MIME Authorization header.
*/
#define http_authorization_d msg_auth_d
#define http_authorization_e msg_auth_e
msg_hclass_t http_authorization_class[] =
HTTP_HEADER_CLASS_AUTH(authorization, "Authorization", single);
/* ====================================================================== */
/**@HTTP_HEADER http_cache_control Cache-Control header. */
#define http_cache_control_d msg_list_d
#define http_cache_control_e msg_list_e
msg_hclass_t http_cache_control_class[] =
HTTP_HEADER_CLASS_LIST(cache_control, "Cache-Control", list);
/* ====================================================================== */
/**@HTTP_HEADER http_connection Connection header. */
#define http_connection_d msg_list_d
#define http_connection_e msg_list_e
msg_hclass_t http_connection_class[] =
HTTP_HEADER_CLASS_LIST(connection, "Connection", list_critical);
/* ====================================================================== */
/**@HTTP_HEADER http_content_encoding Content-Encoding header.
*
* We use MIME Content-Encoding header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_content_language Content-Language header.
*
* We use MIME Content-Language header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_content_length Content-Length header.
*
* We use MIME Content-Length header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_content_location Content-Location header.
*
* We use MIME Content-Location header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_content_md5 Content-MD5 header.
*
* We use MIME Content-MD5 header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_content_range Content-Range header.
*
* The Content-Range entity-header is sent with a partial entity-body to
* specify where in the full entity-body the partial body should be
* applied. Its syntax is defined in [H14.16] as follows:
*
* @code
* Content-Range = "Content-Range" ":" content-range-spec
* content-range-spec = byte-content-range-spec
* byte-content-range-spec = bytes-unit SP
* byte-range-resp-spec "/"
* ( instance-length | "*" )
*
* byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
* | "*"
* instance-length = 1*DIGIT
* @endcode
*
*/
/**@ingroup http_content_range
* @typedef typedef struct http_content_range_s http_content_range_t;
*
* The structure #http_content_range_t contains representation of
* @b Content-Range header.
*
* The #http_content_range_t is defined as follows:
* @code
* typedef struct {
* msg_common_t cr_common[1];
* msg_error_t *cr_next;
* off_t cr_first; // First-byte-pos
* off_t cr_last; // Last-byte-pos
* off_t cr_length; // Instance-length
* } http_content_range_t;
* @endcode
*/
issize_t http_content_range_d(su_home_t *home, http_header_t *h, char *s, isize_t slen)
{
http_content_range_t *cr = h->sh_content_range;
if (strncasecmp(s, "bytes", 5))
return -1;
s += 5; skip_lws(&s);
if (s[0] == '*') {
cr->cr_first = cr->cr_last = (http_off_t)-1;
s++; skip_lws(&s);
} else {
if (msg_delta_d((char const **)&s, &cr->cr_first) < 0)
return -1;
if (s[0] != '-')
return -1;
s++; skip_lws(&s);
if (msg_delta_d((char const **)&s, &cr->cr_last) < 0)
return -1;
}
if (s[0] != '/')
return -1;
s++; skip_lws(&s);
if (s[0] == '*') {
cr->cr_length = (http_off_t)-1;
s++; skip_lws(&s);
} else {
if (msg_delta_d((char const **)&s, &cr->cr_length) < 0)
return -1;
}
return s[0] ? -1 : 0;
}
issize_t http_content_range_e(char b[], isize_t bsiz, http_header_t const *h, int f)
{
http_content_range_t const *cr = h->sh_content_range;
if (cr->cr_first == (http_off_t)-1) {
if (cr->cr_length == (http_off_t)-1)
return snprintf(b, bsiz, "bytes */*");
else
return snprintf(b, bsiz, "bytes */%lu", cr->cr_length);
}
else {
if (cr->cr_length == (http_off_t)-1)
return snprintf(b, bsiz, "bytes %lu-%lu/*", cr->cr_first, cr->cr_last);
else
return snprintf(b, bsiz, "bytes %lu-%lu/%lu",
cr->cr_first, cr->cr_last, cr->cr_length);
}
}
msg_hclass_t http_content_range_class[] =
HTTP_HEADER_CLASS(content_range, "Content-Range", cr_common, single, default);
/* ====================================================================== */
/**@HTTP_HEADER http_content_type Content-Type header.
*
* We use MIME Content-Type header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_date Date header.
*
* The Date header field reflects the time when the request or response was
* first sent. Its syntax is defined in [H14.18] as
* follows:
*
* @code
* Date = "Date" HCOLON HTTP-date
* HTTP-date = rfc1123-date
* rfc1123-date = wkday "," SP date1 SP time SP "GMT"
* date1 = 2DIGIT SP month SP 4DIGIT
* ; day month year (e.g., 02 Jun 1982)
* time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
* ; 00:00:00 - 23:59:59
* wkday = "Mon" / "Tue" / "Wed"
* / "Thu" / "Fri" / "Sat" / "Sun"
* month = "Jan" / "Feb" / "Mar" / "Apr"
* / "May" / "Jun" / "Jul" / "Aug"
* / "Sep" / "Oct" / "Nov" / "Dec"
* @endcode
*
*/
/**@ingroup http_date
* @typedef typedef struct http_date_s http_date_t;
*
* The structure #http_date_t contains representation of @b Date header.
*
* The #http_date_t is defined as follows:
* @code
* typedef struct {
* msg_common_t d_common[1]; // Common fragment info
* msg_error_t *d_next; // Link to next (dummy)
* http_time_t d_time; // Seconds since Jan 1, 1900
* } http_date_t;
* @endcode
*/
issize_t http_date_d(su_home_t *home, http_header_t *h, char *s, isize_t slen)
{
http_date_t *date = h->sh_date;
if (msg_date_d((char const **)&s, &date->d_time) < 0 || *s)
return -1;
else
return 0;
}
issize_t http_date_e(char b[], isize_t bsiz, http_header_t const *h, int f)
{
http_date_t const *date = h->sh_date;
return msg_date_e(b, bsiz, date->d_time);
}
/**@ingroup http_date
* @brief Create an @b Date header object.
*
* The function http_date_create() creates a Date header object with the
* date @a date. If @date is 0, current time (as returned by msg_now()) is
* used.
*
* @param home memory home
* @param date date expressed as seconds since Mon, 01 Jan 1900 00:00:00
*
* @return
* The function http_date_create() returns a pointer to newly created
* @b Date header object when successful, or NULL upon an error.
*/
http_date_t *http_date_create(su_home_t *home, http_time_t date)
{
http_header_t *h = msg_header_alloc(home, http_date_class, 0);
if (h) {
if (date == 0)
date = msg_now();
h->sh_date->d_time = date;
}
return h->sh_date;
}
msg_hclass_t http_date_class[] =
HTTP_HEADER_CLASS(date, "Date", d_common, single, default);
/* ====================================================================== */
/**@HTTP_HEADER http_etag ETag header. */
#define http_etag_d msg_generic_d
#define http_etag_e msg_generic_e
msg_hclass_t http_etag_class[] =
HTTP_HEADER_CLASS_G(etag, "ETag", single);
/* ====================================================================== */
/**@HTTP_HEADER http_expect Expect header. */
#define http_expect_d msg_generic_d
#define http_expect_e msg_generic_e
msg_hclass_t http_expect_class[] =
HTTP_HEADER_CLASS_G(expect, "Expect", single);
/* ====================================================================== */
/**@HTTP_HEADER http_expires Expires header.
*
* The Expires header field gives the date and time after which the message
* content expires. Its syntax is defined in RFC 1428 section 14.21 as
* follows:
*
* @code
* Expires = "Expires:" HTTP-date
* @endcode
*
*/
/**@ingroup http_expires
* @typedef typedef struct http_expires_s http_expires_t;
*
* The structure #http_expires_t contains representation of @b Expires
* header.
*
* The #http_expires_t is defined as follows:
* @code
* typedef struct {
* msg_common_t d_common[1]; // Common fragment info
* msg_error_t *d_next; // Link to next (dummy)
* http_time_t d_time; // Seconds since Jan 1, 1900
* } http_expires_t;
* @endcode
*/
#define http_expires_d http_date_d
#define http_expires_e http_date_e
msg_hclass_t http_expires_class[] =
HTTP_HEADER_CLASS(expires, "Expires", d_common, single, default);
/* ====================================================================== */
/**@HTTP_HEADER http_from From header.
*
* @code
* From = "From" ":" mailbox
* @endcode
*/
#define http_from_d msg_generic_d
#define http_from_e msg_generic_e
msg_hclass_t http_from_class[] =
HTTP_HEADER_CLASS_G(from, "From", single);
/* ====================================================================== */
/**@HTTP_HEADER http_host Host header.
*
* @code
* Host = "Host" ":" host [ ":" port ]
* @endcode
*/
/** Parse Host header */
issize_t http_host_d(su_home_t *home, http_header_t *h, char *s, isize_t slen)
{
http_host_t *host = h->sh_host;
if (msg_hostport_d(&s, &host->h_host, &host->h_port) < 0 || *s)
return -1;
return 0;
}
/** Print Host header */
issize_t http_host_e(char b[], isize_t bsiz, http_header_t const *h, int flags)
{
char *b0 = b, *end = b + bsiz;
MSG_STRING_E(b, end, h->sh_host->h_host);
if (h->sh_host->h_port) {
MSG_CHAR_E(b, end, ':');
MSG_STRING_E(b, end, h->sh_host->h_port);
}
return b - b0;
}
/** Extra size of a http_host_t object. */
static
isize_t http_host_dup_xtra(http_header_t const *h, isize_t offset)
{
offset += MSG_STRING_SIZE(h->sh_host->h_host);
offset += MSG_STRING_SIZE(h->sh_host->h_port);
return offset;
}
/** Duplicate one Host header. */
static
char *http_host_dup_one(http_header_t *dst, http_header_t const *src,
char *b, isize_t xtra)
{
http_host_t *h = dst->sh_host;
http_host_t const *o = src->sh_host;
char *end = b + xtra;
MSG_STRING_DUP(b, h->h_host, o->h_host);
MSG_STRING_DUP(b, h->h_port, o->h_port);
assert(b <= end); (void)end;
return b;
}
/**Create a Host object. */
http_host_t *http_host_create(su_home_t *home,
char const *host,
char const *port)
{
http_host_t h[1];
http_host_init(h);
h->h_host = host, h->h_port = port;
if (host) {
return http_host_dup(home, h);
}
else
return NULL;
}
msg_hclass_t http_host_class[] =
HTTP_HEADER_CLASS(host, "Host", h_common, single, host);
/* ====================================================================== */
/**@HTTP_HEADER http_if_match If-Match header. */
#define http_if_match_d msg_list_d
#define http_if_match_e msg_list_e
msg_hclass_t http_if_match_class[] =
HTTP_HEADER_CLASS_LIST(if_match, "If-Match", list);
/* ====================================================================== */
/**@HTTP_HEADER http_if_modified_since If-Modified-Since header.
*
* The If-Modified-Since header field The If-Modified-Since request-header
* field is used with a method to make it conditional: if the requested
* variant has not been modified since the time specified in this field, an
* entity will not be returned from the server; instead, a 304 (not
* modified) response will be returned without any message-body. Its syntax
* is defined in RFC 2616 secion 14.25 as follows:
*
* @code
* If-Modified-Since = "If-Modified-Since" ":" HTTP-date
* @endcode
*
*/
/**@ingroup http_if_modified_since
* @typedef typedef struct http_if_modified_since_s http_if_modified_since_t;
*
* The structure #http_if_modified_since_t contains representation of
* @b If-Modified-Since header.
*
* The #http_if_modified_since_t is defined as follows:
* @code
* typedef struct {
* msg_common_t d_common[1]; // Common fragment info
* msg_error_t *d_next; // Link to next (dummy)
* http_time_t d_time; // Seconds since Jan 1, 1900
* } http_if_modified_since_t;
* @endcode
*/
#define http_if_modified_since_d http_date_d
#define http_if_modified_since_e http_date_e
msg_hclass_t http_if_modified_since_class[] =
HTTP_HEADER_CLASS(if_modified_since, "If-Modified-Since",
d_common, single, default);
/* ====================================================================== */
/**@HTTP_HEADER http_if_none_match If-None-Match header. */
#define http_if_none_match_d msg_list_d
#define http_if_none_match_e msg_list_e
msg_hclass_t http_if_none_match_class[] =
HTTP_HEADER_CLASS_LIST(if_none_match, "If-None-Match", list);
/* ====================================================================== */
/**@HTTP_HEADER http_if_range If-Range header.
*
* The @b If-Range header is used when a client has a partial copy of an
* entity in its cache, and wishes to have an up-to-date copy of the entire
* entity. Informally, its meaning is `if the entity is unchanged, send
* me the part(s) that I am missing; otherwise, send me the entire new
* entity'. Its syntax is defined in RFC 2616 as follows:
*
* @code
* If-Range = "If-Range" ":" ( entity-tag / HTTP-date )
* @endcode
*/
/** Parse If-Range header */
issize_t http_if_range_d(su_home_t *home, http_header_t *h, char *s, isize_t slen)
{
http_if_range_t *ifr = (http_if_range_t *)h;
if (s[0] == '"' || strncasecmp(s, "W/\"", 3) == 0) {
ifr->ifr_tag = s;
return 0;
} else {
return msg_date_d((char const **)&s, &ifr->ifr_time);
}
}
/** Print If-Range header */
issize_t http_if_range_e(char b[], isize_t bsiz, http_header_t const *h, int flags)
{
http_if_range_t const *ifr = (http_if_range_t const *)h;
char *b0 = b, *end = b + bsiz;
if (ifr->ifr_tag) {
MSG_STRING_E(b, end, ifr->ifr_tag);
return b - b0;
} else {
return msg_date_e(b, bsiz, ifr->ifr_time);
}
}
/** Extra size of a http_if_range_t object. */
static
isize_t http_if_range_dup_xtra(http_header_t const *h, isize_t offset)
{
http_if_range_t const *ifr = (http_if_range_t const *)h;
offset += MSG_STRING_SIZE(ifr->ifr_tag);
return offset;
}
/** Duplicate one If-Range header. */
static
char *http_if_range_dup_one(http_header_t *dst, http_header_t const *src,
char *b, isize_t xtra)
{
http_if_range_t *ifr = dst->sh_if_range;
http_if_range_t const *o = src->sh_if_range;
char *end = b + xtra;
MSG_STRING_DUP(b, ifr->ifr_tag, o->ifr_tag);
ifr->ifr_time = o->ifr_time;
assert(b <= end); (void)end;
return b;
}
msg_hclass_t http_if_range_class[] =
HTTP_HEADER_CLASS(if_range, "If-Range", ifr_common, single, if_range);
/* ====================================================================== */
/**@HTTP_HEADER http_if_unmodified_since If-Unmodified-Since header.
*
* The @b If-Unmodified-Since header is used with a method to make it
* conditional. If the requested resource has not been modified since the
* time specified in this field, the server SHOULD perform the requested
* operation as if the If-Unmodified-Since header were not present. Its
* syntax is defined in RFC 2616 14.28 as follows:
*
* @code
* If-Unmodified-Since = "If-Unmodified-Since:" HTTP-date
* @endcode
*
*/
/**@ingroup http_if_unmodified_since
* @typedef typedef http_date_t http_if_unmodified_since_t;
*
* The structure #http_if_unmodified_since_t contains representation of
* @b If-Unmodified-Since header.
*
* The #http_if_unmodified_since_t is defined as follows:
* @code
* typedef struct {
* msg_common_t d_common[1]; // Common fragment info
* msg_error_t *d_next; // Link to next (dummy)
* http_time_t d_time; // Seconds since Jan 1, 1900
* } http_if_unmodified_since_t;
* @endcode
*/
#define http_if_unmodified_since_d http_date_d
#define http_if_unmodified_since_e http_date_e
msg_hclass_t http_if_unmodified_since_class[] =
HTTP_HEADER_CLASS(if_unmodified_since, "If-Unmodified-Since",
d_common, single, default);
/* ====================================================================== */
/**@HTTP_HEADER http_last_modified Last-Modified header.
*
* The Last-Modified header field gives the date and time after which the
* message content last_modified. Its syntax is defined in [] as follows:
*
* @code
* Last-Modified = "Last-Modified:" HTTP-date
* @endcode
*
*/
/**@ingroup http_last_modified
* @typedef typedef struct http_last_modified_s http_last_modified_t;
*
* The structure #http_last_modified_t contains representation of @b
* Last-Modified header.
*
* The #http_last_modified_t is defined as follows:
* @code
* typedef struct {
* msg_common_t d_common[1]; // Common fragment info
* msg_error_t *d_next; // Link to next (dummy)
* http_time_t d_time; // Seconds since Jan 1, 1900
* } http_last_modified_t;
* @endcode
*/
#define http_last_modified_d http_date_d
#define http_last_modified_e http_date_e
msg_hclass_t http_last_modified_class[] =
HTTP_HEADER_CLASS(last_modified, "Last-Modified", d_common, single, default);
/* ====================================================================== */
/**@HTTP_HEADER http_location Location Header
*
* The Location header is used to redirect the recipient to a location other
* than the Request-URI for completion of the request or identification of a
* new resource. Its syntax is defined in RFC 2616 section 14.30 as follows:
*
* @code
* Location = "Location" ":" absoluteURI
* @endcode
*
*/
/**@ingroup http_location
*
* @typedef typedef struct http_location_s http_location_t;
*
* The structure http_location_t contains representation of @b Location
* header.
*
* The http_location_t is defined as follows:
* @code
* typedef struct http_location_s
* {
* msg_common_t loc_common[1];
* msg_error_t *loc_next;
* url_t loc_url[1];
* } http_location_t;
* @endcode
*/
/** Decode (parse) a Location header */
issize_t http_location_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen)
{
http_location_t *loc = (http_location_t *)h;
return url_d(loc->loc_url, s);
}
/** Encode (print) a Location header */
issize_t http_location_e(char b[], isize_t bsiz, msg_header_t const *h, int flags)
{
http_location_t const *loc = (http_location_t *)h;
return url_e(b, bsiz, loc->loc_url);
}
/** Calculate extra storage used by Location header field */
isize_t http_location_dup_xtra(msg_header_t const *h, isize_t offset)
{
http_location_t const *loc = (http_location_t *)h;
offset += url_xtra(loc->loc_url);
return offset;
}
/** Duplicate a Location header field */
char *http_location_dup_one(msg_header_t *dst, msg_header_t const *src,
char *b, isize_t xtra)
{
http_location_t *loc = (http_location_t *)dst;
http_location_t const *o = (http_location_t const *)src;
char *end = b + xtra;
URL_DUP(b, end, loc->loc_url, o->loc_url);
assert(b <= end);
return b;
}
msg_hclass_t http_location_class[] =
HTTP_HEADER_CLASS(location, "Location", loc_common, single, location);
/* ====================================================================== */
/**@HTTP_HEADER http_max_forwards Max-Forwards header. */
#define http_max_forwards_d msg_numeric_d
#define http_max_forwards_e msg_numeric_e
msg_hclass_t http_max_forwards_class[] =
HTTP_HEADER_CLASS(max_forwards, "Max-Forwards", mf_common, single, numeric);
/* ====================================================================== */
/**@HTTP_HEADER http_pragma Pragma header. */
#define http_pragma_d msg_list_d
#define http_pragma_e msg_list_e
msg_hclass_t http_pragma_class[] =
HTTP_HEADER_CLASS_LIST(pragma, "Pragma", list);
/* ====================================================================== */
/**@HTTP_HEADER http_proxy_authenticate Proxy-Authenticate header. */
#define http_proxy_authenticate_d msg_auth_d
#define http_proxy_authenticate_e msg_auth_e
msg_hclass_t http_proxy_authenticate_class[] =
HTTP_HEADER_CLASS_AUTH(proxy_authenticate, "Proxy-Authenticate", append);
/* ====================================================================== */
/**@HTTP_HEADER http_proxy_authorization Proxy-Authorization header. */
#define http_proxy_authorization_d msg_auth_d
#define http_proxy_authorization_e msg_auth_e
msg_hclass_t http_proxy_authorization_class[] =
HTTP_HEADER_CLASS_AUTH(proxy_authorization, "Proxy-Authorization", append);
/* ====================================================================== */
/**@HTTP_HEADER http_range Range header.
*
* The Range header is used to GET one or more sub-ranges of an entity
* instead of the entire entity. Its syntax is defined in RFC 2616 section
* 14.35 as follows:
*
* @code
* Range = "Range" ":" ranges-specifier
* ranges-specifier = byte-ranges-specifier
* byte-ranges-specifier = bytes-unit "=" byte-range-set
* byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
* byte-range-spec = first-byte-pos "-" [last-byte-pos]
* first-byte-pos = 1*DIGIT
* last-byte-pos = 1*DIGIT
* @endcode
*
*/
/**@ingroup http_range
*
* @typedef typedef struct http_range_s http_range_t;
*
* The structure http_range_t contains representation of @b Range header.
*
* The http_range_t is defined as follows:
* @code
* typedef struct http_range_s
* {
* msg_common_t rng_common[1];
* msg_error_t *rng_next;
* char const *rng_unit;
* char const * const *rng_specs;
* } http_range_t;
* @endcode
*/
static issize_t range_spec_scan(char *start);
/** Decode (parse) a Range header */
issize_t http_range_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen)
{
http_range_t *rng = (http_range_t *)h;
rng->rng_unit = s;
skip_token(&s);
if (s == rng->rng_unit)
return -1;
if (IS_LWS(*s)) {
*s++ = '\0';
skip_lws(&s);
}
if (*s != '=')
return -1;
*s++ = '\0';
skip_lws(&s);
/* XXX - use range-scanner */
return msg_commalist_d(home, &s, &rng->rng_specs, range_spec_scan);
}
/** Scan and compact a range spec. */
static
issize_t range_spec_scan(char *start)
{
size_t tlen;
char *s, *p;
s = p = start;
if (s[0] == ',')
return 0;
/* Three forms: 1*DIGIT "-" 1*DIGIT | 1*DIGIT "-" | "-" 1*DIGIT */
if (*s != '-') {
tlen = span_digit(s);
if (tlen == 0)
return -1;
p += tlen; s += tlen;
skip_lws(&s);
}
if (*s != '-')
return -1;
if (p != s)
*p = *s;
p++, s++; skip_lws(&s);
if (IS_DIGIT(*s)) {
tlen = span_digit(s);
if (tlen == 0)
return -1;
if (p != s)
memmove(p, s, tlen);
p += tlen; s += tlen;
skip_lws(&s);
}
if (p != s)
*p = '\0';
return s - start;
}
/** Encode (print) a Range header */
issize_t http_range_e(char b[], isize_t bsiz, msg_header_t const *h, int flags)
{
http_range_t const *rng = (http_range_t *)h;
char *b0 = b, *end = b + bsiz;
MSG_STRING_E(b, end, rng->rng_unit);
MSG_CHAR_E(b, end, '=');
MSG_COMMALIST_E(b, end, rng->rng_specs, MSG_IS_COMPACT(flags));
MSG_TERM_E(b, end);
return b - b0;
}
/** Calculate extra storage used by Range header field */
isize_t http_range_dup_xtra(msg_header_t const *h, isize_t offset)
{
http_range_t const *rng = (http_range_t *)h;
MSG_PARAMS_SIZE(offset, rng->rng_specs);
offset += MSG_STRING_SIZE(rng->rng_unit);
return offset;
}
/** Duplicate a Range header field */
char *http_range_dup_one(msg_header_t *dst, msg_header_t const *src,
char *b, isize_t xtra)
{
http_range_t *rng = (http_range_t *)dst;
http_range_t const *o = (http_range_t const *)src;
char *end = b + xtra;
b = msg_params_dup((char const * const **)&rng->rng_specs,
o->rng_specs, b, xtra);
MSG_STRING_DUP(b, rng->rng_unit, o->rng_unit);
assert(b <= end); (void)end;
return b;
}
msg_hclass_t http_range_class[] =
HTTP_HEADER_CLASS(range, "Range", rng_specs, single, range);
/* ====================================================================== */
/**@HTTP_HEADER http_referer Referer header.
*
* The Referer header is used to redirect the recipient to a referer other
* than the Request-URI for completion of the request or identification of a
* new resource. Its syntax is defined in RFC 2616 section 14.30 as follows:
*
* @code
* Referer = "Referer" ":" absoluteURI
* @endcode
*
*/
/**@ingroup http_referer
*
* @typedef typedef struct http_referer_s http_referer_t;
*
* The structure http_referer_t contains representation of @b Referer
* header.
*
* The http_referer_t is defined as follows:
* @code
* typedef struct http_referer_s
* {
* msg_common_t loc_common[1];
* msg_error_t *loc_next;
* url_t loc_url[1];
* } http_referer_t;
* @endcode
*/
#define http_referer_d http_location_d
#define http_referer_e http_location_e
msg_hclass_t http_referer_class[] =
HTTP_HEADER_CLASS(referer, "Referer", loc_common, single, location);
/* ====================================================================== */
/**@HTTP_HEADER http_mime_version MIME-Version header.
*
* We use MIME MIME-Version header.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_retry_after Retry-After header.
*
* The Retry-After response-header field can be used with a 503 (Service
* Unavailable) response to indicate how long the service is expected to be
* unavailable to the requesting client. This field MAY also be used with
* any 3xx (Redirection) response to indicate the minimum time the
* user-agent is asked wait before issuing the redirected request. Its
* syntax is defined in RFC 2616 section 14.37 as follows:
*
* @code
* Retry-After = "Retry-After" ":" ( HTTP-date / delta-seconds )
* @endcode
*
*/
/**@ingroup http_retry_after
* @typedef typedef struct http_retry_after_s http_retry_after_t;
*
* The structure #http_retry_after_t contains representation of @b
* Retry-After header.
*
* The #http_retry_after_t is defined as follows:
* @code
* typedef struct {
* msg_common_t ra_common[1]; // Common fragment info
* msg_error_t *ra_next; // Link to next (dummy)
* http_time_t ra_date; // When to retry
* http_time_t ra_delta; // Seconds to before retry
* } http_retry_after_t;
* @endcode
*/
issize_t http_retry_after_d(su_home_t *home, http_header_t *h, char *s, isize_t slen)
{
http_retry_after_t *ra = h->sh_retry_after;
if (msg_date_delta_d((char const **)&s,
&ra->ra_date,
&ra->ra_delta) < 0 || *s)
return -1;
else
return 0;
}
issize_t http_retry_after_e(char b[], isize_t bsiz, http_header_t const *h, int f)
{
http_retry_after_t const *ra = h->sh_retry_after;
if (ra->ra_date)
return msg_date_e(b, bsiz, ra->ra_date + ra->ra_delta);
else
return msg_delta_e(b, bsiz, ra->ra_delta);
}
msg_hclass_t http_retry_after_class[] =
HTTP_HEADER_CLASS(retry_after, "Retry-After", ra_common, single, default);
/* ====================================================================== */
/**@HTTP_HEADER http_server Server header. */
#define http_server_d msg_generic_d
#define http_server_e msg_generic_e
msg_hclass_t http_server_class[] =
HTTP_HEADER_CLASS_G(server, "Server", single);
/* ====================================================================== */
/**@HTTP_HEADER http_te TE header.
*
* The TE request-header field indicates what extension transfer-codings it
* is willing to accept in the response and whether or not it is willing to
* accept trailer fields in a chunked transfer-coding. Its value may consist
* of the keyword "trailers" and/or a comma-separated list of extension
* transfer-coding names with optional accept parameters. Its syntax is
* defined in [H14.39] as follows:
*
* @code
* TE = "TE" ":" #( t-codings )
* t-codings = "trailers" | ( transfer-extension [ accept-params ] )
* @endcode
*
*/
/**@ingroup http_te
* @typedef typedef strucy http_te_s http_te_t;
*
* The structure http_te_t contains representation of @b TE header.
*
* The http_te_t is defined as follows:
* @code
* typedef struct http_te_s {
* } http_te_t;
* @endcode
*/
static inline
void http_te_update(http_te_t *te)
{
te->te_q = msg_header_find_param(te->te_common, "q");
}
issize_t http_te_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen)
{
msg_header_t **hh = &h->sh_succ, *h0 = h;
http_te_t *te = (http_te_t *)h;
assert(h); assert(sizeof(*h));
for (;*s;) {
/* Ignore empty entries (comma-whitespace) */
if (*s == ',') { *s++ = '\0'; skip_lws(&s); continue; }
if (!h) { /* Allocate next header structure */
if (!(h = msg_header_alloc(home, h0->sh_class, 0)))
break;
*hh = h; h->sh_prev = hh; hh = &h->sh_succ;
te = te->te_next = (http_te_t *)h;
}
/* "TE:" #(transfer-extension ; *(parameters))) */
if (msg_token_d(&s, &te->te_extension) == -1)
return -1;
if (*s == ';' && msg_params_d(home, &s, &te->te_params) == -1)
return -1;
if (*s != '\0' && *s != ',')
return -1;
if (te->te_params)
http_te_update(te);
h = NULL;
}
return 0;
}
issize_t http_te_e(char b[], isize_t bsiz, msg_header_t const *h, int flags)
{
char *b0 = b, *end = b + bsiz;
http_te_t const *te = (http_te_t *)h;
assert(http_is_te(h));
MSG_STRING_E(b, end, te->te_extension);
MSG_PARAMS_E(b, end, te->te_params, flags);
MSG_TERM_E(b, end);
return b - b0;
}
isize_t http_te_dup_xtra(msg_header_t const *h, isize_t offset)
{
http_te_t const *te = (http_te_t const *)h;
MSG_PARAMS_SIZE(offset, te->te_params);
offset += MSG_STRING_SIZE(te->te_extension);
return offset;
}
/** Duplicate one http_te_t object */
char *http_te_dup_one(msg_header_t *dst, msg_header_t const *src,
char *b, isize_t xtra)
{
http_te_t *te = (http_te_t *)dst;
http_te_t const *o = (http_te_t const *)src;
char *end = b + xtra;
b = msg_params_dup(&te->te_params, o->te_params, b, xtra);
MSG_STRING_DUP(b, te->te_extension, o->te_extension);
if (te->te_params) http_te_update(te);
assert(b <= end); (void)end;
return b;
}
msg_hclass_t http_te_class[] =
HTTP_HEADER_CLASS(te, "TE", te_params, append, te);
/* ====================================================================== */
/**@HTTP_HEADER http_trailer Trailer header. */
#define http_trailer_d msg_list_d
#define http_trailer_e msg_list_e
msg_hclass_t http_trailer_class[] =
HTTP_HEADER_CLASS_LIST(trailer, "Trailer", list_critical);
/* ====================================================================== */
/**@HTTP_HEADER http_transfer_encoding Transfer-Encoding header. */
#define http_transfer_encoding_d msg_list_d
#define http_transfer_encoding_e msg_list_e
msg_hclass_t http_transfer_encoding_class[] =
HTTP_HEADER_CLASS_LIST(transfer_encoding, "Transfer-Encoding", list_critical);
/* ====================================================================== */
/**@HTTP_HEADER http_upgrade Upgrade header. */
#define http_upgrade_d msg_list_d
#define http_upgrade_e msg_list_e
msg_hclass_t http_upgrade_class[] =
HTTP_HEADER_CLASS_LIST(upgrade, "Upgrade", list_critical);
/* ====================================================================== */
/**@HTTP_HEADER http_user_agent User-Agent header. */
#define http_user_agent_d msg_generic_d
#define http_user_agent_e msg_generic_e
msg_hclass_t http_user_agent_class[] =
HTTP_HEADER_CLASS_G(user_agent, "User-Agent", single);
/* ====================================================================== */
/**@HTTP_HEADER http_vary Vary header. */
#define http_vary_d msg_list_d
#define http_vary_e msg_list_e
msg_hclass_t http_vary_class[] =
HTTP_HEADER_CLASS_LIST(vary, "Vary", list);
/* ====================================================================== */
/**@HTTP_HEADER http_via Via header.
*
* @code
* Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
* received-protocol = [ protocol-name "/" ] protocol-version
* protocol-name = token
* protocol-version = token
* received-by = ( host [ ":" port ] ) | pseudonym
* pseudonym = token
* @endcode
*/
issize_t http_via_d(su_home_t *home, http_header_t *h, char *s, isize_t slen)
{
http_header_t **hh = &h->sh_succ, *h0 = h;
http_via_t *v = h->sh_via;
assert(h && h->sh_class);
for (;*s;) {
/* Ignore empty entries (comma-whitespace) */
if (*s == ',') { *s++ = '\0'; skip_lws(&s); continue; }
if (!h) { /* Allocate next header structure */
if (!(h = msg_header_alloc(home, h0->sh_class, 0)))
return -1;
*hh = h; h->sh_prev = hh; hh = &h->sh_succ;
v = v->v_next = h->sh_via;
}
if (http_version_d(&s, &v->v_version) == -1) /* Parse protocol version */
return -1;
if (msg_hostport_d(&s, &v->v_host, &v->v_port) == -1) /* Host (and port) */
return -1;
if (*s == '(' && msg_comment_d(&s, &v->v_comment) == -1) /* Comment */
return -1;
if (*s != '\0' && *s != ',') /* Extra before next header field? */
return -1;
h = NULL;
}
if (h) /* List without valid header via */
return -1;
return 0;
}
issize_t http_via_e(char b[], isize_t bsiz, http_header_t const *h, int flags)
{
int const compact = MSG_IS_COMPACT(flags);
char *b0 = b, *end = b + bsiz;
http_via_t const *v = h->sh_via;
MSG_STRING_E(b, end, v->v_version);
MSG_CHAR_E(b, end, ' ');
MSG_STRING_E(b, end, v->v_host);
if (v->v_port) {
MSG_CHAR_E(b, end, ':');
MSG_STRING_E(b, end, v->v_port);
}
if (v->v_comment) {
if (!compact) MSG_CHAR_E(b, end, ' ');
MSG_CHAR_E(b, end, '(');
MSG_STRING_E(b, end, v->v_comment);
MSG_CHAR_E(b, end, ')');
}
MSG_TERM_E(b, end);
return b - b0;
}
static isize_t http_via_dup_xtra(http_header_t const *h, isize_t offset)
{
http_via_t const *v = h->sh_via;
offset += MSG_STRING_SIZE(v->v_version);
offset += MSG_STRING_SIZE(v->v_host);
offset += MSG_STRING_SIZE(v->v_port);
offset += MSG_STRING_SIZE(v->v_comment);
return offset;
}
/** Duplicate one http_via_t object */
static char *http_via_dup_one(http_header_t *dst, http_header_t const *src,
char *b, isize_t xtra)
{
http_via_t *v = dst->sh_via;
http_via_t const *o = src->sh_via;
char *end = b + xtra;
MSG_STRING_DUP(b, v->v_version, o->v_version);
MSG_STRING_DUP(b, v->v_host, o->v_host);
MSG_STRING_DUP(b, v->v_port, o->v_port);
MSG_STRING_DUP(b, v->v_comment, o->v_comment);
assert(b <= end); (void)end;
return b;
}
msg_hclass_t http_via_class[] =
HTTP_HEADER_CLASS(via, "Via", v_common, prepend, via);
/* ====================================================================== */
/**@HTTP_HEADER http_warning Warning header. */
#define http_warning_d msg_warning_d
#define http_warning_e msg_warning_e
#define http_warning_dup_xtra msg_warning_dup_xtra
#define http_warning_dup_one msg_warning_dup_one
msg_hclass_t http_warning_class[] =
HTTP_HEADER_CLASS(warning, "Warning", w_common, append, warning);
/* ====================================================================== */
/**@HTTP_HEADER http_www_authenticate WWW-Authenticate header. */
#define http_www_authenticate_d msg_auth_d
#define http_www_authenticate_e msg_auth_e
msg_hclass_t http_www_authenticate_class[] =
HTTP_HEADER_CLASS_AUTH(www_authenticate, "WWW-Authenticate", single);
/* ====================================================================== */
/**@HTTP_HEADER http_error Erroneous headers.
*
* We use erroneous header object from @b msg module.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_unknown Unknown headers.
*
* We use unknown header object from @b msg module.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_separator Header separator.
*
* We use header separator object from @b msg module.
*/
/* ====================================================================== */
/**@HTTP_HEADER http_payload Message payload.
*
* We use message body object from @b msg module.
*/