An API to handle HTTP messages as concrete entities.
This commit is contained in:
parent
2f9370afe7
commit
686826c3b0
252
src/http_message.c
Normal file
252
src/http_message.c
Normal file
@ -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;
|
||||
}
|
90
src/http_message.h
Normal file
90
src/http_message.h
Normal file
@ -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_ */
|
Loading…
Reference in New Issue
Block a user