parse.c 4.13 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"
#include "cbuf.h"

ssize_t
httpRequestParserParse(HttpRequestParser this, int fd)
{
	int     cont = 1;
	ssize_t read;
	char *  line;

	if (cbufIsLocked(this->buffer)) {
		if (FALSE == this->ourLock)
			return 0;
	}
	else {
		cbufLock(this->buffer);
		this->ourLock = TRUE;
	}

	if (NULL != this->incomplete) {
		/**
		 * i need a way to stop incomplete requests
		 * from locking the buffer forever. 
		 * Maybe this is the position for this...
		 * but i must carefully think about the
		 * conditions...maybe a rewrite of the
		 * parser is neccessary to detect a
		 * stale request.
		 * The problem here seems to be that i can't
		 * say for sure if the request is stale.
		 * This is mostly because i work linewise.
		 * This MUST be accomplished within
		 * request line and header reads.
		 * As far as i see the only way it to
		 * always empty the buffer completely after
		 * every read and release the buffer then.
		 */
		cbufSetData(this->buffer, this->incomplete, this->isize);
		free(this->incomplete);
		this->incomplete = NULL;
	}

	if (0 > (read = cbufRead(this->buffer, fd))) {
		return read;
	}

	while (cont) {
		switch(this->state) {
			case HTTP_REQUEST_GARBAGE:
				cbufSkipNonAlpha(this->buffer);
				if (! cbufIsEmpty(this->buffer)) {
					this->cur_request = new(HttpRequest);
					this->state       = HTTP_REQUEST_START;
				}
				else {
					cbufRelease(this->buffer);
					this->ourLock = FALSE;
					cont          = 0;
				}

				break;

			case HTTP_REQUEST_START:
				if (NULL == (line = cbufGetLine(this->buffer))) {
					if (! cbufIsEmpty(this->buffer)) {
						this->isize = this->buffer->bused;
						this->incomplete = malloc(this->isize);
						memcpy(this->incomplete,
								cbufGetData(this->buffer, this->isize),
								this->isize);
					}
					cbufRelease(this->buffer);
					this->ourLock = FALSE;
					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 = cbufGetLine(this->buffer))) {
					if (! cbufIsEmpty(this->buffer)) {
						this->isize = this->buffer->bused;
						this->incomplete = malloc(this->isize);
						memcpy(this->incomplete,
								cbufGetData(this->buffer, this->isize),
								this->isize);
					}
					cbufRelease(this->buffer);
					this->ourLock = FALSE;
					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:
				{
					HttpMessage message = (HttpMessage)this->cur_request;

					httpRequestParserGetBody(this);
					if (cbufIsEmpty(this->buffer)) {
						cbufRelease(this->buffer);
						this->ourLock = FALSE;
					}

					if (message->dbody == message->nbody) {
						this->state = HTTP_REQUEST_DONE;
					}
				}
				break;

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

				this->cur_request = NULL;

				/**
				 * prepare for next request
				 */
				this->state = HTTP_REQUEST_GARBAGE;

				break;

			default:
				break;
		}
	}

	return ret;
}

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