parse.c 3.19 KB
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

#include "http/request.h"
#include "http/message.h"
#include "http/request/parser.h"
#include "interface/class.h"

#ifndef TRUE
#define TRUE	1
#endif

#ifndef FALSE
#define FALSE	0
#endif

static
inline
char *
getLine(HttpRequestParser this)
{
	char * cr = memchr(
			this->buffer->buffer + this->buffer->bstart,
			'\r',
			this->buffer->bused);

	char * nl = (NULL == cr)? NULL : cr + 1;

	if (NULL != cr && NULL != nl && '\n' == *nl) {
		*cr = 0;
		return cr;
	}

	return NULL;
}

static
inline
char
httpRequestSkip(HttpRequestParser this)
{
	while (this->buffer->bused > 0 &&
			! isalpha(this->buffer->buffer[this->buffer->bstart])) {
		this->buffer->bstart = (this->buffer->bstart >= this->buffer->bsize)?
			0 : this->buffer->bstart + 1;
		this->buffer->bused--;
	}

	return (isalpha(this->buffer->buffer[this->buffer->bstart]))? TRUE : FALSE;
}

ssize_t
httpRequestParserParse(HttpRequestParser this, int fd)
{
	int     cont = 1;
	ssize_t ret;

	if (0 > (ret = rbRead(this->buffer, fd))) {
		cont = 0;
	}

	while (cont) {
		switch(this->state) {
			char * line_end;
			size_t len;

			case HTTP_REQUEST_GARBAGE:
				if (httpRequestSkip(this)) {
					this->cur_request = new(HttpRequest);
					this->state       = HTTP_REQUEST_START;
				}
				else {
					cont = 0;
				}
				break;

			case HTTP_REQUEST_START:
				if (NULL == (line_end = getLine(this))) {
					cont = 0;
					break;
				}
				
				if (0 > httpRequestParserGetRequestLine(this, line_end)) {
					ret  = -1;
					cont = 0;
					break;
				}

				len = line_end - this->buffer->buffer - this->buffer->bstart + 2;
				this->buffer->bstart += len;
				if (this->buffer->bstart >= this->buffer->bsize) {
					this->buffer->bstart -= this->buffer->bsize;
				}
				this->buffer->bused  -= len;

				this->state = HTTP_REQUEST_REQUEST_LINE_DONE;
				break;

			case HTTP_REQUEST_REQUEST_LINE_DONE:
				if (NULL == (line_end = getLine(this))) {
					cont = 0;
					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;
					break;
				}

				httpRequestParserGetHeader(this, line_end);

				len = line_end - this->buffer->buffer - this->buffer->bstart + 2;
				this->buffer->bstart += len;
				if (this->buffer->bstart >= this->buffer->bsize) {
					this->buffer->bstart -= this->buffer->bsize;
				}
				this->buffer->bused  -= len;

				break;

			case HTTP_REQUEST_HEADERS_DONE:
				/**
				 * allocate memory according to content-length.
				 * If content length is to large reject request.
				 *
				 * @FUTURE check for multipart mime and handle it
				 * with temporary file.
				 */
				httpRequestParserGetBody(this);
				break;

			case HTTP_REQUEST_DONE:
				this->request_queue->msgs[(this->request_queue->nmsgs)++] =
					(HttpMessage)this->cur_request;

				ret = this->request_queue->nmsgs;
				this->cur_request = NULL;

				this->state = HTTP_REQUEST_GARBAGE;

				break;

			default:
				break;
		}
	}

	return ret;
}

// vim: set ts=4 sw=4: