Continue request parser
This commit is contained in:
parent
d45b540603
commit
d1c1a1b06f
|
@ -29,7 +29,7 @@ handler_t chunk_parser_next(server *srv, connection *con, chunk_parser_ctx *ctx,
|
|||
/* 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;
|
||||
ctx->start -= l;
|
||||
}
|
||||
|
||||
if (NULL == ctx->curi.element) return HANDLER_WAIT_FOR_EVENT;
|
||||
|
@ -48,8 +48,9 @@ void chunk_parser_done(chunk_parser_ctx *ctx, goffset 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);
|
||||
gboolean chunk_extract_to(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to, GString *dest) {
|
||||
g_string_set_size(dest, 0);
|
||||
|
||||
chunk_parser_mark i;
|
||||
for ( i = from; i.ci.element != to.ci.element; chunkiter_next(&i.ci) ) {
|
||||
goffset len = chunkiter_length(i.ci);
|
||||
|
@ -57,7 +58,7 @@ GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chu
|
|||
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);
|
||||
g_string_append_len(dest, buf, we_have);
|
||||
i.pos += we_have;
|
||||
}
|
||||
i.pos = 0;
|
||||
|
@ -66,13 +67,20 @@ GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chu
|
|||
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);
|
||||
g_string_append_len(dest, buf, we_have);
|
||||
i.pos += we_have;
|
||||
}
|
||||
|
||||
return str;
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
g_string_assign(dest, "");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to) {
|
||||
GString *str = g_string_sized_new(0);
|
||||
if (chunk_extract_to(srv, con, from, to, str)) return str;
|
||||
g_string_free(str, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ 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 gboolean chunk_extract_to(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to, GString *dest);
|
||||
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);
|
||||
|
|
|
@ -9,13 +9,15 @@ typedef struct http_request_ctx http_request_ctx;
|
|||
|
||||
struct http_request_ctx {
|
||||
chunk_parser_ctx chunk_ctx;
|
||||
request *request;
|
||||
|
||||
chunk_parser_mark mark;
|
||||
|
||||
request *request;
|
||||
GString *h_key, *h_value;
|
||||
};
|
||||
|
||||
LI_API void http_request_parser_init(http_request_ctx *ctx, request *req, chunkqueue *cq);
|
||||
LI_API http_request_ctx* http_request_parser_new(request *req, chunkqueue *cq);
|
||||
LI_API void http_request_parser_free(http_request_ctx *ctx);
|
||||
|
||||
LI_API handler_t http_request_parse(server *srv, connection *con, http_request_ctx *ctx);
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
#define _getString(M, FPC) (chunk_extract(srv, con, ctx->M, GETMARK(FPC)))
|
||||
#define getString(FPC) _getString(mark, FPC)
|
||||
|
||||
#define _getStringTo(M, FPC, s) (chunk_extract_to(srv, con, ctx->M, GETMARK(FPC), s))
|
||||
#define getStringTo(FPC, s) _getStringTo(mark, FPC, s)
|
||||
|
||||
|
||||
%%{
|
||||
|
||||
machine http_request_parser;
|
||||
|
@ -14,8 +18,19 @@
|
|||
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); }
|
||||
action method { getStringTo(fpc, ctx->request->http_method_str); }
|
||||
action uri { getStringTo(fpc, ctx->request->uri.uri); }
|
||||
|
||||
action header_key {
|
||||
getStringTo(fpc, ctx->h_key);
|
||||
g_string_truncate(ctx->h_value, 0);
|
||||
}
|
||||
action header_value {
|
||||
getStringTo(fpc, ctx->h_value);
|
||||
}
|
||||
action header {
|
||||
http_header_insert(ctx->request->headers, ctx->h_key, ctx->h_value);
|
||||
}
|
||||
|
||||
# RFC 2616
|
||||
OCTET = any;
|
||||
|
@ -48,7 +63,10 @@
|
|||
QDText = TEXT - DQUOTE;
|
||||
Quoted_String = DQUOTE ( QDText | Quoted_Pair )* DQUOTE;
|
||||
|
||||
HTTP_Version = "HTTP" "/" DIGIT+ "." DIGIT+;
|
||||
HTTP_Version = (
|
||||
"HTTP/1.0"
|
||||
| "HTTP/1.1"
|
||||
| "HTTP" "/" DIGIT+ "." DIGIT+ );
|
||||
#HTTP_URL = "http:" "//" Host ( ":" Port )? ( abs_path ( "?" query )? )?;
|
||||
|
||||
# RFC 2396
|
||||
|
@ -62,13 +80,37 @@
|
|||
Path_Segments = Segment ("/" Segment)*;
|
||||
Abs_Path = "/" Path_Segments;
|
||||
|
||||
Method = Token >mark %method;
|
||||
Request_URI = ("*" | ( any - CTL - SP )) >mark %uri;
|
||||
Method = (
|
||||
"GET" %{ ctx->request->http_method = HTTP_METHOD_GET; }
|
||||
| "POST" %{ ctx->request->http_method = HTTP_METHOD_POST; }
|
||||
| "HEAD" %{ ctx->request->http_method = HTTP_METHOD_HEAD; }
|
||||
| "OPTIONS" %{ ctx->request->http_method = HTTP_METHOD_OPTIONS; }
|
||||
| "PROPFIND" %{ ctx->request->http_method = HTTP_METHOD_PROPFIND; }
|
||||
| "MKCOL" %{ ctx->request->http_method = HTTP_METHOD_MKCOL; }
|
||||
| "PUT" %{ ctx->request->http_method = HTTP_METHOD_PUT; }
|
||||
| "DELETE" %{ ctx->request->http_method = HTTP_METHOD_DELETE; }
|
||||
| "COPY" %{ ctx->request->http_method = HTTP_METHOD_COPY; }
|
||||
| "MOVE" %{ ctx->request->http_method = HTTP_METHOD_MOVE; }
|
||||
| "PROPPATCH" %{ ctx->request->http_method = HTTP_METHOD_PROPPATCH; }
|
||||
| "REPORT" %{ ctx->request->http_method = HTTP_METHOD_REPORT; }
|
||||
| "CHKECOUT" %{ ctx->request->http_method = HTTP_METHOD_CHECKOUT; }
|
||||
| "CHECKIN" %{ ctx->request->http_method = HTTP_METHOD_CHECKIN; }
|
||||
| "VERSION-CONTROL" %{ ctx->request->http_method = HTTP_METHOD_VERSION_CONTROL; }
|
||||
| "UNCHECKOUT" %{ ctx->request->http_method = HTTP_METHOD_UNCHECKOUT; }
|
||||
| "MKACTIVITY" %{ ctx->request->http_method = HTTP_METHOD_MKACTIVITY; }
|
||||
| "MERGE" %{ ctx->request->http_method = HTTP_METHOD_MERGE; }
|
||||
| "LOCK" %{ ctx->request->http_method = HTTP_METHOD_LOCK; }
|
||||
| "UNLOCK" %{ ctx->request->http_method = HTTP_METHOD_UNLOCK; }
|
||||
| "LABEL" %{ ctx->request->http_method = HTTP_METHOD_LABEL; }
|
||||
| "CONNECT" %{ ctx->request->http_method = HTTP_METHOD_CONNECT; }
|
||||
| Token ) >mark >{ ctx->request->http_method = HTTP_METHOD_UNSET; } %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?;
|
||||
Field_Value = " "* (Field_Content+ ( Field_Content | LWS )*)? >mark %header_value;
|
||||
Message_Header = Token >mark %header_key ":" Field_Value? % header;
|
||||
|
||||
main := (CRLF)* Request_Line (Message_Header CRLF)* CRLF @ done;
|
||||
}%%
|
||||
|
@ -83,15 +125,32 @@ static int http_request_parser_is_finished(http_request_ctx *ctx) {
|
|||
return ctx->chunk_ctx.cs >= http_request_parser_first_final;
|
||||
}
|
||||
|
||||
void http_request_parser_init(http_request_ctx *ctx, request *req, chunkqueue *cq) {
|
||||
http_request_ctx* http_request_parser_new(request *req, chunkqueue *cq) {
|
||||
http_request_ctx *ctx = g_slice_new0(http_request_ctx);
|
||||
|
||||
%% write init;
|
||||
chunk_parser_init(&ctx->chunk_ctx, cq);
|
||||
ctx->request = req;
|
||||
ctx->h_key = g_string_sized_new(0);
|
||||
ctx->h_value = g_string_sized_new(0);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void http_request_parser_free(http_request_ctx *ctx) {
|
||||
if (!ctx) return;
|
||||
|
||||
g_string_free(ctx->h_key, TRUE);
|
||||
g_string_free(ctx->h_value, TRUE);
|
||||
|
||||
g_slice_free(http_request_ctx, ctx);
|
||||
}
|
||||
|
||||
handler_t http_request_parse(server *srv, connection *con, http_request_ctx *ctx) {
|
||||
handler_t res;
|
||||
|
||||
if (http_request_parser_is_finished(ctx)) return HANDLER_GO_ON;
|
||||
|
||||
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)) {
|
||||
|
@ -101,10 +160,13 @@ handler_t http_request_parse(server *srv, connection *con, http_request_ctx *ctx
|
|||
|
||||
%% write exec;
|
||||
|
||||
chunk_parser_done(&ctx->chunk_ctx, pe - p);
|
||||
chunk_parser_done(&ctx->chunk_ctx, p - ctx->chunk_ctx.buf);
|
||||
}
|
||||
|
||||
if (http_request_parser_has_error(ctx)) return HANDLER_ERROR;
|
||||
if (http_request_parser_is_finished(ctx)) return HANDLER_GO_ON;
|
||||
if (http_request_parser_is_finished(ctx)) {
|
||||
chunkqueue_skip(ctx->chunk_ctx.cq, ctx->chunk_ctx.bytes_in);
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,22 @@
|
|||
|
||||
request* request_new() {
|
||||
request *req = g_slice_new0(request);
|
||||
|
||||
req->http_method = HTTP_METHOD_UNSET;
|
||||
req->http_method_str = g_string_sized_new(0);
|
||||
req->http_version = HTTP_VERSION_UNSET;
|
||||
|
||||
req->uri.uri = g_string_sized_new(0);
|
||||
req->uri.orig_uri = g_string_sized_new(0);
|
||||
req->uri.scheme = g_string_sized_new(0);
|
||||
req->uri.path = g_string_sized_new(0);
|
||||
req->uri.query = g_string_sized_new(0);
|
||||
|
||||
req->headers = http_headers_new();
|
||||
|
||||
req->host = g_string_sized_new(0);
|
||||
req->content_length = -1;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,13 +134,14 @@
|
|||
#endif
|
||||
|
||||
|
||||
typedef enum { HANDLER_UNSET,
|
||||
HANDLER_GO_ON,
|
||||
HANDLER_FINISHED,
|
||||
HANDLER_COMEBACK,
|
||||
HANDLER_WAIT_FOR_EVENT,
|
||||
HANDLER_ERROR,
|
||||
HANDLER_WAIT_FOR_FD
|
||||
typedef enum {
|
||||
HANDLER_UNSET,
|
||||
HANDLER_GO_ON,
|
||||
HANDLER_FINISHED,
|
||||
HANDLER_COMEBACK,
|
||||
HANDLER_WAIT_FOR_EVENT,
|
||||
HANDLER_ERROR,
|
||||
HANDLER_WAIT_FOR_FD
|
||||
} handler_t;
|
||||
|
||||
/* Shared library support */
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef FD_SETSIZE
|
||||
/* By default this is 64 */
|
||||
#define FD_SETSIZE 4096
|
||||
#endif
|
||||
#endif /* FD_SETSIZE */
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
//#include <wspiapi.h>
|
||||
|
@ -30,7 +30,7 @@
|
|||
#define STDERR_FILENO 2
|
||||
#ifndef __MINGW32__
|
||||
#define ssize_t int
|
||||
#endif
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
#define sockread( fd, buf, bytes ) recv( fd, buf, bytes, 0 )
|
||||
|
||||
|
@ -39,7 +39,7 @@ int inet_aton(const char *cp, struct in_addr *inp);
|
|||
#define HAVE_INET_ADDR
|
||||
#undef HAVE_INET_ATON
|
||||
|
||||
#else
|
||||
#else /* _WIN32 */
|
||||
#include <sys/types.h> /* required by netinet/tcp.h on FreeBSD */
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
@ -51,7 +51,7 @@ int inet_aton(const char *cp, struct in_addr *inp);
|
|||
#ifndef SUN_LEN
|
||||
#define SUN_LEN(su) \
|
||||
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
|
||||
#endif
|
||||
#endif /* SUN_LEN */
|
||||
|
||||
#define sockread( fd, buf, bytes ) read( fd, buf, bytes )
|
||||
#define closesocket(x) close(x)
|
||||
|
@ -63,8 +63,8 @@ int inet_aton(const char *cp, struct in_addr *inp);
|
|||
/* only define it if it isn't defined yet */
|
||||
#ifndef HAVE_IPV6
|
||||
#define HAVE_IPV6
|
||||
#endif
|
||||
#endif
|
||||
#endif /* HAVE_IPV6 */
|
||||
#endif /* HAVE_INET_NTOP */
|
||||
|
||||
typedef union {
|
||||
#ifdef HAVE_IPV6
|
||||
|
|
22
src/tests.c
22
src/tests.c
|
@ -10,26 +10,32 @@
|
|||
int request_test() {
|
||||
chunkqueue *cq;
|
||||
request *req;
|
||||
http_request_ctx ctx;
|
||||
http_request_ctx *ctx;
|
||||
handler_t res;
|
||||
|
||||
cq = chunkqueue_new();
|
||||
req = request_new();
|
||||
|
||||
http_request_parser_init(&ctx, req, cq);
|
||||
ctx = http_request_parser_new(req, cq);
|
||||
|
||||
chunkqueue_append_mem(cq, CONST_STR_LEN(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: www.example.com\r\n"
|
||||
"\r\n"
|
||||
"abc"
|
||||
));
|
||||
|
||||
res = http_request_parse(NULL, NULL, &ctx);
|
||||
res = http_request_parse(NULL, NULL, ctx);
|
||||
if (res != HANDLER_GO_ON) {
|
||||
fprintf(stderr, "Parser return %i", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
assert(req->http_method == HTTP_METHOD_GET);
|
||||
assert(cq->length == 3);
|
||||
|
||||
http_request_parser_free(ctx);
|
||||
|
||||
return res == HANDLER_GO_ON ? 0 : 1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
@ -47,10 +53,10 @@ int main() {
|
|||
s = ipv6_tostring(ipv6);
|
||||
printf("parsed ipv6: %s/%u\n", s->str, network);
|
||||
|
||||
request_test();
|
||||
|
||||
return 0;
|
||||
|
||||
srv = server_new();
|
||||
|
||||
|
||||
return request_test();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue