diff options
Diffstat (limited to 'src/http_message.c')
-rw-r--r-- | src/http_message.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/http_message.c b/src/http_message.c new file mode 100644 index 0000000..6f19db9 --- /dev/null +++ b/src/http_message.c @@ -0,0 +1,252 @@ +/* $Id: http_message.c,v 1.1 2003-03-13 05:25:30 rjkaes Exp $ + * + * See 'http_message.h' for a detailed description. + * + * Copyright (C) 2003 Robert James Kaes (rjkaes@flarenet.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program 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 + * General Public License for more details. + */ + +#include "common.h" +#include "heap.h" +#include "http_message.h" +#include "network.h" + +/* + * Package up an HTTP message into a nice little structure. As you can + * see this structure doesn't actually store any allocated strings; + * therefore, the caller must free any memory referenced by this struct. + * Also, the caller MUST NOT free the memory while the structure is + * still in use---bad things would happen. + */ +struct http_message_s { + /* Response string and code supplied on the HTTP status line */ + struct { + const char* string; + int code; + } response; + + /* + * A group of headers to be sent with this message. Right now + * the strings are referenced through pointers in an array. + * I might change this to a vector in the future. + */ + struct { + char** strings; + unsigned int total; + unsigned int used; + } headers; + + /* Body of the message (most likely an HTML message) */ + struct { + const char* text; + size_t length; + } body; +}; + +/* + * Check if the HTTP message is validly formed. This is the one odd-ball + * function. It returns 0 if the message is invalid; otherwise, a positive + * number is returned. Useful for if() tests and assert() tests. + */ +static int +is_http_message_valid(http_message_t msg) +{ + if (msg == NULL) return 0; + if (msg->headers.strings == NULL) return 0; + if (msg->response.string == NULL) return 0; + if (msg->response.code < 1 || msg->response.code > 999) return 0; + + return 1; +} + +/* Initially allocate space for 128 headers */ +#define NUMBER_OF_HEADERS 128 + +/* + * Allocate a new http_message structure on the heap. + * If memory could not be allocated, return a NULL. + */ +http_message_t +http_message_create(int response_code, const char* response_string) +{ + http_message_t msg; + int ret; + + msg = safecalloc(1, sizeof(struct http_message_s)); + if (msg == NULL) + return NULL; + + msg->headers.strings = safecalloc(NUMBER_OF_HEADERS, sizeof(char*)); + if (msg->headers.strings == NULL) { + safefree(msg); + return NULL; + } + + msg->headers.total = NUMBER_OF_HEADERS; + + /* Store the HTTP response information in the structure */ + ret = http_message_set_response(msg, response_code, response_string); + if (IS_HTTP_MSG_ERROR(ret)) { + safefree(msg->headers.strings); + safefree(msg); + return NULL; + } + + return msg; +} + +/* + * Free up the space associated with this HTTP message structure. + * This DOES NOT free the pointers stored in this structure. That memory + * is the responsibility of the caller. + */ +int +http_message_destroy(http_message_t msg) +{ + assert(msg != NULL); + assert(msg->headers.strings != NULL); + + /* Check for valid arguments */ + if (msg == NULL) return -EFAULT; + + if (msg->headers.strings != NULL) + safefree(msg->headers.strings); + safefree(msg); + return 0; +} + +/* + * Set the HTTP response information for this structure. The response_string + * must be a NUL ('\0') terminated C string. + */ +int +http_message_set_response(http_message_t msg, + int response_code, + const char* response_string) +{ + /* Check for valid arguments */ + if (msg == NULL) return -EFAULT; + if (response_code < 1 || response_code > 999) return -EINVAL; + if (response_string == NULL) return -EINVAL; + if (strlen(response_string) == 0) return -EINVAL; + + msg->response.code = response_code; + msg->response.string = response_string; + + return 0; +} + +/* + * Set the HTTP message body. + */ +int +http_message_set_body(http_message_t msg, const char* body, size_t len) +{ + /* Check for valid arguments */ + if (msg == NULL) return -EFAULT; + if (body == NULL) return -EINVAL; + if (len == 0) return -EINVAL; + + msg->body.text = body; + msg->body.length = len; + + return 0; +} + +/* + * Add headers to the structure. + */ +int +http_message_add_headers(http_message_t msg, char** headers, + int num_headers) +{ + char** new_headers; + int i; + + /* Check for valid arguments */ + if (msg == NULL) return -EFAULT; + if (headers == NULL) return -EINVAL; + if (num_headers < 1) return -EINVAL; + + /* + * If the number of headers to add is greater than the space + * available, reallocate the memory. + */ + if (msg->headers.used + num_headers > msg->headers.total) { + new_headers = safecalloc(msg->headers.total * 2, + sizeof(char*)); + if (new_headers == NULL) + return -ENOMEM; + + /* Copy the array */ + for (i = 0; i < msg->headers.used; ++i) + new_headers[i] = msg->headers.strings[i]; + + /* Remove the old array and replace it with the new array */ + safefree(msg->headers.strings); + msg->headers.strings = new_headers; + msg->headers.total *= 2; + } + + /* + * Add the new headers to the structure + */ + for (i = 0; i < num_headers; ++i) + msg->headers.strings[i + msg->headers.used] = headers[i]; + msg->headers.used += num_headers; + + return 0; +} + +/* + * Send the completed HTTP message via the supplied file descriptor. + */ +int +http_message_send(http_message_t msg, int fd) +{ + char timebuf[30]; + time_t global_time; + int i; + + assert(is_http_message_valid(msg)); + + /* Check for valid arguments */ + if (msg == NULL) return -EFAULT; + if (fd < 1) return -EBADF; + if (!is_http_message_valid(msg)) return -EINVAL; + + /* Write the response line */ + write_message(fd, "HTTP/1.0 %d %s\r\n", + msg->response.code, msg->response.string); + + /* Go through all the headers */ + for (i = 0; i < msg->headers.used; ++i) + write_message(fd, "%s\r\n", msg->headers.strings[i]); + + /* Output the date */ + global_time = time(NULL); + strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", + gmtime(&global_time)); + write_message(fd, "Date: %s\r\n", timebuf); + + /* Output the content-length */ + write_message(fd, "Content-length: %u\r\n", msg->body.length); + + /* Write the separator between the headers and body */ + safe_write(fd, "\r\n", 2); + + /* If there's a body, send it! */ + if (msg->body.length > 0) + safe_write(fd, msg->body.text, msg->body.length); + + return 0; +} |