parse.c 4 KB
/**
 * \file
 *
 * \author	Georg Hopp
 *
 * \copyright
 * Copyright (C) 2012  Georg Hopp
 *
 * 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 3 of the License, 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.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;
	char *  line_end;

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

	if (NULL != this->incomplete) {
		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, &line_end))) {
					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;
				}

				httpRequestParserGetRequestLine(this->cur_request, line);
				if (! httpRequestHasValidMethod(this->cur_request)) {
					cbufRelease(this->buffer);
					this->ourLock = FALSE;
					return -1;
				}
				if (! httpMessageHasValidVersion((HttpMessage)this->cur_request)) {
					cbufRelease(this->buffer);
					this->ourLock = FALSE;
					return -1;
				}

				this->state = HTTP_REQUEST_REQUEST_LINE_DONE;
				break;

			case HTTP_REQUEST_REQUEST_LINE_DONE:
				if (NULL == (line = cbufGetLine(this->buffer, &line_end))) {
					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->cur_request, line, line_end);
				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: