HelenOS sources
This source file includes following definitions.
- http_header_init
- http_header_create
- http_header_destroy
- http_header_encode
- http_header_receive_name
- http_header_receive_value
- http_header_receive
- http_header_normalize_value
- http_header_name_match
- http_headers_init
- http_headers_find_single
- http_headers_append
- http_headers_set
- http_headers_get
- http_headers_receive
- http_headers_clear
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <str.h>
#include <macros.h>
#include <http/http.h>
#include <http/ctype.h>
#define HTTP_HEADER_LINE "%s: %s\r\n"
void http_header_init(http_header_t *header)
{
link_initialize(&header->link);
header->name = NULL;
header->value = NULL;
}
http_header_t *http_header_create(const char *name, const char *value)
{
http_header_t *header = malloc(sizeof(http_header_t));
if (header == NULL)
return NULL;
http_header_init(header);
header->name = str_dup(name);
if (header->name == NULL) {
free(header);
return NULL;
}
header->value = str_dup(value);
if (header->value == NULL) {
free(header->name);
free(header);
return NULL;
}
return header;
}
void http_header_destroy(http_header_t *header)
{
free(header->name);
free(header->value);
free(header);
}
ssize_t http_header_encode(http_header_t *header, char *buf, size_t buf_size)
{
return snprintf(buf, buf_size,
HTTP_HEADER_LINE, header->name, header->value);
}
errno_t http_header_receive_name(receive_buffer_t *rb,
receive_buffer_mark_t *name_end)
{
char c = 0;
do {
if (name_end)
recv_mark_update(rb, name_end);
errno_t rc = recv_char(rb, &c, true);
if (rc != EOK)
return rc;
} while (is_token(c));
if (c != ':')
return EINVAL;
return EOK;
}
errno_t http_header_receive_value(receive_buffer_t *rb,
receive_buffer_mark_t *value_start, receive_buffer_mark_t *value_end)
{
errno_t rc = EOK;
char c = 0;
while (true) {
if (value_start)
recv_mark_update(rb, value_start);
rc = recv_char(rb, &c, false);
if (rc != EOK)
return rc;
if (c != ' ' && c != '\t')
break;
rc = recv_char(rb, &c, true);
if (rc != EOK)
return rc;
}
while (true) {
recv_mark_update(rb, value_end);
rc = recv_char(rb, &c, true);
if (rc != EOK)
return rc;
if (c != '\r' && c != '\n')
continue;
size_t nrecv;
rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'), &nrecv);
if (rc != EOK)
return rc;
rc = recv_char(rb, &c, false);
if (rc != EOK)
return rc;
if (c != ' ' && c != '\t')
break;
rc = recv_char(rb, &c, true);
if (rc != EOK)
return rc;
}
return EOK;
}
errno_t http_header_receive(receive_buffer_t *rb, http_header_t *header,
size_t size_limit, size_t *out_bytes_used)
{
receive_buffer_mark_t mark_start;
receive_buffer_mark_t mark_end;
recv_mark(rb, &mark_start);
recv_mark(rb, &mark_end);
errno_t rc = http_header_receive_name(rb, &mark_end);
if (rc != EOK)
goto end;
size_t name_size = mark_end.offset - mark_start.offset;
if (size_limit > 0 && name_size > size_limit) {
rc = ELIMIT;
goto end;
}
char *name = NULL;
rc = recv_cut_str(rb, &mark_start, &mark_end, &name);
if (rc != EOK)
goto end;
rc = http_header_receive_value(rb, &mark_start, &mark_end);
if (rc != EOK)
goto end_with_name;
size_t value_size = mark_end.offset - mark_start.offset;
if (size_limit > 0 && (name_size + value_size) > size_limit) {
rc = ELIMIT;
goto end_with_name;
}
char *value = NULL;
rc = recv_cut_str(rb, &mark_start, &mark_end, &value);
if (rc != EOK)
goto end_with_name;
if (out_bytes_used)
*out_bytes_used = name_size + value_size;
header->name = name;
header->value = value;
goto end;
end_with_name:
free(name);
end:
recv_unmark(rb, &mark_start);
recv_unmark(rb, &mark_end);
return rc;
}
void http_header_normalize_value(char *value)
{
size_t read_index = 0;
size_t write_index = 0;
while (is_lws(value[read_index]))
read_index++;
while (value[read_index] != 0) {
if (is_lws(value[read_index])) {
while (is_lws(value[read_index]))
read_index++;
if (value[read_index] != 0)
value[write_index++] = ' ';
continue;
}
value[write_index++] = value[read_index++];
}
value[write_index] = 0;
}
bool http_header_name_match(const char *name_a, const char *name_b)
{
return str_casecmp(name_a, name_b) == 0;
}
void http_headers_init(http_headers_t *headers)
{
list_initialize(&headers->list);
}
errno_t http_headers_find_single(http_headers_t *headers, const char *name,
http_header_t **out_header)
{
http_header_t *found = NULL;
http_headers_foreach(*headers, header) {
if (!http_header_name_match(header->name, name))
continue;
if (found == NULL) {
found = header;
} else {
return HTTP_EMULTIPLE_HEADERS;
}
}
if (found == NULL)
return HTTP_EMISSING_HEADER;
*out_header = found;
return EOK;
}
errno_t http_headers_append(http_headers_t *headers, const char *name,
const char *value)
{
http_header_t *header = http_header_create(name, value);
if (header == NULL)
return ENOMEM;
http_headers_append_header(headers, header);
return EOK;
}
errno_t http_headers_set(http_headers_t *headers, const char *name,
const char *value)
{
http_header_t *header = NULL;
errno_t rc = http_headers_find_single(headers, name, &header);
if (rc != EOK && rc != HTTP_EMISSING_HEADER)
return rc;
if (rc == HTTP_EMISSING_HEADER)
return http_headers_append(headers, name, value);
char *new_value = str_dup(value);
if (new_value == NULL)
return ENOMEM;
free(header->value);
header->value = new_value;
return EOK;
}
errno_t http_headers_get(http_headers_t *headers, const char *name, char **value)
{
http_header_t *header = NULL;
errno_t rc = http_headers_find_single(headers, name, &header);
if (rc != EOK)
return rc;
*value = header->value;
return EOK;
}
errno_t http_headers_receive(receive_buffer_t *rb, http_headers_t *headers,
size_t limit_alloc, unsigned limit_count)
{
errno_t rc = EOK;
unsigned added = 0;
while (true) {
char c = 0;
rc = recv_char(rb, &c, false);
if (rc != EOK)
goto error;
if (c == '\n' || c == '\r')
break;
if (limit_count > 0 && added >= limit_count) {
rc = ELIMIT;
goto error;
}
http_header_t *header = malloc(sizeof(http_header_t));
if (header == NULL) {
rc = ENOMEM;
goto error;
}
http_header_init(header);
size_t header_size;
rc = http_header_receive(rb, header, limit_alloc, &header_size);
if (rc != EOK) {
free(header);
goto error;
}
limit_alloc -= header_size;
http_headers_append_header(headers, header);
added++;
}
return EOK;
error:
while (added-- > 0) {
link_t *link = list_last(&headers->list);
http_header_t *header = list_get_instance(link, http_header_t, link);
http_headers_remove(headers, header);
http_header_destroy(header);
}
return rc;
}
void http_headers_clear(http_headers_t *headers)
{
link_t *link = list_first(&headers->list);
while (link != NULL) {
link_t *next = list_next(link, &headers->list);
http_header_t *header = list_get_instance(link, http_header_t, link);
list_remove(link);
http_header_destroy(header);
link = next;
}
}
HelenOS homepage, sources at GitHub