Added response parser and used it in mod_fastcgi
parent
99f14078ec
commit
587ee27cd0
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue