summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/http_message.c252
-rw-r--r--src/http_message.h90
2 files changed, 342 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;
+}
diff --git a/src/http_message.h b/src/http_message.h
new file mode 100644
index 0000000..0344410
--- /dev/null
+++ b/src/http_message.h
@@ -0,0 +1,90 @@
+/* $Id: http_message.h,v 1.1 2003-03-13 05:25:30 rjkaes Exp $
+ *
+ * HTTP Message API
+ * ----------------
+ * The idea behind this application programming interface (API) is to
+ * represent an HTTP response message as a concrete entity. The API
+ * functions allow the message to be built up systematically before
+ * transmission to a connected socket.
+ *
+ * The order of the functions in your program would look something like
+ * this:
+ * http_message_create()
+ * http_message_set_response()
+ * http_message_set_body() [optional if no body is required]
+ * http_message_add_headers() [optional if no additional headers are used]
+ * http_message_send()
+ * http_message_destroy()
+ *
+ * NOTE: No user data is stored in the http_message_t type; therefore,
+ * do not delete strings referenced by the http_message_t object
+ * before you call http_message_destroy(). By not copying data, the
+ * API functions are faster, but you must take greater care.
+ *
+ * (Side note: be _very_ careful when using stack allocated memory with
+ * this API. Bad things will happen if you try to pass the
+ * http_message_t out of the calling function since the stack
+ * allocated memory referenced by the http_message_t will no long
+ * exist.)
+ *
+ * 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.
+ */
+
+#ifndef _TINYPROXY_HTTP_MESSAGE_H_
+#define _TINYPROXY_HTTP_MESSAGE_H_
+
+/* Use the "http_message_t" as a cookie or handle to the structure. */
+typedef struct http_message_s *http_message_t;
+
+/*
+ * Macro to test if an error occurred with the API. All the HTTP message
+ * functions will return 0 if no error occurred, or a negative number if
+ * there was a problem.
+ */
+#define IS_HTTP_MSG_ERROR(x) (x < 0)
+
+/* Initialize the internal structure of the HTTP message */
+extern http_message_t http_message_create(int response_code,
+ const char* response_string);
+
+/* Free up an _internal_ resources */
+extern int http_message_destroy(http_message_t msg);
+
+/*
+ * Send an HTTP message via the supplied file descriptor. This function
+ * will add the "Date" header before it's sent.
+ */
+extern int http_message_send(http_message_t msg, int fd);
+
+/*
+ * Change the internal state of the HTTP message. Either set the
+ * body of the message, update the response information, or
+ * add a new set of headers.
+ */
+extern int http_message_set_body(http_message_t msg,
+ const char* body, size_t len);
+extern int http_message_set_response(http_message_t msg,
+ int response_code,
+ const char* response_string);
+
+/*
+ * Set the headers for this HTTP message. Each string must be NUL ('\0')
+ * terminated, but DO NOT include any carriage returns (CR) or
+ * line-feeds (LF) since they will be included when the http_message is
+ * sent.
+ */
+extern int http_message_add_headers(http_message_t msg,
+ char** headers,
+ int num_headers);
+
+#endif /* _TINYPROXY_HTTP_MESSAGE_H_ */