diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index 93d7d9fc..0becc6fa 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -284,7 +284,6 @@ static handler_t fcgi_create_env(server *srv, handler_ctx *hctx) { } typedef struct { - buffer *b; unsigned int len; int type; int padding; @@ -292,82 +291,61 @@ typedef struct { } fastcgi_response_packet; static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_packet *packet) { - chunk *c; - size_t offset; - size_t toread; - FCGI_Header *header; - - if (!hctx->rb->first) return -1; - - packet->b = buffer_init(); - packet->len = 0; - packet->type = 0; - packet->padding = 0; - packet->request_id = 0; + FCGI_Header header; + size_t toread = sizeof(FCGI_Header), flen = 0; + off_t rblen = chunkqueue_length(hctx->rb); + if (rblen < (off_t)sizeof(FCGI_Header)) { + /* no header */ + if (hctx->conf.debug && 0 != rblen) { + log_error_write(srv, __FILE__, __LINE__, "sosds", "FastCGI: header too small:", rblen, "bytes <", sizeof(FCGI_Header), "bytes, waiting for more data"); + } + return -1; + } - offset = 0; toread = 8; /* get at least the FastCGI header */ - for (c = hctx->rb->first; c; c = c->next) { + for (chunk *c = hctx->rb->first; c; c = c->next) { size_t weHave = buffer_string_length(c->mem) - c->offset; + if (weHave >= toread) { + memcpy((char *)&header + flen, c->mem->ptr + c->offset, toread); + break; + } - if (weHave > toread) weHave = toread; - - buffer_append_string_len(packet->b, c->mem->ptr + c->offset, weHave); + memcpy((char *)&header + flen, c->mem->ptr + c->offset, weHave); + flen += weHave; toread -= weHave; - offset = weHave; /* skip offset bytes in chunk for "real" data */ - - if (0 == toread) break; } - if (buffer_string_length(packet->b) < sizeof(FCGI_Header)) { - /* no header */ - if (hctx->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sdsds", "FastCGI: header too small:", buffer_string_length(packet->b), "bytes <", sizeof(FCGI_Header), "bytes, waiting for more data"); - } - - buffer_free(packet->b); + /* we have at least a header, now check how much we have to fetch */ + packet->len = (header.contentLengthB0 | (header.contentLengthB1 << 8)) + header.paddingLength; + packet->request_id = (header.requestIdB0 | (header.requestIdB1 << 8)); + packet->type = header.type; + packet->padding = header.paddingLength; - return -1; + if (packet->len > (unsigned int)rblen-sizeof(FCGI_Header)) { + return -1; /* we didn't get the full packet */ } - /* we have at least a header, now check how much me have to fetch */ - header = (FCGI_Header *)(packet->b->ptr); - - packet->len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength; - packet->request_id = (header->requestIdB0 | (header->requestIdB1 << 8)); - packet->type = header->type; - packet->padding = header->paddingLength; - - /* ->b should only be the content */ - buffer_string_set_length(packet->b, 0); - - if (packet->len) { - /* copy the content */ - for (; c && (buffer_string_length(packet->b) < packet->len); c = c->next) { - size_t weWant = packet->len - buffer_string_length(packet->b); - size_t weHave = buffer_string_length(c->mem) - c->offset - offset; - - if (weHave > weWant) weHave = weWant; - - buffer_append_string_len(packet->b, c->mem->ptr + c->offset + offset, weHave); - - /* we only skipped the first bytes as they belonged to the fcgi header */ - offset = 0; - } - - if (buffer_string_length(packet->b) < packet->len) { - /* we didn't get the full packet */ + chunkqueue_mark_written(hctx->rb, sizeof(FCGI_Header)); + return 0; +} - buffer_free(packet->b); - return -1; +static void fastcgi_get_packet_body(buffer *b, handler_ctx *hctx, fastcgi_response_packet *packet) { + /* copy content; hctx->rb must contain at least packet->len content */ + size_t toread = packet->len; + buffer_string_prepare_append(b, packet->len); + for (chunk *c = hctx->rb->first; c; c = c->next) { + size_t weHave = buffer_string_length(c->mem) - c->offset; + if (weHave >= toread) { + buffer_append_string_len(b, c->mem->ptr + c->offset, toread); + break; } - buffer_string_set_length(packet->b, buffer_string_length(packet->b) - packet->padding); + buffer_append_string_len(b, c->mem->ptr + c->offset, weHave); + toread -= weHave; } - chunkqueue_mark_written(hctx->rb, packet->len + sizeof(FCGI_Header)); - - return 0; + buffer_string_set_length(b, buffer_string_length(b) - packet->padding); + chunkqueue_mark_written(hctx->rb, packet->len); } static handler_t fcgi_recv_parse(server *srv, connection *con, struct http_response_opts_t *opts, buffer *b, size_t n) { @@ -409,19 +387,20 @@ static handler_t fcgi_recv_parse(server *srv, connection *con, struct http_respo /* is the header already finished */ if (0 == con->file_started) { /* split header from body */ - buffer *hdrs = (!hctx->response) - ? packet.b - : (buffer_append_string_buffer(hctx->response, packet.b), hctx->response); - handler_t rc = http_response_parse_headers(srv, con, &hctx->opts, hdrs); - if (rc != HANDLER_GO_ON) { + buffer *hdrs = hctx->response; + if (NULL == hdrs) { + hdrs = srv->tmp_buf; + buffer_string_set_length(srv->tmp_buf, 0); + } + fastcgi_get_packet_body(hdrs, hctx, &packet); + if (HANDLER_GO_ON != http_response_parse_headers(srv, con, &hctx->opts, hdrs)) { hctx->send_content_body = 0; fin = 1; break; } if (0 == con->file_started) { if (!hctx->response) { - hctx->response = packet.b; - packet.b = NULL; + hctx->response = buffer_init_buffer(hdrs); } } else if (hctx->gw_mode == GW_AUTHORIZER && @@ -429,19 +408,24 @@ static handler_t fcgi_recv_parse(server *srv, connection *con, struct http_respo /* authorizer approved request; ignore the content here */ hctx->send_content_body = 0; } - } else if (hctx->send_content_body && !buffer_string_is_empty(packet.b)) { - if (0 != http_chunk_append_buffer(srv, con, packet.b)) { + } else if (hctx->send_content_body) { + buffer_string_set_length(srv->tmp_buf, 0); + fastcgi_get_packet_body(srv->tmp_buf, hctx, &packet); + if (0 != http_chunk_append_buffer(srv, con, srv->tmp_buf)) { /* error writing to tempfile; * truncate response or send 500 if nothing sent yet */ fin = 1; - break; } + } else { + chunkqueue_mark_written(hctx->rb, packet.len); } break; case FCGI_STDERR: if (packet.len == 0) break; - log_error_write_multiline_buffer(srv, __FILE__, __LINE__, packet.b, "s", + buffer_string_set_length(srv->tmp_buf, 0); + fastcgi_get_packet_body(srv->tmp_buf, hctx, &packet); + log_error_write_multiline_buffer(srv, __FILE__, __LINE__, srv->tmp_buf, "s", "FastCGI-stderr:"); break; @@ -452,9 +436,9 @@ static handler_t fcgi_recv_parse(server *srv, connection *con, struct http_respo default: log_error_write(srv, __FILE__, __LINE__, "sd", "FastCGI: header.type not handled: ", packet.type); + chunkqueue_mark_written(hctx->rb, packet.len); break; } - buffer_free(packet.b); } return 0 == fin ? HANDLER_GO_ON : HANDLER_FINISHED;