Renamed chunk sources, added request parser, header management, test file.

personal/stbuehler/wip
Stefan Bühler 14 years ago
parent 503e8d7859
commit 18413d698a
  1. 3
      src/base.c
  2. 31
      src/chunk.c
  3. 10
      src/chunk.h
  4. 78
      src/chunk_parser.c
  5. 54
      src/chunk_parser.h
  6. 2
      src/condition.c
  7. 95
      src/http_headers.c
  8. 27
      src/http_headers.h
  9. 18
      src/http_request_parser.h
  10. 118
      src/http_request_parser.rl
  11. 11
      src/request.c
  12. 7
      src/request.h
  13. 9
      src/tests.c
  14. 12
      src/wscript
  15. 46
      wscript

@ -13,5 +13,8 @@ gboolean parse_option(server *srv, const char *key, option *opt, option_mark *ma
sopt = find_option(srv, key);
if (!sopt) return FALSE;
/* TODO */
UNUSED(opt);
return FALSE;
}

@ -1,6 +1,6 @@
#include "base.h"
#include "chunks.h"
#include "chunk.h"
#include "log.h"
/******************
@ -179,35 +179,6 @@ read_chunk:
return HANDLER_GO_ON;
}
GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to) {
GString *str = g_string_sized_new(0);
chunk_parser_mark i;
for ( i = from; i.ci.element != to.ci.element; chunkiter_next(&i.ci) ) {
goffset len = chunkiter_length(i.ci);
while (i.pos < len) {
char *buf;
off_t we_have;
if (HANDLER_GO_ON != chunkiter_read(srv, con, i.ci, i.pos, len - i.pos, &buf, &we_have)) goto error;
g_string_append_len(str, buf, we_have);
i.pos += we_have;
}
i.pos = 0;
}
while (i.pos < to.pos) {
char *buf;
off_t we_have;
if (HANDLER_GO_ON != chunkiter_read(srv, con, i.ci, i.pos, to.pos - i.pos, &buf, &we_have)) goto error;
g_string_append_len(str, buf, we_have);
i.pos += we_have;
}
return str;
error:
g_string_free(str, TRUE);
return NULL;
}
/******************
* chunk *
******************/

@ -15,9 +15,6 @@ typedef struct chunkqueue chunkqueue;
struct chunkiter;
typedef struct chunkiter chunkiter;
struct chunk_parser_mark;
typedef struct chunk_parser_mark chunk_parser_mark;
#include "base.h"
/* Open a file only once, so it shouldn't get lost;
@ -69,11 +66,6 @@ struct chunkiter {
GList *element;
};
struct chunk_parser_mark {
chunkiter ci;
off_t pos;
};
/******************
* chunkfile *
******************/
@ -99,8 +91,6 @@ INLINE goffset chunkiter_length(chunkiter iter);
*/
LI_API handler_t chunkiter_read(server *srv, connection *con, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len);
LI_API GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to);
/******************
* chunk *
******************/

@ -0,0 +1,78 @@
#include "chunk_parser.h"
void chunk_parser_init(chunk_parser_ctx *ctx, chunkqueue *cq) {
ctx->cq = cq;
ctx->bytes_in = 0;
ctx->curi.element = NULL;
ctx->start = 0;
ctx->length = 0;
ctx->buf = NULL;
}
handler_t chunk_parser_prepare(chunk_parser_ctx *ctx) {
if (NULL == ctx->curi.element) {
ctx->curi = chunkqueue_iter(ctx->cq);
if (NULL == ctx->curi.element) return HANDLER_WAIT_FOR_EVENT;
}
return HANDLER_GO_ON;
}
handler_t chunk_parser_next(server *srv, connection *con, chunk_parser_ctx *ctx, char **p, char **pe) {
off_t l;
handler_t res;
if (NULL == ctx->curi.element) return HANDLER_WAIT_FOR_EVENT;
while (ctx->start >= (l = chunkiter_length(ctx->curi))) {
chunkiter i = ctx->curi;
/* Wait at the end of the last chunk if it gets extended */
if (!chunkiter_next(&i)) return HANDLER_WAIT_FOR_EVENT;
ctx->curi = i;
ctx->start = 0;
}
if (NULL == ctx->curi.element) return HANDLER_WAIT_FOR_EVENT;
if (HANDLER_GO_ON != (res = chunkiter_read(srv, con, ctx->curi, ctx->start, l - ctx->start, &ctx->buf, &ctx->length))) {
return res;
}
*p = ctx->buf;
*pe = ctx->buf + ctx->length;
return HANDLER_GO_ON;
}
void chunk_parser_done(chunk_parser_ctx *ctx, goffset len) {
ctx->bytes_in += len;
ctx->start += len;
}
GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to) {
GString *str = g_string_sized_new(0);
chunk_parser_mark i;
for ( i = from; i.ci.element != to.ci.element; chunkiter_next(&i.ci) ) {
goffset len = chunkiter_length(i.ci);
while (i.pos < len) {
char *buf;
off_t we_have;
if (HANDLER_GO_ON != chunkiter_read(srv, con, i.ci, i.pos, len - i.pos, &buf, &we_have)) goto error;
g_string_append_len(str, buf, we_have);
i.pos += we_have;
}
i.pos = 0;
}
while (i.pos < to.pos) {
char *buf;
off_t we_have;
if (HANDLER_GO_ON != chunkiter_read(srv, con, i.ci, i.pos, to.pos - i.pos, &buf, &we_have)) goto error;
g_string_append_len(str, buf, we_have);
i.pos += we_have;
}
return str;
error:
g_string_free(str, TRUE);
return NULL;
}

@ -0,0 +1,54 @@
#ifndef _LIGHTTPD_CHUNK_PARSER_H_
#define _LIGHTTPD_CHUNK_PARSER_H_
struct chunk_parser_ctx;
typedef struct chunk_parser_ctx chunk_parser_ctx;
struct chunk_parser_mark;
typedef struct chunk_parser_mark chunk_parser_mark;
#include "chunk.h"
struct chunk_parser_ctx {
chunkqueue *cq;
goffset bytes_in;
/* current position
* buf is curi[start..start+length)
*/
chunkiter curi;
off_t start, length;
char *buf;
int cs;
};
struct chunk_parser_mark {
chunkiter ci;
off_t pos;
};
LI_API void chunk_parser_init(chunk_parser_ctx *ctx, chunkqueue *cq);
LI_API handler_t chunk_parser_prepare(chunk_parser_ctx *ctx);
LI_API handler_t chunk_parser_next(server *srv, connection *con, chunk_parser_ctx *ctx, char **p, char **pe);
LI_API void chunk_parser_done(chunk_parser_ctx *ctx, goffset len);
LI_API GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to);
INLINE chunk_parser_mark chunk_parser_getmark(chunk_parser_ctx *ctx, const char *fpc);
/********************
* Inline functions *
********************/
INLINE chunk_parser_mark chunk_parser_getmark(chunk_parser_ctx *ctx, const char *fpc) {
chunk_parser_mark m;
m.ci = ctx->curi;
m.pos = ctx->start + fpc - ctx->buf;
return m;
}
#define GETMARK(FPC) (chunk_parser_getmark(&ctx->chunk_ctx, FPC))
#endif

@ -281,6 +281,8 @@ static gboolean condition_check_eval_int(server *srv, connection *con, condition
assert(NULL);
return FALSE;
}
return FALSE;
}

@ -0,0 +1,95 @@
#include "http_headers.h"
static void _string_free(gpointer p) {
g_string_free((GString*) p, TRUE);
}
http_headers* http_headers_new() {
http_headers* headers = g_slice_new0(http_headers);
headers->table = g_hash_table_new_full(
(GHashFunc) g_string_hash, (GEqualFunc) g_string_equal,
_string_free, _string_free);
return headers;
}
void http_headers_free(http_headers* headers) {
if (!headers) return;
g_hash_table_destroy(headers->table);
g_slice_free(http_headers, headers);
}
/* Just insert the header (using lokey)
*/
static void header_insert(http_headers *headers, GString *lokey, GString *key, GString *value) {
GString *newval = g_string_sized_new(key->len + value->len + 2);
g_string_append_len(newval, key->str, key->len);
g_string_append_len(newval, ": ", 2);
g_string_append_len(newval, value->str, value->len);
g_hash_table_insert(headers->table, lokey, newval);
}
/** If header does not exist, just insert normal header. If it exists, append (", %s", value) */
void http_header_append(http_headers *headers, GString *key, GString *value) {
GString *lokey, *tval;
lokey = g_string_new_len(key->str, key->len);
g_string_ascii_down(lokey);
tval = (GString*) g_hash_table_lookup(headers->table, lokey);
if (!tval) {
header_insert(headers, lokey, key, value);
} else {
g_string_free(lokey, TRUE);
g_string_append_len(tval, ", ", 2);
g_string_append_len(tval, value->str, value->len);
}
}
/** If header does not exist, just insert normal header. If it exists, append ("\r\n%s: %s", key, value) */
void http_header_insert(http_headers *headers, GString *key, GString *value) {
GString *lokey, *tval;
lokey = g_string_new_len(key->str, key->len);
g_string_ascii_down(lokey);
tval = (GString*) g_hash_table_lookup(headers->table, lokey);
if (!tval) {
header_insert(headers, lokey, key, value);
} else {
g_string_free(lokey, TRUE);
g_string_append_len(tval, "\r\n", 2);
g_string_append_len(tval, key->str, key->len);
g_string_append_len(tval, ": ", 2);
g_string_append_len(tval, value->str, value->len);
}
}
/** If header does not exist, just insert normal header. If it exists, overwrite the value */
void http_header_overwrite(http_headers *headers, GString *key, GString *value) {
GString *lokey, *tval;
lokey = g_string_new_len(key->str, key->len);
g_string_ascii_down(lokey);
tval = (GString*) g_hash_table_lookup(headers->table, lokey);
if (!tval) {
header_insert(headers, lokey, key, value);
} else {
g_string_free(lokey, TRUE);
g_string_truncate(tval, 0);
g_string_append_len(tval, key->str, key->len);
g_string_append_len(tval, ": ", 2);
g_string_append_len(tval, value->str, value->len);
}
}
LI_API gboolean http_header_remove(http_headers *headers, GString *key) {
GString *lokey;
gboolean res;
lokey = g_string_new_len(key->str, key->len);
g_string_ascii_down(lokey);
res = g_hash_table_remove(headers->table, lokey);
g_string_free(lokey, TRUE);
return res;
}

@ -0,0 +1,27 @@
#ifndef _LIGHTTPD_HTTP_HEADERS_H_
#define _LIGHTTPD_HTTP_HEADERS_H_
struct http_headers;
typedef struct http_headers http_headers;
#include "settings.h"
struct http_headers {
/** keys are lowercase header name, values contain the complete header */
GHashTable *table;
};
/* strings alweays get copied, so you should free key and value yourself */
LI_API http_headers* http_headers_new();
LI_API void http_headers_free(http_headers* headers);
/** If header does not exist, just insert normal header. If it exists, append (", %s", value) */
LI_API void http_header_append(http_headers *headers, GString *key, GString *value);
/** If header does not exist, just insert normal header. If it exists, append ("\r\n%s: %s", key, value) */
LI_API void http_header_insert(http_headers *headers, GString *key, GString *value);
/** If header does not exist, just insert normal header. If it exists, overwrite the value */
LI_API void http_header_overwrite(http_headers *headers, GString *key, GString *value);
LI_API gboolean http_header_remove(http_headers *headers, GString *key);
#endif

@ -4,23 +4,15 @@
struct http_request_ctx;
typedef struct http_request_ctx http_request_ctx;
#include "chunks.h"
#include "chunk_parser.h"
#include "request.h"
struct http_request_ctx {
chunkqueue *cq;
goffset bytes_in;
/* current position
* buf is curi[start..start+length)
*/
chunkiter curi;
off_t start, length;
char *buf;
int cs;
chunk_parser_ctx chunk_ctx;
chunk_parser_mark mark;
request *request;
};
#endif

@ -1,55 +1,109 @@
#include "http_request_parser.h"
static chunk_parser_mark getmark(http_request_ctx *ctx, const char *fpc) {
chunk_parser_mark m;
m.ci = ctx->curi;
m.pos = ctx->start + fpc - ctx->buf;
return m;
}
/** Machine **/
#define _getString(M, FPC) (chunk_extract(srv, con, ctx->M, GETMARK(FPC)))
#define getString(FPC) _getString(mark, FPC)
%%{
machine http_request_parser;
variable cs ctx->chunk_ctx.cs;
action mark { ctx->mark = GETMARK(fpc); }
action done { fbreak; }
action method { ctx->request->http_method_str = getString(fpc); }
action uri { ctx->request->uri.uri = getString(fpc); }
# RFC 2616
OCTET = any;
CHAR = ascii;
UPALPHA = upper;
LOALPHA = lower;
ALPHA = alpha;
DIGIT = digit;
CTL = ( 0 .. 31 | 127 );
CR = '\r';
LF = '\n';
SP = ' ';
HT = '\t';
DQUOTE = '"';
CRLF = CR LF;
LWS = CRLF? (SP | HT)+; # linear white space
TEXT = (OCTET - CTL) | LWS;
HEX = [a-fA-F0-9];
Separators = [()<>@,;:\\\"/\[\]?={}] | SP | HT;
Token = OCTET - Separators - CTL;
# original definition
# Comment = "(" ( CText | Quoted_Pair | Comment )* ")";
# CText = TEXT - [()];
Quoted_Pair = "\\" CHAR;
Comment = ( TEXT | Quoted_Pair )*;
QDText = TEXT - DQUOTE;
Quoted_String = DQUOTE ( QDText | Quoted_Pair )* DQUOTE;
HTTP_Version = "HTTP" "/" DIGIT+ "." DIGIT+;
#HTTP_URL = "http:" "//" Host ( ":" Port )? ( abs_path ( "?" query )? )?;
# RFC 2396
CRLF = "\r\n";
Mark = [\-_!~*\'()];
Unreserved = alnum | Mark;
Escaped = "%" HEX HEX;
action mark { ctx->mark = getmark(ctx, fpc); }
PChar = Unreserved | Escaped | [:@&=+$,];
Segment = PChar* ( ";" PChar* )*;
Path_Segments = Segment ("/" Segment)*;
Abs_Path = "/" Path_Segments;
main := CRLF ;
Method = Token >mark %method;
Request_URI = ("*" | ( any - CTL - SP )) >mark %uri;
Request_Line = Method " " Request_URI " " HTTP_Version CRLF;
Field_Content = ( TEXT+ | ( Token | Separators | Quoted_String )+ );
Field_Value = " "* (Field_Content+ ( Field_Content | LWS )*)? >mark;
Message_Header = Token ":" Field_Value?;
main := (CRLF)* Request_Line (Message_Header CRLF)* CRLF @ done;
}%%
%% write data;
void http_request_parse(server *srv, connection *con, http_request_ctx *ctx) {
int cs = ctx->cs;
while (cs != http_request_parser_error && cs != http_request_parser_first_final) {
char *p, *pe;
off_t l;
static int http_request_parser_has_error(http_request_ctx *ctx) {
return ctx->chunk_ctx.cs == http_request_parser_error;
}
l = chunkiter_length(ctx->curi);
if (ctx->start >= l) {
chunkiter_next(&ctx->curi);
continue;
}
static int http_request_parser_is_finished(http_request_ctx *ctx) {
return ctx->chunk_ctx.cs >= http_request_parser_first_final;
}
if (HANDLER_GO_ON != chunkiter_read(srv, con, ctx->curi, ctx->start, l - ctx->start, &ctx->buf, &ctx->length)) {
return;
}
void http_request_parser_init(http_request_ctx *ctx, chunkqueue *cq) {
%% write init;
chunk_parser_init(&ctx->chunk_ctx, cq);
}
p = ctx->buf;
pe = ctx->buf + ctx->length;
handler_t http_request_parse(server *srv, connection *con, http_request_ctx *ctx) {
handler_t res;
if (HANDLER_GO_ON != (res = chunk_parser_prepare(&ctx->chunk_ctx))) return res;
while (!http_request_parser_has_error(ctx) && !http_request_parser_is_finished(ctx)) {
char *p, *pe;
if (HANDLER_GO_ON != (res = chunk_parser_next(srv, con, &ctx->chunk_ctx, &p, &pe))) return res;
%% write exec;
ctx->start += pe - p;
ctx->bytes_in += pe - p;
if (ctx->start >= l) {
chunkiter_next(&ctx->curi);
ctx->start = 0;
}
chunk_parser_done(&ctx->chunk_ctx, pe - p);
}
ctx->cs = cs;
if (http_request_parser_has_error(ctx)) return HANDLER_ERROR;
if (http_request_parser_is_finished(ctx)) return HANDLER_GO_ON;
return HANDLER_ERROR;
}

@ -0,0 +1,11 @@
#include "request.h"
request* request_new() {
request *req = g_slice_new0(request);
}
void request_free(request *req) {
/* TODO */
g_slice_free(request, req);
}

@ -43,6 +43,8 @@ typedef struct request_uri request_uri;
struct physical;
typedef struct physical physical;
#include "http_headers.h"
struct request_uri {
GString *uri, *orig_uri;
@ -70,10 +72,13 @@ struct request {
request_uri uri;
GArray *headers;
http_headers *headers;
/* Parsed headers: */
GString *host;
goffset content_length;
};
LI_API request* request_new();
LI_API void request_free(request *req);
#endif

@ -0,0 +1,9 @@
#include "base.h"
int request_test() {
}
int main() {
return 0;
}

@ -9,8 +9,10 @@ common_uselib = 'glib '
common_source='''
actions.c
base.c
chunks.c
chunk.c
chunk_parser.c
condition.c
http_headers.c
http_request_parser.rl
log.c
options.c
@ -119,6 +121,14 @@ def build(bld):
#lighty_mod(bld, 'mod_magnet', 'mod_magnet.c mod_magnet_cache.c', uselib = 'lua')
#lighty_mod(bld, 'mod_deflate', 'mod_deflate.c', uselib = 'bzip zlib')
#lighty_mod(bld, 'mod_webdav', 'mod_webdav.c', uselib = 'sqlite3 xml uuid')
tests = bld.new_task_gen('cc', 'program')
tests.inst_var = 0
tests.unit_test = 1
tests.name = 'tests'
tests.source = 'tests.c ' + common_source
tests.target = 'tests'
tests.uselib += 'lighty dl openssl pcre lua ' + common_uselib
def configure(conf):
env = conf.env

@ -449,46 +449,12 @@ class TestObject:
return self.label
def run_tests():
import UnitTest, Object
env = Params.g_build.env()
srctestnode = Params.g_build.m_srcnode.find_dir('tests')
prepare = srctestnode.find_source('prepare.sh')
runtests = srctestnode.find_source('run-tests.pl')
cleanup = srctestnode.find_source('cleanup.sh')
print
import os
os.environ['srcdir'] = srctestnode.abspath()
os.environ['top_builddir'] = srctestnode.m_parent.abspath(env)
if Params.g_options.verbose: os.environ['VERBOSE'] = '1'
Runner.exec_command_interact(prepare.abspath())
print
singletest = '' # '*.t' or '.t' or ''
if singletest:
objsave = Object.g_allobjs
tests = []
for f in srctestnode.files():
if f.m_name.endswith(singletest):
tests.append(TestObject(f.m_name, f.abspath()))
Object.g_allobjs = tests
unittest = UnitTest.unit_test()
unittest.want_to_see_test_output = Params.g_options.verbose
unittest.want_to_see_test_error = Params.g_options.verbose
unittest.run()
unittest.print_results()
Object.g_allobjs = objsave
else:
Runner.exec_command_interact(runtests.abspath())
print
Runner.exec_command_interact(cleanup.abspath())
print
import UnitTest
unittest = UnitTest.unit_test()
unittest.want_to_see_test_output = Params.g_options.verbose
unittest.want_to_see_test_error = Params.g_options.verbose
unittest.run()
unittest.print_results()
def shutdown():
if Params.g_commands['check']: run_tests()

Loading…
Cancel
Save