2
0
Fork 0

Added response parser and used it in mod_fastcgi

personal/stbuehler/wip
Stefan Bühler 2009-01-10 13:30:58 +01:00
parent 99f14078ec
commit 587ee27cd0
8 changed files with 201 additions and 6 deletions

View File

@ -38,6 +38,7 @@
#include <lighttpd/plugin.h>
#include <lighttpd/http_headers.h>
#include <lighttpd/http_request_parser.h>
#include <lighttpd/http_response_parser.h>
#include <lighttpd/request.h>
#include <lighttpd/response.h>
#include <lighttpd/environment.h>

View File

@ -0,0 +1,28 @@
#ifndef _LIGHTTPD_HTTP_RESPONSE_PARSER_H_
#define _LIGHTTPD_HTTP_RESPONSE_PARSER_H_
#ifndef _LIGHTTPD_BASE_H_
#error Please include <lighttpd/base.h> instead of this file
#endif
struct http_response_ctx;
typedef struct http_response_ctx http_response_ctx;
struct http_response_ctx {
chunk_parser_ctx chunk_ctx;
struct response *response;
gboolean accept_cgi, accept_nph;
chunk_parser_mark mark;
GString *h_key, *h_value;
};
LI_API void http_response_parser_init(http_response_ctx* ctx, response *req, chunkqueue *cq, gboolean accept_cgi, gboolean accept_nph);
LI_API void http_response_parser_reset(http_response_ctx* ctx);
LI_API void http_response_parser_clear(http_response_ctx *ctx);
LI_API handler_t http_response_parse(vrequest *vr, http_response_ctx *ctx);
#endif

View File

@ -62,4 +62,6 @@ LI_API void sockaddr_clear(sockaddr *saddr);
LI_API void gstring_replace_char_with_str_len(GString *gstr, gchar c, gchar *str, guint len);
LI_API gboolean l_g_strncase_equal(GString *str, const gchar *s, guint len);
#endif

View File

@ -288,6 +288,7 @@ SET(COMMON_SRC
filter_chunked.c
http_headers.c
http_request_parser.c
http_response_parser.c
log.c
module.c
network.c
@ -324,6 +325,7 @@ ENDIF(WITH_LUA)
RAGEL_PARSER(condition_parsers.rl)
RAGEL_PARSER(config_parser.rl -T0)
RAGEL_PARSER(http_request_parser.rl)
RAGEL_PARSER(http_response_parser.rl)
RAGEL_PARSER(url_parser.rl)
SET(L_INSTALL_TARGETS)

147
src/http_response_parser.rl Normal file
View File

@ -0,0 +1,147 @@
#include <lighttpd/base.h>
#include <lighttpd/http_response_parser.h>
/** Machine **/
#define _getString(M, FPC) (chunk_extract(vr, ctx->M, GETMARK(FPC)))
#define getString(FPC) _getString(mark, FPC)
#define _getStringTo(M, FPC, s) (chunk_extract_to(vr, ctx->M, GETMARK(FPC), s))
#define getStringTo(FPC, s) _getStringTo(mark, FPC, s)
%%{
machine http_response_parser;
variable cs ctx->chunk_ctx.cs;
action mark { ctx->mark = GETMARK(fpc); }
action done { fbreak; }
action status {
getStringTo(fpc, ctx->h_value);
ctx->response->http_status = atoi(ctx->h_value->str);
}
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 {
if (ctx->accept_cgi && l_g_strncase_equal(ctx->h_key, CONST_STR_LEN("Status"))) {
ctx->response->http_status = atoi(ctx->h_value->str);
} else {
http_header_insert(ctx->response->headers, GSTR_LEN(ctx->h_key), GSTR_LEN(ctx->h_value));
}
}
# 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/1.0" %{ ctx->response->http_version = HTTP_VERSION_1_0; }
| "HTTP/1.1" %{ ctx->response->http_version = HTTP_VERSION_1_1; }
| "HTTP" "/" DIGIT+ "." DIGIT+ ) >{ ctx->response->http_version = HTTP_VERSION_UNSET; };
#HTTP_URL = "http:" "//" Host ( ":" Port )? ( abs_path ( "?" query )? )?;
Status = (digit digit digit) >mark %status;
Response_Line = "HTTP/" digit+ "." digit+ SP Status SP (any - CTL - CR - LF)* CRLF;
Field_Content = ( TEXT+ | ( Token | Separators | Quoted_String )+ );
Field_Value = " "* (Field_Content+ ( Field_Content | LWS )*)? >mark %header_value;
Message_Header = Token >mark %header_key ":" Field_Value? % header;
main := Response_Line? (Message_Header CRLF)* CRLF @ done;
}%%
%% write data;
static int http_response_parser_has_error(http_response_ctx *ctx) {
return ctx->chunk_ctx.cs == http_response_parser_error;
}
static int http_response_parser_is_finished(http_response_ctx *ctx) {
return ctx->chunk_ctx.cs >= http_response_parser_first_final;
}
void http_response_parser_init(http_response_ctx* ctx, response *req, chunkqueue *cq, gboolean accept_cgi, gboolean accept_nph) {
chunk_parser_init(&ctx->chunk_ctx, cq);
ctx->response = req;
ctx->accept_cgi = accept_cgi;
ctx->accept_nph = accept_nph;
ctx->h_key = g_string_sized_new(0);
ctx->h_value = g_string_sized_new(0);
%% write init;
}
void http_response_parser_reset(http_response_ctx* ctx) {
chunk_parser_reset(&ctx->chunk_ctx);
g_string_truncate(ctx->h_key, 0);
g_string_truncate(ctx->h_value, 0);
%% write init;
}
void http_response_parser_clear(http_response_ctx *ctx) {
g_string_free(ctx->h_key, TRUE);
g_string_free(ctx->h_value, TRUE);
}
handler_t http_response_parse(vrequest *vr, http_response_ctx *ctx) {
handler_t res;
if (http_response_parser_is_finished(ctx)) return HANDLER_GO_ON;
if (HANDLER_GO_ON != (res = chunk_parser_prepare(&ctx->chunk_ctx))) return res;
while (!http_response_parser_has_error(ctx) && !http_response_parser_is_finished(ctx)) {
char *p, *pe;
if (HANDLER_GO_ON != (res = chunk_parser_next(vr, &ctx->chunk_ctx, &p, &pe))) return res;
%% write exec;
chunk_parser_done(&ctx->chunk_ctx, p - ctx->chunk_ctx.buf);
}
if (http_response_parser_has_error(ctx)) return HANDLER_ERROR;
if (http_response_parser_is_finished(ctx)) {
chunkqueue_skip(ctx->chunk_ctx.cq, ctx->chunk_ctx.bytes_in);
if (ctx->response->http_status == 0) ctx->response->http_status = 200;
return HANDLER_GO_ON;
}
return HANDLER_ERROR;
}

View File

@ -43,6 +43,9 @@ struct fastcgi_connection {
GString *buf_in_record;
FCGI_Record fcgi_in_record;
guint16 requestid;
http_response_ctx parse_response_ctx;
gboolean response_headers_finished;
};
struct fastcgi_context {
@ -139,6 +142,8 @@ static fastcgi_connection* fastcgi_connection_new(vrequest *vr, fastcgi_context
fcon->buf_in_record = g_string_sized_new(FCGI_HEADER_LEN);
fcon->requestid = 1;
fcon->state = FS_WAIT_FOR_REQUEST;
http_response_parser_init(&fcon->parse_response_ctx, &vr->response, fcon->stdout, TRUE, FALSE);
fcon->response_headers_finished = FALSE;
return fcon;
}
@ -156,6 +161,8 @@ static void fastcgi_connection_free(fastcgi_connection *fcon) {
chunkqueue_free(fcon->stdout);
g_string_free(fcon->buf_in_record, TRUE);
http_response_parser_clear(&fcon->parse_response_ctx);
g_slice_free(fastcgi_connection, fcon);
}
@ -496,14 +503,16 @@ static void fastcgi_fd_cb(struct ev_loop *loop, ev_io *w, int revents) {
if (!fastcgi_parse_response(fcon)) return;
/* TODO: parse stdout response */
if (fcon->vr->out->bytes_in == 0 && fcon->stdout->length > 0) {
fcon->vr->response.http_status = 200;
if (!fcon->response_headers_finished && HANDLER_GO_ON == http_response_parse(fcon->vr, &fcon->parse_response_ctx)) {
fcon->response_headers_finished = TRUE;
vrequest_handle_response_headers(fcon->vr);
}
chunkqueue_steal_all(fcon->vr->out, fcon->stdout);
fcon->vr->out->is_closed = fcon->stdout->is_closed;
vrequest_handle_response_body(fcon->vr);
if (fcon->response_headers_finished) {
chunkqueue_steal_all(fcon->vr->out, fcon->stdout);
fcon->vr->out->is_closed = fcon->stdout->is_closed;
vrequest_handle_response_body(fcon->vr);
}
if (fcon->fcgi_in->is_closed && !fcon->vr->out->is_closed) {
VR_ERROR(fcon->vr, "%s", "unexpected end-of-file (perhaps the fastcgi process died)");

View File

@ -603,3 +603,8 @@ void gstring_replace_char_with_str_len(GString *gstr, gchar c, gchar *str, guint
}
}
}
gboolean l_g_strncase_equal(GString *str, const gchar *s, guint len) {
if (str->len != len) return FALSE;
return 0 == g_ascii_strncasecmp(str->str, s, len);
}

View File

@ -30,6 +30,7 @@ common_src = '''
filter_chunked.c
http_headers.c
http_request_parser.rl
http_response_parser.rl
log.c
module.c
network.c