Browse Source

moved request parser to generic parser. This is now able to create either a request or a response message dependent on the initial message line (version first or last). TODO change constructor of response to use a len parameter for the reason

master
Georg Hopp 14 years ago
parent
commit
ee6040201e
  1. 41
      include/http/parser.h
  2. 7
      include/http/worker.h
  3. 16
      include/utils/http.h
  4. 36
      src/Makefile.am
  5. 21
      src/http/message/has_keep_alive.c
  6. 9
      src/http/message/has_valid_version.c
  7. 30
      src/http/parser.c
  8. 36
      src/http/parser/body.c
  9. 61
      src/http/parser/header.c
  10. 34
      src/http/parser/new_message.c
  11. 77
      src/http/parser/parse.c
  12. 23
      src/http/request.c
  13. 13
      src/http/worker.c
  14. 9
      src/http/worker/process.c
  15. 57
      src/utils/http.c

41
include/http/request/parser.h → include/http/parser.h

@ -21,11 +21,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __HTTP_REQUEST_PARSER_H__
#define __HTTP_REQUEST_PARSER_H__
#ifndef __HTTP_PARSER_H__
#define __HTTP_PARSER_H__
#include "class.h"
#include "http/request.h"
#include "http/message.h"
#include "http/message/queue.h"
#include "cbuf.h"
@ -38,36 +38,33 @@
#endif
typedef enum e_HttpRequestState {
HTTP_REQUEST_GARBAGE=0,
HTTP_REQUEST_START,
HTTP_REQUEST_REQUEST_LINE_DONE,
HTTP_REQUEST_HEADERS_DONE,
HTTP_REQUEST_DONE
} HttpRequestState;
typedef enum e_HttpMessageState {
HTTP_MESSAGE_GARBAGE=0,
HTTP_MESSAGE_START,
HTTP_MESSAGE_INTRO_DONE,
HTTP_MESSAGE_HEADERS_DONE,
HTTP_MESSAGE_DONE
} HttpMessageState;
CLASS(HttpRequestParser) {
CLASS(HttpParser) {
Cbuf buffer;
void * ourLock;
char * incomplete;
size_t isize;
HttpMessageQueue request_queue;
HttpRequest cur_request;
HttpMessageQueue queue;
HttpMessage current;
HttpRequestState state;
HttpMessageState state;
};
ssize_t httpRequestParserParse(HttpRequestParser, int);
void httpRequestParserGetBody(HttpRequestParser);
ssize_t httpParserParse(void *, int);
void httpParserHeader(HttpParser, const char *, const char *);
void httpParserNewMessage(HttpParser, const char *, const char * lend);
size_t httpParserBody(HttpParser, const char *, size_t);
void httpRequestParserGetRequestLine(
HttpRequest, const char *, const char *);
void httpRequestParserGetHeader(
HttpRequest, const char *, const char *);
#endif // __HTTP_REQUEST_PARSER_H__
#endif // __HTTP_PARSER_H__
// vim: set ts=4 sw=4:

7
include/http/worker.h

@ -27,7 +27,7 @@
#include <sys/types.h>
#include "class.h"
#include "http/request/parser.h"
#include "http/parser.h"
#include "http/response/writer.h"
#include "cbuf.h"
@ -49,13 +49,10 @@ CLASS(HttpWorker) {
Cbuf pbuf;
Cbuf wbuf;
HttpRequestParser parser;
HttpParser parser;
HttpResponseWriter writer;
};
ssize_t httpWorkerProcess(HttpWorker, int);
ssize_t httpWorkerWrite(HttpWorker, int);
#endif // __HTTP_WORKER_H__
// vim: set ts=4 sw=4:

16
include/utils/http.h

@ -0,0 +1,16 @@
#ifndef __UTILS_HTTP_H__
#define __UTILS_HTTP_H__
#include <sys/types.h>
#include "http/message.h"
char isHttpVersion(const char *, size_t);
HttpMessage httpGetMessage(
const char *, size_t,
const char *, size_t,
const char *, size_t);
#endif // __UTILS_HTTP_H__
// vim: set ts=4 sw=4:

36
src/Makefile.am

@ -14,24 +14,38 @@ CB = cbuf.c cbuf/read.c cbuf/write.c \
cbuf/inc_read.c cbuf/inc_write.c cbuf/is_empty.c cbuf/memchr.c \
cbuf/skip_non_alpha.c cbuf/is_locked.c cbuf/lock.c cbuf/release.c \
cbuf/empty.c
MSG = http/message.c http/message/queue.c http/message/has_keep_alive.c \
http/message/header_size_get.c http/message/header_to_string.c \
http/message/get_version.c http/message/has_valid_version.c
REQ = http/request.c http/request/has_valid_method.c
MSG = http/message.c \
http/message/has_keep_alive.c \
http/message/header_size_get.c \
http/message/header_to_string.c \
http/message/get_version.c \
http/message/has_valid_version.c
MSGQ = http/message/queue.c
REQ = http/request.c \
http/request/has_valid_method.c
RESP = http/response.c \
http/response/304.c \
http/response/404.c \
http/response/asset.c \
http/response/me.c
WORKER = http/worker.c http/worker/process.c http/worker/write.c \
http/worker/get_asset.c http/worker/add_common_header.c
PARSER = http/parser.c \
http/parser/parse.c \
http/parser/new_message.c \
http/parser/header.c \
http/parser/body.c
WORKER = http/worker.c \
http/worker/process.c \
http/worker/write.c \
http/worker/get_asset.c \
http/worker/add_common_header.c
WRITER = http/response/writer.c http/response/writer/write.c
HEADER = http/header.c http/header/get.c http/header/add.c \
http/header/to_string.c
PARSER = http/request/parser.c http/request/parser/get_header.c \
http/request/parser/parse.c http/request/parser/get_request_line.c \
http/request/parser/get_body.c
UTILS = utils/hash.c utils/memory.c utils/daemonize.c utils/signalHandling.c
UTILS = utils/hash.c \
utils/memory.c \
utils/http.c \
utils/daemonize.c \
utils/signalHandling.c
AM_CFLAGS = -Wall -I ../include/
@ -41,6 +55,6 @@ bin_PROGRAMS = testserver
testserver_SOURCES = testserver.c \
$(IFACE) $(SOCKET) $(SERVER) $(LOGGER) $(MSG) $(REQ) \
$(WRITER) $(RESP) $(HEADER) $(PARSER) $(WORKER) $(CB) \
$(UTILS)
$(UTILS) $(MSGQ)
testserver_CFLAGS = -Wall -I ../include/
testserver_LDFLAGS = -lrt

21
src/http/message/has_keep_alive.c

@ -28,6 +28,13 @@
#include "http/request.h"
#include "http/header.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
char
httpMessageHasKeepAlive(HttpMessage message)
@ -35,7 +42,6 @@ httpMessageHasKeepAlive(HttpMessage message)
HttpHeader header;
size_t size;
char * value;
char * keep_alive = "keep-alive";
header = httpHeaderGet(
&(message->header),
@ -46,21 +52,10 @@ httpMessageHasKeepAlive(HttpMessage message)
return 0;
}
if ((sizeof("keep-alive")-1) != (header->nvalue)[0]) {
return 0;
}
size = (header->nvalue)[0];
value = (header->value)[0];
for (; 0 < size && tolower(*value) == *keep_alive;
size--, value++, keep_alive++);
if (0 == size) {
return 1;
}
return 0;
return (0 == strncasecmp("keep-alive", value, size))? 1 : 0;
}
// vim: set ts=4 sw=4:

9
src/http/message/has_valid_version.c

@ -23,6 +23,7 @@
#include <string.h>
#include "http/message.h"
#include "utils/http.h"
int
httpMessageHasValidVersion(HttpMessage this)
@ -30,18 +31,12 @@ httpMessageHasValidVersion(HttpMessage this)
int major;
int minor;
if (NULL == this->version)
return 0;
if (8 > strlen(this->version))
if (! isHttpVersion(this->version, strlen(this->version)))
return 0;
if (0 > httpMessageGetVersion(this, &major, &minor))
return 0;
if (0 != memcmp("HTTP/", this->version, sizeof("HTTP/")-1))
return 0;
if (1 != major)
return 0;

30
src/http/request/parser.c → src/http/parser.c

@ -28,9 +28,10 @@
#include "interface/class.h"
#include "interface/stream_reader.h"
#include "http/request/parser.h"
#include "http/parser.h"
#include "http/message/queue.h"
#include "http/request.h"
#include "http/response.h"
#include "cbuf.h"
#include "utils/memory.h"
@ -38,33 +39,38 @@
static
int
requestParserCtor(void * _this, va_list * params)
httpParserCtor(void * _this, va_list * params)
{
HttpRequestParser this = _this;
HttpParser this = _this;
this->buffer = va_arg(* params, Cbuf);
this->request_queue = new(HttpMessageQueue);
this->buffer = va_arg(* params, Cbuf);
if (NULL == this->buffer) {
return -1;
}
this->queue = new(HttpMessageQueue);
return 0;
}
static
void
requestParserDtor(void * _this)
httpParserDtor(void * _this)
{
HttpRequestParser this = _this;
HttpParser this = _this;
delete(this->request_queue);
delete(this->queue);
if (TRUE == this->ourLock)
cbufRelease(this->buffer);
FREE(this->incomplete);
delete(this->cur_request);
delete(this->current);
}
INIT_IFACE(Class, requestParserCtor, requestParserDtor, NULL);
INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpRequestParserParse);
CREATE_CLASS(HttpRequestParser, NULL, IFACE(Class), IFACE(StreamReader));
INIT_IFACE(Class, httpParserCtor, httpParserDtor, NULL);
INIT_IFACE(StreamReader, httpParserParse);
CREATE_CLASS(HttpParser, NULL, IFACE(Class), IFACE(StreamReader));
// vim: set ts=4 sw=4:

36
src/http/request/parser/get_header.c → src/http/parser/body.c

@ -20,31 +20,31 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include "class.h"
#include "interface/class.h"
#include "http/header.h"
#include "http/message.h"
#include "http/request/parser.h"
#include "ringbuffer.h"
void
httpRequestParserGetHeader(
HttpMessage message,
const char * line,
const char * line_end)
#include "http/parser.h"
#include "cbuf.h"
#define MAX(a,b) (((a) > (b))? (a) : (b))
size_t
httpParserBody(HttpParser this, const char * buf, size_t nbuf)
{
const char * name = line;
char * value = memchr(line, ':', line_end - line);
size_t nname = (value++) - name;
size_t len = 0;
HttpMessage current = this->current;
if (current->dbody < current->nbody) {
len = MAX(current->nbody - current->dbody, nbuf);
memcpy(current->body, buf, len);
for (; *value == ' ' && *value != 0; value++);
current->dbody += len;
}
httpHeaderAdd(
&(message->header),
new(HttpHeader, name, nname, value, line_end - value));
return len;
}
// vim: set ts=4 sw=4:

61
src/http/request/parser/get_body.c → src/http/parser/header.c

@ -21,14 +21,14 @@
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "class.h"
#include "interface/class.h"
#include "http/header.h"
#include "http/parser.h"
#include "http/message.h"
#include "http/request/parser.h"
#include "cbuf.h"
#define MAX(a,b) (((a) > (b))? (a) : (b))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
@ -37,41 +37,38 @@
* @TODO: not final...input buffer handling not final
*/
void
httpRequestParserGetBody(HttpRequestParser this)
httpParserHeader(
HttpParser this,
const char * line,
const char * lend)
{
HttpMessage message = (HttpMessage)(this->cur_request);
size_t len;
const char * name = line;
char * value = memchr(line, ':', lend - line);
size_t nname = (value++) - name;
HttpMessage current = this->current;
if (0 == message->nbody) {
HttpHeader clen = httpHeaderGet(
&(message->header),
"Content-Length",
sizeof("Content-Length")-1);
if (NULL == clen) {
this->state = HTTP_REQUEST_DONE;
return;
}
else {
message->type = HTTP_MESSAGE_BUFFERED;
message->nbody = atoi((clen->value)[0]);
if (0 < message->nbody) {
message->body = malloc(message->nbody);
}
message->dbody = 0;
}
if (NULL == value) {
return;
}
this->buffer->bused -= len;
if (message->dbody < message->nbody) {
len = MAX(
message->nbody - message->dbody,
this->buffer->bused);
for (; *value == ' ' && value < lend; value++);
memcpy(message->body, cbufGetData(this->buffer, len), len);
if (value == lend) {
return;
}
message->dbody += len;
if (0 == strncasecmp("content-length", name, nname-1)) {
current->nbody = strtoul(value, NULL, 10);
if (0 < this->current->nbody) {
current->body = malloc(current->nbody);
}
current->dbody = 0;
}
httpHeaderAdd(
&(current->header),
new(HttpHeader, name, nname, value, lend - value));
}
// vim: set ts=4 sw=4:

34
src/http/parser/new_message.c

@ -0,0 +1,34 @@
#include "http/parser.h"
#include "utils/http.h"
void
httpParserNewMessage(
HttpParser this,
const char * line,
const char * lend)
{
const char * part1, * part2, * part3;
size_t len1, len2, len3;
part1 = line;
part2 = memchr(line, ' ', lend - line);
if (NULL == part2) return;
len1 = part2 - part1;
for (; *part2 == ' ' && *part2 != 0; part2++);
part3 = memchr(part2, ' ', lend - part2);
if (NULL == part3) return;
len2 = part3 - part2;
for (; *part3 == ' ' && *part3 != 0; part3++);
len3 = lend - part3;
this->current = httpGetMessage(part1, len1, part2, len2, part3, len3);
}
// vim: set ts=4 sw=4:

77
src/http/request/parser/parse.c → src/http/parser/parse.c

@ -22,19 +22,19 @@
#include <stdlib.h>
#include "http/request.h"
#include "http/message.h"
#include "http/request/parser.h"
#include "http/parser.h"
#include "interface/class.h"
#include "interface/http_intro.h"
#include "cbuf.h"
ssize_t
httpRequestParserParse(HttpRequestParser this, int fd)
httpParserParse(void * _this, int fd)
{
int cont = 1;
ssize_t read;
char * line;
char * line_end;
HttpParser this = _this;
int cont = 1;
ssize_t read;
char * line;
char * line_end;
if (cbufIsLocked(this->buffer)) {
if (FALSE == this->ourLock)
@ -57,11 +57,10 @@ httpRequestParserParse(HttpRequestParser this, int fd)
while (cont) {
switch(this->state) {
case HTTP_REQUEST_GARBAGE:
case HTTP_MESSAGE_GARBAGE:
cbufSkipNonAlpha(this->buffer);
if (! cbufIsEmpty(this->buffer)) {
this->cur_request = new(HttpRequest);
this->state = HTTP_REQUEST_START;
this->state = HTTP_MESSAGE_START;
}
else {
cbufRelease(this->buffer);
@ -71,7 +70,7 @@ httpRequestParserParse(HttpRequestParser this, int fd)
break;
case HTTP_REQUEST_START:
case HTTP_MESSAGE_START:
if (NULL == (line = cbufGetLine(this->buffer, &line_end))) {
if (! cbufIsEmpty(this->buffer)) {
this->isize = this->buffer->bused;
@ -86,22 +85,17 @@ httpRequestParserParse(HttpRequestParser this, int fd)
break;
}
httpRequestParserGetRequestLine(this->cur_request, line, line_end);
if (! httpRequestHasValidMethod(this->cur_request)) {
cbufRelease(this->buffer);
this->ourLock = FALSE;
return -1;
}
if (! httpMessageHasValidVersion((HttpMessage)this->cur_request)) {
httpParserNewMessage(this, line, line_end);
if (NULL == this->current) {
cbufRelease(this->buffer);
this->ourLock = FALSE;
return -1;
}
this->state = HTTP_REQUEST_REQUEST_LINE_DONE;
this->state = HTTP_MESSAGE_INTRO_DONE;
break;
case HTTP_REQUEST_REQUEST_LINE_DONE:
case HTTP_MESSAGE_INTRO_DONE:
if (NULL == (line = cbufGetLine(this->buffer, &line_end))) {
if (! cbufIsEmpty(this->buffer)) {
this->isize = this->buffer->bused;
@ -116,46 +110,45 @@ httpRequestParserParse(HttpRequestParser this, int fd)
break;
}
if (0 == line_end - this->buffer->buffer - this->buffer->bstart) {
this->buffer->bstart += 2;
if (this->buffer->bstart >= this->buffer->bsize) {
this->buffer->bstart -= this->buffer->bsize;
}
this->buffer->bused -= 2;
this->state = HTTP_REQUEST_HEADERS_DONE;
if (0 == strlen(line)) {
this->state = HTTP_MESSAGE_HEADERS_DONE;
break;
}
httpRequestParserGetHeader(this->cur_request, line, line_end);
httpParserHeader(this->current, line, line_end);
break;
case HTTP_REQUEST_HEADERS_DONE:
case HTTP_MESSAGE_HEADERS_DONE:
{
HttpMessage message = (HttpMessage)this->cur_request;
cbufIncRead(
this->buffer,
httpParserBody(
this,
cbufGetRead(this->buffer),
this->buffer->bused));
httpRequestParserGetBody(this);
if (cbufIsEmpty(this->buffer)) {
cbufRelease(this->buffer);
this->ourLock = FALSE;
}
if (message->dbody == message->nbody) {
this->state = HTTP_REQUEST_DONE;
if (this->current->dbody == this->current->nbody) {
this->state = HTTP_MESSAGE_DONE;
}
}
break;
case HTTP_REQUEST_DONE:
this->request_queue->msgs[(this->request_queue->nmsgs)++] =
(HttpMessage)this->cur_request;
this->cur_request = NULL;
case HTTP_MESSAGE_DONE:
/**
* enqueue current request
*/
this->queue->msgs[(this->queue->nmsgs)++] = this->current;
this->current = NULL;
/**
* prepare for next request
*/
this->state = HTTP_REQUEST_GARBAGE;
this->state = HTTP_MESSAGE_GARBAGE;
break;
@ -164,7 +157,7 @@ httpRequestParserParse(HttpRequestParser this, int fd)
}
}
return ret;
return this->queue->nmsgs;
}
// vim: set ts=4 sw=4:

23
src/http/request.c

@ -37,10 +37,25 @@ static
int
httpRequestCtor(void * _this, va_list * params)
{
/**
* the parent is not called by intention. All infomations
* are read from the request stream.
*/
HttpRequest this = _this;
char * method, * uri;
size_t mlen, ulen;
method = va_arg(* params, char *);
mlen = va_arg(* params, size_t);
uri = va_arg(* params, char *);
ulen = va_arg(* params, size_t);
PARENTCALL(_this, Class, ctor, params);
this->method = malloc(mlen + 1);
this->method[mlen] = 0;
memcpy(this->method, method, mlen);
this->uri = malloc(ulen + 1);
this->uri[ulen] = 0;
memcpy(this->uri, uri, ulen);
return 0;
}

13
src/http/worker.c

@ -6,7 +6,7 @@
#include "class.h"
#include "http/worker.h"
#include "http/request/parser.h"
#include "http/parser.h"
#include "http/response/writer.h"
#include "interface/class.h"
@ -34,7 +34,7 @@ httpWorkerCtor(void * _this, va_list * params)
sprintf(cbuf_id, "%s_%s", "writer", id);
this->wbuf = new(Cbuf, cbuf_id, RESPONSE_WRITER_MAX_BUF);
this->parser = new(HttpRequestParser, this->pbuf);
this->parser = new(HttpParser, this->pbuf);
this->writer = new(HttpResponseWriter, this->wbuf);
return 0;
@ -67,13 +67,16 @@ _clone(void * _this, void * _base)
this->pbuf = NULL;
this->wbuf = NULL;
this->parser = new(HttpRequestParser, base->pbuf);
this->parser = new(HttpParser, base->pbuf);
this->writer = new(HttpResponseWriter, base->wbuf);
}
ssize_t httpWorkerProcess(void *, int);
ssize_t httpWorkerWrite(void *, int);
INIT_IFACE(Class, httpWorkerCtor, httpWorkerDtor, _clone);
INIT_IFACE(StreamReader, (fptr_streamReaderRead)httpWorkerProcess);
INIT_IFACE(StreamWriter, (fptr_streamWriterWrite)httpWorkerWrite);
INIT_IFACE(StreamReader, httpWorkerProcess);
INIT_IFACE(StreamWriter, httpWorkerWrite);
CREATE_CLASS(
HttpWorker,
NULL,

9
src/http/worker/process.c

@ -29,10 +29,7 @@
#include "http/message.h"
#include "http/request.h"
#include "http/message/queue.h"
#include "http/request/parser.h"
#include "http/header.h"
#include "http/request.h"
#include "http/message.h"
#include "http/parser.h"
HttpMessage httpWorkerGetAsset(HttpRequest, const char *, const char *, size_t);
void httpWorkerAddCommonHeader(HttpMessage, HttpMessage);
@ -45,9 +42,9 @@ httpWorkerProcess(HttpWorker this, int fd)
char buffer[200];
ssize_t size;
if (0 < (size = httpRequestParserParse(this->parser, fd))) {
if (0 < (size = httpParserParse(this->parser, fd))) {
int i;
HttpMessageQueue reqq = this->parser->request_queue;
HttpMessageQueue reqq = this->parser->queue;
HttpMessageQueue respq = this->writer->response_queue;
for (i=0; i<reqq->nmsgs; i++) {

57
src/utils/http.c

@ -0,0 +1,57 @@
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include "http/message.h"
#include "http/request.h"
#include "http/response.h"
#include "interface/class.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
char
isHttpVersion(const char * str, size_t len)
{
if (NULL == str)
return FALSE;
if (8 > len)
return FALSE;
if (0 != memcmp("HTTP/", str, sizeof("HTTP/")-1))
return FALSE;
return TRUE;
}
HttpMessage
httpGetMessage(
const char * part1, size_t len1,
const char * part2, size_t len2,
const char * part3, size_t len3)
{
if (isHttpVersion(part1, len1)) {
return new(HttpResponse,
part1, len1,
strtoul(part2, NULL, 10),
part3, len3);
}
if (isHttpVersion(part3, len3)) {
return new(HttpRequest,
part1, len1,
part2, len2,
part3, len3);
}
return NULL;
}
// vim: set ts=4 sw=4:
Loading…
Cancel
Save