#include "gskhttpheader.h"
#include "gskhttprequest.h"
#include "gskhttpresponse.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "../common/gskdate.h"
#include "../common/gskbase64.h"
/* === output: writing a header to a buffer === */
static GEnumClass *gsk_http_connection_class = NULL;
static GEnumClass *gsk_http_content_encoding_class = NULL;
static GEnumClass *gsk_http_transfer_encoding_class = NULL;
static GEnumClass *gsk_http_verb_class = NULL;
static inline void
init_classes (void)
{
gsk_http_connection_class = g_type_class_ref (GSK_TYPE_HTTP_CONNECTION);
gsk_http_content_encoding_class = g_type_class_ref (GSK_TYPE_HTTP_CONTENT_ENCODING);
gsk_http_transfer_encoding_class = g_type_class_ref (GSK_TYPE_HTTP_TRANSFER_ENCODING);
gsk_http_verb_class = g_type_class_ref (GSK_TYPE_HTTP_VERB);
}
/* http status code descriptions */
typedef struct
{
int status_code;
const char *description;
} GskHttpStatusDescription;
static gint
status_description_comparator(const GskHttpStatusDescription *desc_a,
const GskHttpStatusDescription *desc_b)
{
int a = desc_a->status_code;
int b = desc_b->status_code;
if (a < b)
return -1;
if (a > b)
return +1;
return 0;
}
static const char*
get_http_status_description(int id)
{
static GskHttpStatusDescription descriptions[] = {
{GSK_HTTP_STATUS_CONTINUE, "Continue"},
{GSK_HTTP_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols"},
{200, "OK"},
{201, "Created"},
{202, "Accepted"},
{203, "Non-Authoritative Information"},
{204, "No Content"},
{205, "Reset Content"},
{206, "Partial Content"},
{300, "Multiple Choices"},
{301, "Moved Permanently"},
{302, "Found"},
{303, "See Other"},
{304, "Not Modified"},
{305, "Use Proxy"},
{307, "Temporary Redirect"},
{400, "Bad Request"},
{401, "Unauthorized"},
{402, "Payment Required"},
{403, "Forbidden"},
{404, "Not Found"},
{405, "Method Not Allowed"},
{406, "Not Acceptable"},
{407, "Proxy Authentication Required"},
{408, "Request Time-out"},
{409, "Conflict"},
{410, "Gone"},
{411, "Length Required"},
{412, "Precondition Failed"},
{413, "Request Entity Too Large"},
{414, "Request-URI Too Large"},
{415, "Unsupported Media Type"},
{416, "Requested range not satisfiable"},
{417, "Expectation Failed"},
{500, "Internal Server Error"},
{501, "Not Implemented"},
{502, "Bad Gateway"},
{503, "Service Unavailable"},
{504, "Gateway Time-out"},
{505, "HTTP Version not supported"}
};
GskHttpStatusDescription *description;
description = bsearch (&id,
descriptions,
G_N_ELEMENTS (descriptions),
sizeof (descriptions[0]),
(GCompareFunc) status_description_comparator);
if (description)
return description->description;
return "Unknown HTTP Status Code being proxied";
}
/* TODO: make this more efficient */
static void
print_request_first_line(GskHttpVerb verb,
const char *path,
int http_minor_version,
GskHttpHeaderPrintFunc print_func,
gpointer data)
{
guint len = strlen (path) + 100;
char *tmp = g_alloca (len);
GEnumValue *enum_value = g_enum_get_value (gsk_http_verb_class, verb);
const char *verb_name = enum_value ? enum_value->value_nick : "unknown";
char *at;
g_snprintf (tmp, len, "%s %s HTTP/1.%d", verb_name, path, http_minor_version);
for (at = tmp; *at != 0 && isalpha (*at); at++)
*at = toupper (*at);
(*print_func) (tmp, data);
}
static void
print_response_first_line(int code,
int http_minor_version,
GskHttpHeaderPrintFunc print_func,
gpointer data)
{
/* Assert: all status strings must be short enough!!!! */
char buf[256];
g_snprintf (buf, sizeof (buf),
"HTTP/1.%d %d %s",
http_minor_version,
code,
get_http_status_description (code));
(*print_func) (buf, data);
}
/* NOTE: must sync with cookie_to_string */
static guint
cookie_max_length (GskHttpCookie *cookie)
{
guint rv = 0;
if (!cookie->key || !cookie->value)
return 0;
rv += strlen (cookie->key) + strlen (cookie->value) + 3;
if (cookie->domain)
rv += strlen (cookie->domain) + 9;
if (cookie->expire_date)
rv += strlen (cookie->expire_date) + 10;
if (cookie->max_age >= 0)
rv += 30;
if (cookie->secure)
rv += 10;
if (cookie->version)
rv += 12;
if (cookie->path)
rv += strlen (cookie->path) + 7;
return rv;
}
static guint
cookie_to_string (GskHttpCookie *cookie,
char *buf_at,
guint remaining)
{
char *start = buf_at;
if (!cookie->key || !cookie->value)
return 0;
g_snprintf (buf_at, remaining, "%s=%s;", cookie->key, cookie->value);
buf_at = strchr (buf_at, 0);
if (cookie->domain)
{
g_snprintf (buf_at, remaining - (buf_at - start),
" Domain=%s;", cookie->domain);
buf_at = strchr (buf_at, 0);
}
if (cookie->max_age >= 0)
{
g_snprintf (buf_at, remaining - (buf_at - start),
" Max-Age=%ld;", (long) cookie->max_age);
buf_at = strchr (buf_at, 0);
}
if (cookie->expire_date)
{
g_snprintf (buf_at, remaining - (buf_at - start),
" Expires=%s;", cookie->expire_date);
buf_at = strchr (buf_at, 0);
}
if (cookie->path)
{
g_snprintf (buf_at, remaining - (buf_at - start),
" Path=%s;", cookie->path);
buf_at = strchr (buf_at, 0);
}
if (cookie->version)
{
g_snprintf (buf_at, remaining - (buf_at - start),
" Version=%u;",
cookie->version);
buf_at = strchr (buf_at, 0);
}
if (cookie->secure)
{
g_snprintf (buf_at, remaining - (buf_at - start), " Secure;");
buf_at = strchr (buf_at, 0);
}
return buf_at - start;
}
static void
print_cookielist (const char *header,
GSList *cookie_list,
GskHttpHeaderPrintFunc print_func,
gpointer data)
{
if (cookie_list)
{
char *buf;
guint index = 0;
guint len = 0;
GSList *tmp;
for (tmp = cookie_list; tmp != NULL; tmp = tmp->next)
len += cookie_max_length (tmp->data) + strlen (header) + 4;
buf = g_alloca (len + 1);
index = 0;
for (tmp = cookie_list; tmp != NULL; tmp = tmp->next)
{
strcpy (buf + index, header);
index += strlen (header);
strcpy (buf + index, ": ");
index += 2;
index += cookie_to_string (tmp->data, buf + index, len - index);
if (tmp->next != NULL)
{
strcpy (buf + index, "\r\n");
index += 2;
}
}
print_func (buf, data);
}
}
static void
gsk_http_char_set_append_list (GskHttpCharSet *set,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
GskHttpCharSet *at;
guint approx_len = 20;
guint cur_len;
char *buf;
for (at = set; at != NULL; at = at->next)
approx_len += strlen (at->charset_name) + 50 + 5;
buf = g_alloca (approx_len + 1);
strcpy (buf, "Accept-CharSet: ");
cur_len = 16;
for (at = set; at != NULL; at = at->next)
{
strcpy (buf + cur_len, at->charset_name);
cur_len += strlen (at->charset_name);
if (set->quality >= 0.0)
{
g_snprintf (buf, approx_len - cur_len, ";q=%.1g", set->quality);
cur_len += strlen (buf + cur_len);
}
if (at->next != NULL)
{
strcpy (buf + cur_len, ", ");
cur_len += 2;
}
}
buf[cur_len] = 0;
print_func (buf, print_data);
}
static void
gsk_http_content_encoding_set_append_list (GskHttpContentEncodingSet *set,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
GskHttpContentEncodingSet *at;
guint approx_len = 30;
guint cur_len;
char *buf;
for (at = set; at != NULL; at = at->next)
approx_len += 50 + 30;
buf = g_alloca (approx_len + 1);
strcpy (buf, "Accept-Encoding: ");
cur_len = 17;
for (at = set; at != NULL; at = at->next)
{
switch (at->encoding)
{
case GSK_HTTP_CONTENT_ENCODING_UNRECOGNIZED:
/* XXX: error handling */
continue;
case GSK_HTTP_CONTENT_ENCODING_IDENTITY:
strcpy (buf + cur_len, "identity");
cur_len += 8;
break;
case GSK_HTTP_CONTENT_ENCODING_GZIP:
strcpy (buf + cur_len, "gzip");
cur_len += 4;
break;
case GSK_HTTP_CONTENT_ENCODING_COMPRESS:
strcpy (buf + cur_len, "compress");
cur_len += 7;
break;
default:
/* XXX: error handling */
g_warning ("gsk_http_content_encoding_set_append_list: "
"unknown encoding %d", at->encoding);
break;
}
if (at->quality >= 0.0)
{
g_snprintf (buf + cur_len, approx_len - cur_len,
";q=%.1g", at->quality);
cur_len += strlen (buf + cur_len);
}
if (at->next != NULL)
{
strcpy (buf + cur_len, ", ");
cur_len += 2;
}
}
buf[cur_len] = 0;
print_func (buf, print_data);
}
static void
gsk_http_transfer_encoding_set_append_list (GskHttpTransferEncodingSet *set,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
GskHttpTransferEncodingSet *at;
guint approx_len = 30;
guint cur_len;
char *buf;
for (at = set; at != NULL; at = at->next)
approx_len += 50 + 30;
buf = g_alloca (approx_len + 1);
strcpy (buf, "TE: ");
cur_len = 17;
for (at = set; at != NULL; at = at->next)
{
switch (at->encoding)
{
case GSK_HTTP_TRANSFER_ENCODING_UNRECOGNIZED:
/* XXX: error handling */
continue;
case GSK_HTTP_TRANSFER_ENCODING_NONE:
strcpy (buf + cur_len, "none");
cur_len += 4;
break;
case GSK_HTTP_TRANSFER_ENCODING_CHUNKED:
strcpy (buf + cur_len, "chunked");
cur_len += 7;
break;
default:
/* XXX: error handling */
g_warning ("gsk_http_transfer_encoding_set_append_list: "
"unknown encoding %d", at->encoding);
break;
}
if (at->quality >= 0.0)
{
g_snprintf (buf + cur_len, approx_len - cur_len,
";q=%.1g", at->quality);
cur_len += strlen (buf + cur_len);
}
if (at->next != NULL)
{
strcpy (buf + cur_len, ", ");
cur_len += 2;
}
}
buf[cur_len] = 0;
print_func (buf, print_data);
}
static void
gsk_http_range_set_append_list (GskHttpRangeSet *list,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
/* XXX: implement this!!! */
//gsk_buffer_append (buffer, "Accept-Ranges: ", 15);
}
/* uh, standardize this */
#define QUALITY_MAX_LEN 64
static void
gsk_http_language_set_append_list (GskHttpLanguageSet *list,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
GskHttpLanguageSet *at;
guint max_len = strlen ("Accept-Language: ");
char *line, *line_at;
/* estimate length */
for (at = list; at != NULL; at = at->next)
{
if (at->quality != -1.0)
max_len += QUALITY_MAX_LEN + 4; /* for our double. */
max_len += strlen(at->language);
max_len += 20;
}
/* produce line */
line = g_alloca (max_len);
line_at = line;
strcpy (line_at, "Accept-Language: ");
line_at = strchr (line_at, 0);
for (at = list; at != NULL; at = at->next)
{
strcpy (line_at, at->language);
line_at = strchr (line_at, 0);
if (at->quality != -1)
{
char qual_buf[QUALITY_MAX_LEN];
g_snprintf(qual_buf, QUALITY_MAX_LEN, ";q=%.6f", at->quality);
strcpy (line_at, qual_buf);
line_at = strchr (line_at, 0);;
}
if (at->next)
*line_at++ = ',';
}
(*print_func) (line, print_data);
}
static void
gsk_http_media_type_set_append_list (GskHttpMediaTypeSet *list,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
/* XXX: implement this!!! */
// gsk_buffer_append (buffer, "Accept: ", 8);
}
static void
gsk_http_append_if_matches (char **matches,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
guint i;
guint approx_len = 20;
guint cur_len;
char *buf;
for (i = 0; matches[i] != NULL; i++)
approx_len += strlen (matches[i]) + 5;
buf = g_alloca (approx_len);
strcpy (buf, "If-Match: ");
cur_len = 10;
for (i = 0; matches[i] != NULL; i++)
{
strcpy (buf + cur_len, matches[i]);
cur_len += strlen (matches[i]);
if (matches[i + 1] != NULL)
{
strcpy (buf + cur_len, ", ");
cur_len += 2;
}
}
print_func (buf, print_data);
}
static void
print_allowed_verb (guint allowed,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
char tmp[256];
guint len = 7;
strcpy (tmp, "Allow: ");
#define MAYBE_ADD_VERB(verb) \
G_STMT_START{ \
if ((allowed & (1 << GSK_HTTP_VERB_ ## verb)) != 0) \
{ \
if (len > 7) \
{ \
strcpy (tmp + len, ", "); \
len += 2; \
} \
strcpy (tmp, #verb); \
len += strlen (#verb); \
} \
}G_STMT_END
MAYBE_ADD_VERB (GET);
MAYBE_ADD_VERB (POST);
MAYBE_ADD_VERB (PUT);
MAYBE_ADD_VERB (HEAD);
MAYBE_ADD_VERB (OPTIONS);
MAYBE_ADD_VERB (DELETE);
MAYBE_ADD_VERB (TRACE);
#undef MAYBE_ADD_VERB
(*print_func) (tmp, print_data);
}
static void
print_content_type (const char *type,
const char *subtype,
const char *charset,
GSList *additional,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
guint approx_len = 20
+ (type ? strlen (type) : 5) + 3
+ (subtype ? strlen (subtype) : 5) + 3
+ (charset ? strlen (charset) + 20 : 5) + 3;
GSList *at;
guint cur_len;
char *buf;
for (at = additional; at != NULL; at = at->next)
approx_len += strlen (additional->data) + 5;
buf = g_alloca (approx_len + 1);
strcpy (buf, "Content-Type: ");
cur_len = 14;
if (type == NULL)
buf[cur_len++] = '*';
else
{
strcpy (buf + cur_len, type);
cur_len += strlen (buf + cur_len);
}
buf[cur_len++] = '/';
if (subtype == NULL)
{
buf[cur_len++] = '*';
buf[cur_len] = 0;
}
else
{
strcpy (buf + cur_len, subtype);
cur_len += strlen (buf + cur_len);
}
if (charset != NULL)
{
strcpy (buf + cur_len, "; charset=");
cur_len += 10;
strcpy (buf + cur_len, charset);
cur_len += strlen (buf + cur_len);
}
for (at = additional; at != NULL; at = at->next)
{
buf[cur_len++] = ';';
buf[cur_len++] = ' ';
strcpy (buf + cur_len, additional->data);
cur_len += strlen (additional->data);
}
g_assert (buf[cur_len] == 0);
print_func (buf, print_data);
}
static void
print_date_line (const char *tag,
time_t date,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
char tmp[256];
guint len = strlen (tag);
memcpy (tmp, tag, len);
strcpy (tmp + len, ": ");
len += 2;
g_assert (len < sizeof (tmp));
gsk_date_print_timet (date, tmp + len, sizeof (tmp) - len, GSK_DATE_FORMAT_1123);
(*print_func) (tmp, print_data);
}
static void
print_retry_after (gboolean is_relative,
long t,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
if (is_relative)
{
char tmp[128];
g_snprintf (tmp, sizeof (tmp), "Retry-After: %ld", t);
(*print_func) (tmp, print_data);
}
else
print_date_line ("Retry-After", t, print_func, print_data);
}
static void
print_response_cache_control(GskHttpResponseCacheDirective *cache_dir,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
char numbuf[64];
char buf[2048];
char *end;
strcpy (buf, "Cache-Control:");
end = strchr (buf, 0);
#define APPEND(str) G_STMT_START{strcpy(end,str);end=strchr(end,0);}G_STMT_END
#define APPEND_UINT(ui) G_STMT_START{g_snprintf(numbuf,sizeof(numbuf),"%u",ui);APPEND(numbuf);}G_STMT_END
if (cache_dir->no_cache)
{
APPEND (" no-cache");
if (cache_dir->no_cache_name)
{
APPEND ("=");
APPEND (cache_dir->no_cache_name);
}
APPEND (",");
}
if (cache_dir->no_store)
{
APPEND (" no-store,");
}
if (cache_dir->no_transform)
{
APPEND (" no-transform,");
}
if (cache_dir->is_public)
{
APPEND (" public,");
}
if (cache_dir->is_private)
{
APPEND (" private");
if (cache_dir->private_name)
{
APPEND ("=");
APPEND (cache_dir->private_name);
}
APPEND (",");
}
if (cache_dir->must_revalidate)
{
APPEND (" must-revalidate,");
}
if (cache_dir->proxy_revalidate)
{
APPEND (" proxy-revalidate,");
}
if (cache_dir->max_age > 0)
{
APPEND (" max-age=");
APPEND_UINT (cache_dir->max_age);
APPEND (",");
}
if (cache_dir->s_max_age > 0)
{
APPEND (" s-maxage=");
APPEND_UINT (cache_dir->s_max_age);
APPEND (",");
}
print_func (buf, print_data);
#undef APPEND_UINT
#undef APPEND
}
static void
print_request_cache_control (GskHttpRequestCacheDirective *cache_dir,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
char numbuf[64];
char buf[2048];
char *end;
strcpy (buf, "Cache-Control:");
end = strchr (buf, 0);
#define APPEND(str) G_STMT_START{strcpy(end,str);end=strchr(end,0);}G_STMT_END
#define APPEND_UINT(ui) G_STMT_START{g_snprintf(numbuf,sizeof(numbuf),"%u",ui);APPEND(numbuf);}G_STMT_END
if (cache_dir->no_cache)
{
APPEND (" no-cache,");
}
if (cache_dir->no_store)
{
APPEND (" no-store,");
}
if (cache_dir->max_age > 0)
{
APPEND (" max-age=");
APPEND_UINT (cache_dir->max_age);
APPEND (",");
}
if (cache_dir->max_stale != 0)
{
if (cache_dir->max_stale > 0) /* set with arg */
{
APPEND (" max-stale=");
APPEND_UINT (cache_dir->max_stale);
}
else /* set with no argument */
{
APPEND (" max-stale");
}
APPEND (",");
}
if (cache_dir->min_fresh > 0)
{
APPEND (" min-fresh=");
APPEND_UINT (cache_dir->min_fresh);
APPEND (",");
}
if (cache_dir->no_transform)
{
APPEND (" no-transform,");
}
if (cache_dir->only_if_cached)
{
APPEND (" only-if-cached,");
}
print_func (buf, print_data);
#undef APPEND_UINT
#undef APPEND
}
static void
print_header_line (const char *tag,
const char *value,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
guint len_tag = strlen (tag);
guint len = 3 + len_tag + strlen (value);
char *line = g_alloca (len + 1);
strcpy (line, tag);
line[len_tag] = ':';
line[len_tag + 1] = ' ';
strcpy (line + len_tag + 2, value);
(*print_func) (line, print_data);
}
typedef struct _PrintInfo PrintInfo;
struct _PrintInfo
{
GskHttpHeaderPrintFunc print_func;
gpointer print_data;
};
static void
append_key_value_to_print_info (gpointer key,
gpointer value,
gpointer data)
{
PrintInfo *info = data;
guint key_len = strlen (key);
guint value_len = strlen (value);
guint total_len = key_len + 2 + value_len + 1;
char *buf = g_alloca (total_len + 1);
g_snprintf (buf, total_len, "%s: %s", (char *) key, (char *) value);
info->print_func (buf, info->print_data);
}
/**
* gsk_http_header_print:
* @http_header: the header to output, line-by-line.
* @print_func: a function to call on each line of output.
* @print_data: data to pass to @print_func.
*
* Print the HTTP header line-by-line, through a generic
* printing function. This could conceivable save memory
* and allow better streaming.
*/
void
gsk_http_header_print(GskHttpHeader *http_header,
GskHttpHeaderPrintFunc print_func,
gpointer print_data)
{
const char *type;
GEnumValue *enum_value;
GskHttpRequest *request = NULL;
GskHttpResponse *response = NULL;
GSList *list;
if (gsk_http_verb_class == NULL)
init_classes ();
if (GSK_IS_HTTP_REQUEST (http_header))
request = GSK_HTTP_REQUEST (http_header);
if (GSK_IS_HTTP_RESPONSE (http_header))
response = GSK_HTTP_RESPONSE (http_header);
/*
* print the first line.
*/
if (request)
{
print_request_first_line (request->verb,
request->path,
http_header->http_minor_version,
print_func, print_data);
}
else
{
print_response_first_line (response->status_code,
http_header->http_minor_version,
print_func, print_data);
}
#define MAYBE_PRINT_TAG(string, tag) \
G_STMT_START{ \
if ((string) != NULL) \
print_header_line (tag, (string), print_func, print_data); \
}G_STMT_END
#define MAYBE_PRINT_STRING(object, member, tag) \
G_STMT_START{ \
if ((object) != NULL) \
MAYBE_PRINT_TAG ((object)->member, tag); \
}G_STMT_END
#define MAYBE_PRINT_DATE(object, member, tag) \
G_STMT_START{ \
if ((object)->member != (time_t)-1) \
print_date_line (tag, (object)->member, print_func, print_data); \
}G_STMT_END
/* Host: */
MAYBE_PRINT_STRING (request, host, "Host");
/*
* Content-Length:
*/
if (http_header->content_length >= 0)
{
char content_length[128];
g_snprintf (content_length, sizeof (content_length),
"Content-Length: %"G_GUINT64_FORMAT,
(guint64) http_header->content_length);
print_func (content_length, print_data);
}
/*
* Connection:
*/
if (http_header->http_minor_version < 1)
{
if (http_header->connection_type == GSK_HTTP_CONNECTION_KEEPALIVE)
{
/* XXX: TODO: we should support this. see RFC 2068 for HTTP/1.0 things... */
g_warning ("!! WE DON'T SUPPORT KEEPALIVE FOR HTTP/1.0");
}
else if (http_header->connection_type == GSK_HTTP_CONNECTION_CLOSE)
print_func ("Connection: close", print_data);
}
else
{
if (http_header->connection_type == GSK_HTTP_CONNECTION_KEEPALIVE
|| http_header->connection_type == GSK_HTTP_CONNECTION_NONE)
/* Default: nothing to do. */
;
else if (http_header->connection_type == GSK_HTTP_CONNECTION_CLOSE)
print_func ("Connection: close", print_data);
else
g_warning ("unknown connection type");
}
/*
* Content-Encoding:
*/
if (http_header->content_encoding_type == GSK_HTTP_CONTENT_ENCODING_IDENTITY)
type = NULL;
else if (http_header->content_encoding_type == GSK_HTTP_CONTENT_ENCODING_UNRECOGNIZED)
type = http_header->content_encoding;
else
{
enum_value = g_enum_get_value (gsk_http_content_encoding_class, http_header->content_encoding_type);
type = enum_value ? enum_value->value_nick : NULL;
}
MAYBE_PRINT_TAG (type, "Content-Encoding");
/*
* Content-Language:
*/
if (http_header->content_languages != NULL)
{
char *value;
/* TODO: avoid allocation? */
value = g_strjoinv (", ", http_header->content_languages);
MAYBE_PRINT_TAG (value, "Content-Language");
g_free (value);
}
/*
* Transfer-Encoding:
*/
if (http_header->transfer_encoding_type == GSK_HTTP_TRANSFER_ENCODING_NONE)
type = NULL;
else if (http_header->transfer_encoding_type == GSK_HTTP_TRANSFER_ENCODING_UNRECOGNIZED)
type = http_header->unrecognized_transfer_encoding;
else
{
enum_value = g_enum_get_value (gsk_http_transfer_encoding_class, http_header->transfer_encoding_type);
type = enum_value ? enum_value->value_nick : NULL;
}
MAYBE_PRINT_TAG (type, "Transfer-Encoding");
/* Add `Date' */
MAYBE_PRINT_DATE (http_header, date, "Date");
/* Add `Pragma's */
for (list = http_header->pragmas; list; list = list->next)
print_header_line ("Pragma", (char*)(list->data), print_func, print_data);
/*
* Add all the cookies.
*/
if (request)
print_cookielist ("Cookie", request->cookies, print_func, print_data);
else
print_cookielist ("Set-Cookie", response->set_cookies, print_func, print_data);
/* Add `Content-Type' */
if (http_header->has_content_type)
{
print_content_type (http_header->content_type, http_header->content_subtype,
http_header->content_charset, http_header->content_additional,
print_func, print_data);
}
/* Add `Accept-Ranges' */
if (http_header->accepted_range_units != NULL)
gsk_http_range_set_append_list (http_header->accepted_range_units,
print_func, print_data);
/*
* Add the various fields from `info'.
*/
if (request)
{
/* Add `Accept-CharSet' */
if (request->accept_charsets)
gsk_http_char_set_append_list (request->accept_charsets,
print_func, print_data);
/* Add `Accept-Encoding' */
if (request->accept_content_encodings != NULL)
gsk_http_content_encoding_set_append_list (request->accept_content_encodings,
print_func, print_data);
/* Add `Accept-Encoding' */
if (request->accept_transfer_encodings != NULL)
gsk_http_transfer_encoding_set_append_list (request->accept_transfer_encodings,
print_func, print_data);
/* Add `Accept-Languages' */
if (request->accept_languages != NULL)
gsk_http_language_set_append_list (request->accept_languages,
print_func, print_data);
/* Add `Accept' */
if (request->accept_media_types != NULL)
gsk_http_media_type_set_append_list (request->accept_media_types,
print_func, print_data);
/* Add `If-Match' */
if (request->had_if_match)
gsk_http_append_if_matches (request->if_match, print_func, print_data);
/* Add `If-Modified-Since' */
MAYBE_PRINT_DATE (request, if_modified_since, "If-Modified-Since");
/* Add `User-Agent' */
MAYBE_PRINT_STRING (request, user_agent, "User-Agent");
/* Add `Referer' */
MAYBE_PRINT_STRING (request, referrer, "Referer");
/* Add `From' */
MAYBE_PRINT_STRING (request, from, "From");
///* Add `Proxy-Authorization' */
//MAYBE_PRINT_STRING (request, proxy_auth.credentials, "Proxy-Authorization");
/* Add `Keep-Alive' */
if (request->keep_alive_seconds >= 0)
{
char tmp[128];
g_snprintf (tmp, sizeof (tmp), "Keep-Alive: %d", request->keep_alive_seconds);
(*print_func) (tmp, print_data);
}
/* Add `Max-Forwards' */
if (request->max_forwards >= 0)
{
char tmp[128];
g_snprintf (tmp, sizeof (tmp), "Max-Forwards: %d", request->max_forwards);
(*print_func) (tmp, print_data);
}
/* Add cache-controle directives */
if (request->cache_control)
{
print_request_cache_control (request->cache_control,
print_func, print_data);
}
}
else
{
/*
* Add the response-specific headers.
*/
/* Add `Age' */
if (response->age >= 0)
{
char tmp[64];
g_snprintf (tmp, sizeof (tmp), "Age: %d", response->age);
(*print_func) (tmp, print_data);
}
/* Add `Allow' */
if (response->allowed_verbs)
print_allowed_verb (response->allowed_verbs, print_func, print_data);
/* Add base64-encoded MD5 checksum (Content-MD5) */
if (response->has_md5sum)
{
char encoded[50 + GSK_BASE64_GET_ENCODED_LEN (16)];
strcpy (encoded, "Content-MD5: ");
gsk_base64_encode (encoded + 13, (char*)response->md5sum, 16);
encoded[13 + GSK_BASE64_GET_ENCODED_LEN (16)] = 0;
(*print_func) (encoded, print_data);
}
/* Add `Expires' */
if (response->expires != (time_t)(-1))
{
MAYBE_PRINT_DATE (response, expires, "Expires");
}
else
{
MAYBE_PRINT_STRING (response, expires_str, "Expires");
}
/* Add `ETag' */
MAYBE_PRINT_STRING (response, etag, "ETag");
/* Location: */
MAYBE_PRINT_STRING (response, location, "Location");
///* Add `Proxy-Authenticate' */
//MAYBE_PRINT_STRING (response, proxy_auth.challenge, "Proxy-Authenticate");
/* Add `Retry-After' */
if (response->has_retry_after)
print_retry_after (response->retry_after_relative,
response->retry_after,
print_func, print_data);
/* Add `Last-Modified' */
MAYBE_PRINT_DATE (response, last_modified, "Last-Modified");
/* Add `Server' */
MAYBE_PRINT_STRING (response, server, "Server");
/* Add cache-controle directives */
if (response->cache_control)
print_response_cache_control (response->cache_control,
print_func, print_data);
}
/*
* And the miscellaneous headers.
*/
if (http_header->header_lines)
{
PrintInfo info;
info.print_func = print_func;
info.print_data = print_data;
g_hash_table_foreach (http_header->header_lines,
append_key_value_to_print_info, &info);
}
}
/* convenience api */
/**
* gsk_http_header_to_buffer:
* @header: the HTTP header to write into the buffer.
* @output: the buffer to store the header in, as text.
*
* Appends an HTTP header into a buffer.
*/
static inline void
add_newline_to_buffer (GskBuffer *buffer)
{
gsk_buffer_append (buffer, "\r\n", 2);
}
static void
write_header_line_to_buffer_print_func (const char *text,
gpointer buffer_ptr)
{
GskBuffer *buffer = buffer_ptr;
gsk_buffer_append_string (buffer, text);
add_newline_to_buffer (buffer);
}
void
gsk_http_header_to_buffer (GskHttpHeader *header,
GskBuffer *output)
{
gsk_http_header_print (header, write_header_line_to_buffer_print_func, output);
add_newline_to_buffer (output);
}
syntax highlighted by Code2HTML, v. 0.9.1