diff --git a/Makefile b/Makefile index 9f861d4..d3c0123 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ bin/res/%.txt: res/%.* bin/sesimos: bin/server.o bin/logger.o bin/cache_handler.o bin/async.o bin/workers.o \ bin/worker/request_handler.o bin/worker/tcp_acceptor.o \ bin/worker/fastcgi_handler.o bin/worker/local_handler.o bin/worker/proxy_handler.o \ - bin/worker/ws_frame_handler.o bin/worker/chunk_handler.o \ + bin/worker/ws_frame_handler.o bin/worker/chunk_handler.o bin/worker/fastcgi_frame_handler.o \ bin/lib/http_static.o bin/res/default.o bin/res/proxy.o bin/res/style.o \ bin/res/icon_error.o bin/res/icon_info.o bin/res/icon_success.o bin/res/icon_warning.o \ bin/res/globe.o \ @@ -92,6 +92,8 @@ bin/worker/proxy_handler.o: src/worker/func.h bin/worker/ws_frame_handler.o: src/worker/func.h +bin/worker/fastcgi_frame_handler.o: src/worker/func.h + bin/worker/chunk_handler.o: src/worker/func.h bin/lib/compress.o: src/lib/compress.h diff --git a/src/lib/fastcgi.c b/src/lib/fastcgi.c index 01e03a6..31117a2 100644 --- a/src/lib/fastcgi.c +++ b/src/lib/fastcgi.c @@ -11,12 +11,14 @@ #include "utils.h" #include "../logger.h" #include "list.h" +#include "../workers.h" #include #include #include - -// TODO use pipes for stdin, stdout, stderr in FastCGI +#include +#include +#include char *fastcgi_add_param(char *buf, const char *key, const char *value) { char *ptr = buf; @@ -76,9 +78,9 @@ int fastcgi_send_data(fastcgi_cnx_t *cnx, unsigned char type, unsigned short len int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock *client, const http_req *req, const http_uri *uri) { conn->mode = mode; conn->req_id = (req_num + 1) & 0xFFFF; - conn->out_buf = NULL; - conn->out_off = 0; conn->webroot = uri->webroot; + conn->err = NULL; + sock_init(&conn->out, 0, SOCK_PIPE); conn->socket.enc = 0; if ((conn->socket.socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { @@ -183,16 +185,39 @@ int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock if (fastcgi_send_data(conn, FCGI_PARAMS, 0, NULL) == -1) return -1; + int pipes[2][2]; + if (pipe(pipes[0]) == -1 || pipe(pipes[1]) == -1) + return -1; + + conn->fd_out = pipes[1][1]; + conn->out.socket = pipes[1][0]; + + conn->fd_err = pipes[0][1]; + conn->err = fdopen(pipes[0][0], "r"); + + return 0; +} + +int fastcgi_close_cnx(fastcgi_cnx_t *cnx) { + int e = errno; + + if (cnx->err) fclose(cnx->err); + cnx->err = NULL; + if (cnx->socket.socket) sock_close(&cnx->socket); + + sock_close(&cnx->out); + close(cnx->fd_err); + close(cnx->fd_out); + + errno = e; return 0; } int fastcgi_close_stdin(fastcgi_cnx_t *conn) { - if (fastcgi_send_data(conn, FCGI_STDIN, 0, NULL) == -1) - return -1; - - return 0; + return (fastcgi_send_data(conn, FCGI_STDIN, 0, NULL) == -1) ? -1 : 0; } +// TODO show/log php stderr int fastcgi_php_error(const fastcgi_cnx_t *conn, const char *msg, int msg_len, char *err_msg) { char *msg_str = malloc(msg_len + 1); char *ptr0 = msg_str; @@ -201,7 +226,6 @@ int fastcgi_php_error(const fastcgi_cnx_t *conn, const char *msg, int msg_len, c char *ptr1 = NULL; int len, err = 0; - // FIXME *msg is part of a stream, handle fragmented lines while (1) { log_lvl_t msg_type = LOG_INFO; int msg_pre_len = 0; @@ -260,74 +284,97 @@ int fastcgi_php_error(const fastcgi_cnx_t *conn, const char *msg, int msg_len, c return err; } -int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) { +int fastcgi_recv_frame(fastcgi_cnx_t *cnx) { FCGI_Header header; - char *content; - unsigned short content_len, req_id; - long ret; - int err = 0; + unsigned short req_id, content_len; - while (1) { - if (sock_recv_x(&conn->socket, &header, sizeof(header), 0) == -1) { - res->status = http_get_status(500); - sprintf(err_msg, "Unable to communicate with FastCGI socket."); - error("Unable to receive from FastCGI socket"); - return 1; - } - req_id = ntohs(header.requestId); - content_len = ntohs(header.contentLength); - content = malloc(content_len + header.paddingLength); - if (sock_recv_x(&conn->socket, content, content_len + header.paddingLength, 0) == -1) { - res->status = http_get_status(500); - sprintf(err_msg, "Unable to communicate with FastCGI socket."); - error("Unable to receive from FastCGI socket"); - free(content); - return 1; - } + if (sock_recv_x(&cnx->socket, &header, sizeof(header), 0) == -1) + return -1; - if (req_id != conn->req_id) { - continue; - } + req_id = ntohs(header.requestId); + content_len = ntohs(header.contentLength); - if (header.type == FCGI_END_REQUEST) { - FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; - int app_status = ntohl(body->appStatus); - if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { - error("FastCGI protocol error: %i", body->protocolStatus); - } - if (app_status != 0) { - error("FastCGI app terminated with exit code %i", app_status); - } - sock_close(&conn->socket); - free(content); - return 1; - } else if (header.type == FCGI_STDERR) { - if (conn->mode == FASTCGI_BACKEND_PHP) { - err = err || fastcgi_php_error(conn, content, content_len, err_msg); - } - } else if (header.type == FCGI_STDOUT) { - break; - } else { - error("Unknown FastCGI type: %i", header.type); - } - - free(content); + if (req_id != cnx->req_id) { + warning("Invalid request id from FastCGI socket"); + char content[256 * 256]; + sock_recv_x(&cnx->socket, content, content_len + header.paddingLength, 0); + return -1; } - if (err) { + + if (header.type == FCGI_STDOUT || header.type == FCGI_STDERR) { + char buf[256]; + if (header.type == FCGI_STDOUT) { + uint64_t len = content_len; + if (write(cnx->fd_out, &len, sizeof(len)) == -1) + return -1; + } + + int fd = (header.type == FCGI_STDOUT) ? cnx->fd_out : cnx->fd_err; + for (long ret, sent = 0; sent < content_len; sent += ret) { + if ((ret = splice(cnx->socket.socket, 0, fd, 0, content_len - sent, 0)) == -1) { + if (errno == EINTR) { + errno = 0, ret = 0; + continue; + } else { + return -1; + } + } + } + + if (sock_recv_x(&cnx->socket, buf, header.paddingLength, 0) == -1) + return -1; + + return header.type; + } + + char content[256 * 256]; + if (sock_recv_x(&cnx->socket, content, content_len + header.paddingLength, 0) == -1) + return -1; + + if (header.type == FCGI_END_REQUEST) { + FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; + cnx->app_status = ntohl(body->appStatus); + if (body->protocolStatus != FCGI_REQUEST_COMPLETE) + error("FastCGI protocol error: %i", body->protocolStatus); + } else { + warning("Unknown FastCGI type: %i", header.type); + return -1; + } + + return header.type; +} + +long fastcgi_send(fastcgi_cnx_t *cnx, sock *client) { + char buf[CHUNK_SIZE]; + return sock_splice_all(client, &cnx->out, buf, sizeof(buf)); +} + +int fastcgi_header(fastcgi_cnx_t *cnx, http_res *res, char *err_msg) { + long ret, len; + char content[CLIENT_MAX_HEADER_SIZE]; + + if ((len = sock_recv_chunk_header(&cnx->out)) == -1) { res->status = http_get_status(500); - return 2; + sprintf(err_msg, "Unable to communicate with FastCGI socket."); + error("Unable to receive from FastCGI socket (1)"); + return -1; } - conn->out_buf = content; - conn->out_len = content_len; - conn->out_off = (unsigned short) (strstr(content, "\r\n\r\n") - content + 4); + if ((ret = sock_recv_x(&cnx->out, content, len, 0)) == -1) { + res->status = http_get_status(500); + sprintf(err_msg, "Unable to communicate with FastCGI socket."); + error("Unable to receive from FastCGI socket (2)"); + return -1; + } + content[ret] = 0; char *buf = content; - unsigned short header_len = conn->out_off; - if (header_len <= 0) { + char *h_pos = strstr(content, "\r\n\r\n"); + if (h_pos == NULL) { error("Unable to parse header: End of header not found"); return 1; } + long header_len = h_pos - content + 4; for (int i = 0; i < header_len; i++) { if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) { @@ -356,135 +403,12 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) { return 0; } -int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags) { - FCGI_Header header; - long ret; - char buf0[256], *content, *ptr; - int len; - unsigned short content_len; - - if (conn->out_buf != NULL && conn->out_len > conn->out_off) { - content = conn->out_buf; - ptr = content + conn->out_off; - content_len = conn->out_len - conn->out_off; - goto out; - } - - while (1) { - if ((sock_recv_x(&conn->socket, &header, sizeof(header), 0)) == -1) { - error("Unable to receive from FastCGI socket"); - return -1; - } - - content_len = ntohs(header.contentLength); - content = malloc(content_len + header.paddingLength); - ptr = content; - - long rcv_len = 0; - while (rcv_len < content_len + header.paddingLength) { - ret = sock_recv(&conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0); - if (ret < 0) { - error("Unable to receive from FastCGI socket"); - free(content); - return -1; - } - rcv_len += ret; - } - - if (header.type == FCGI_END_REQUEST) { - FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; - int app_status = ntohl(body->appStatus); - if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { - error("FastCGI protocol error: %i", body->protocolStatus); - } - if (app_status != 0) { - error("FastCGI app terminated with exit code %i", app_status); - } - sock_close(&conn->socket); - free(content); - - if (flags & FASTCGI_CHUNKED) - if (sock_send_last_chunk(client) == -1) - return -1; - - return 0; - } else if (header.type == FCGI_STDERR) { - if (conn->mode == FASTCGI_BACKEND_PHP) { - fastcgi_php_error(conn, content, content_len, buf0); - } - } else if (header.type == FCGI_STDOUT) { - out: - if (content_len != 0) { - if (flags & FASTCGI_CHUNKED) if (sock_send_chunk_header(client, content_len) == -1) return -1; - if (sock_send_x(client, ptr, content_len, 0) == -1) return -1; - if (flags & FASTCGI_CHUNKED) if (sock_send_chunk_trailer(client) == -1) return -1; - } - } else { - error("Unknown FastCGI type: %i", header.type); - } - free(content); - } +int fastcgi_dump(fastcgi_cnx_t *cnx, char *buf, long len) { + return sock_recv_x(&cnx->socket, buf, len, 0) == -1 ? -1 : 0; } -int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len) { - FCGI_Header header; - long ret; - char buf0[256]; - char *content, *ptr = buf; - unsigned short content_len; - - if (conn->out_buf != NULL && conn->out_len > conn->out_off) { - ptr += snprintf(ptr, len, "%.*s", conn->out_len - conn->out_off, conn->out_buf + conn->out_off); - } - - while (1) { - if (sock_recv_x(&conn->socket, &header, sizeof(header), 0) == -1) { - error("Unable to receive from FastCGI socket"); - return -1; - } - - content_len = ntohs(header.contentLength); - content = malloc(content_len + header.paddingLength); - - long rcv_len = 0; - while (rcv_len < content_len + header.paddingLength) { - ret = sock_recv(&conn->socket, content + rcv_len, content_len + header.paddingLength - rcv_len, 0); - if (ret < 0) { - error("Unable to receive from FastCGI socket"); - free(content); - return -1; - } - rcv_len += ret; - } - - if (header.type == FCGI_END_REQUEST) { - FCGI_EndRequestBody *body = (FCGI_EndRequestBody *) content; - int app_status = ntohl(body->appStatus); - if (body->protocolStatus != FCGI_REQUEST_COMPLETE) { - error("FastCGI protocol error: %i", body->protocolStatus); - } - if (app_status != 0) { - error("FastCGI app terminated with exit code %i", app_status); - } - sock_close(&conn->socket); - free(content); - - return 0; - } else if (header.type == FCGI_STDERR) { - if (conn->mode == FASTCGI_BACKEND_PHP) { - fastcgi_php_error(conn, content, content_len, buf0); - } - } else if (header.type == FCGI_STDOUT) { - ptr += snprintf(ptr, len - (ptr - buf), "%.*s", content_len, content); - } else { - error("Unknown FastCGI type: %i", header.type); - } - free(content); - } -} - -int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len) { - char *buf[16384]; +int fastcgi_receive(fastcgi_cnx_t *cnx, sock *client, unsigned long len) { + char buf[CHUNK_SIZE]; for (long to_send = (long) len, ret; to_send > 0; to_send -= ret) { if ((ret = sock_recv(client, buf, (to_send > sizeof(buf)) ? sizeof(buf) : to_send, 0)) <= 0) { @@ -492,14 +416,14 @@ int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len) { return -1; } - if (fastcgi_send_data(conn, FCGI_STDIN, ret, buf) == -1) + if (fastcgi_send_data(cnx, FCGI_STDIN, ret, buf) == -1) return -1; } return 0; } -int fastcgi_receive_chunked(fastcgi_cnx_t *conn, sock *client) { +int fastcgi_receive_chunked(fastcgi_cnx_t *cnx, sock *client) { for (long ret;;) { if ((ret = sock_recv_chunk_header(client)) < 0) { return (int) ret; @@ -507,7 +431,7 @@ int fastcgi_receive_chunked(fastcgi_cnx_t *conn, sock *client) { break; } - if ((ret = fastcgi_receive(conn, client, ret)) < 0) + if ((ret = fastcgi_receive(cnx, client, ret)) < 0) return (int) ret; } diff --git a/src/lib/fastcgi.h b/src/lib/fastcgi.h index a22418c..7f7c16a 100644 --- a/src/lib/fastcgi.h +++ b/src/lib/fastcgi.h @@ -13,7 +13,7 @@ #include "http.h" #include "uri.h" -#define FASTCGI_CHUNKED 1 +#define FASTCGI_TIMEOUT 3600 #define FASTCGI_BACKEND_PHP 1 @@ -23,12 +23,12 @@ typedef struct { int mode; - sock socket; - int in, out, err; + sock socket, out; + int fd_err, fd_out; + FILE *err; unsigned short req_id; + int app_status; const char *webroot; - char *out_buf; - unsigned short out_len, out_off; char *r_addr; char *r_host; } fastcgi_cnx_t; @@ -37,18 +37,24 @@ char *fastcgi_add_param(char *buf, const char *key, const char *value); int fastcgi_init(fastcgi_cnx_t *conn, int mode, unsigned int req_num, const sock *client, const http_req *req, const http_uri *uri); +int fastcgi_close_cnx(fastcgi_cnx_t *cnx); + int fastcgi_close_stdin(fastcgi_cnx_t *conn); int fastcgi_php_error(const fastcgi_cnx_t *conn, const char *msg, int msg_len, char *err_msg); -int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg); +int fastcgi_recv_frame(fastcgi_cnx_t *cnx); -int fastcgi_send(fastcgi_cnx_t *conn, sock *client, int flags); +int fastcgi_header(fastcgi_cnx_t *cnx, http_res *res, char *err_msg); -int fastcgi_dump(fastcgi_cnx_t *conn, char *buf, long len); +long fastcgi_send(fastcgi_cnx_t *cnx, sock *client); + +int fastcgi_dump(fastcgi_cnx_t *cnx, char *buf, long len); + +int fastcgi_receive(fastcgi_cnx_t *cnx, sock *client, unsigned long len); + +int fastcgi_receive_chunked(fastcgi_cnx_t *cnx, sock *client); -int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len); -int fastcgi_receive_chunked(fastcgi_cnx_t *conn, sock *client); #endif //SESIMOS_FASTCGI_H diff --git a/src/worker/fastcgi_frame_handler.c b/src/worker/fastcgi_frame_handler.c new file mode 100644 index 0000000..98eafc6 --- /dev/null +++ b/src/worker/fastcgi_frame_handler.c @@ -0,0 +1,70 @@ +/** + * sesimos - secure, simple, modern web server + * @brief FastCGI frame handler + * @file src/worker/fcti_frame_handler.c + * @author Lorenz Stechauner + * @date 2023-01-22 + */ + +#include "func.h" +#include "../lib/fastcgi.h" +#include "../logger.h" +#include "../workers.h" + +#include +#include +#include + +void fastcgi_frame_handler_func(fastcgi_ctx_t *ctx) { + logger_set_prefix("[%*s]%s", ADDRSTRLEN, ctx->client->socket.s_addr, ctx->client->log_prefix); + + switch (fastcgi_recv_frame(&ctx->cnx)) { + case FCGI_STDOUT: + case FCGI_STDERR: + fastcgi_handle_frame(ctx); + break; + case -1: + error("Unable to receive FastCGI frame"); + ctx->client->s_keep_alive = 0; + fastcgi_close(ctx); + break; + default: + // end of request received + write(ctx->cnx.fd_out, "\0\0\0\0\0\0\0\0\r\n", 10); + fastcgi_close(ctx); + } +} + +int fastcgi_handle_connection(client_ctx_t *ctx, fastcgi_cnx_t **cnx) { + sock_set_timeout(&(*cnx)->socket, FASTCGI_TIMEOUT); + sock_set_socket_timeout(&(*cnx)->socket, 1); + + fastcgi_ctx_t *a = malloc(sizeof(fastcgi_ctx_t)); + a->closed = 0; + a->client = ctx; + memcpy(&a->cnx, *cnx, sizeof(fastcgi_cnx_t)); + ctx->fcgi_cnx = a; + fastcgi_handle_frame(a); + *cnx = &a->cnx; + + return 0; +} + +void fastcgi_close(fastcgi_ctx_t *ctx) { + if (ctx->closed == 0) { + ctx->closed++; + return; + } + + logger_set_prefix("[%*s]%s", ADDRSTRLEN, ctx->client->socket.s_addr, ctx->client->log_prefix); + + if (ctx->cnx.app_status != 0) + error("FastCGI app terminated with exit code %i", ctx->cnx.app_status); + + debug("Closing FastCGI connection"); + + fastcgi_close_cnx(&ctx->cnx); + ctx->client->fcgi_cnx = NULL; + free(ctx); + errno = 0; +} diff --git a/src/worker/fastcgi_handler.c b/src/worker/fastcgi_handler.c index e83c6a7..21c7a58 100644 --- a/src/worker/fastcgi_handler.c +++ b/src/worker/fastcgi_handler.c @@ -9,40 +9,48 @@ #include "func.h" #include "../logger.h" #include "../lib/utils.h" -#include "../lib/compress.h" #include "../workers.h" #include "../lib/fastcgi.h" #include #include +#include -static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx); +static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t **fcgi_cnx); static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx); void fastcgi_handler_func(client_ctx_t *ctx) { logger_set_prefix("[%s%*s%s]%s", BLD_STR, ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix); - fastcgi_cnx_t fcgi_cnx; - int ret = fastcgi_handler_1(ctx, &fcgi_cnx); - respond(ctx); - if (ret == 0) fastcgi_handler_2(ctx, &fcgi_cnx); - request_complete(ctx); + if (!ctx->chunks_transferred) { + fastcgi_cnx_t *fcgi_cnx = NULL; + int ret = fastcgi_handler_1(ctx, &fcgi_cnx); + respond(ctx); + if (ret == 0) { + switch (fastcgi_handler_2(ctx, fcgi_cnx)) { + case 1: return; + case 2: break; + } + } + } + request_complete(ctx); handle_request(ctx); } -static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { +static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t **fcgi_cnx) { http_res *res = &ctx->res; http_req *req = &ctx->req; http_uri *uri = &ctx->uri; sock *client = &ctx->socket; char *err_msg = ctx->err_msg; - fcgi_cnx->socket.socket = 0; - fcgi_cnx->socket.enc = 0; - fcgi_cnx->req_id = 0; - fcgi_cnx->r_addr = ctx->socket.addr; - fcgi_cnx->r_host = (ctx->host[0] != 0) ? ctx->host : NULL; + fastcgi_cnx_t fcgi_cnx_buf; + (*fcgi_cnx) = &fcgi_cnx_buf; + sock_init(&(*fcgi_cnx)->socket, 0, 0); + (*fcgi_cnx)->req_id = 0; + (*fcgi_cnx)->r_addr = ctx->socket.addr; + (*fcgi_cnx)->r_host = (ctx->host[0] != 0) ? ctx->host : NULL; char buf[1024]; @@ -61,19 +69,21 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { http_add_header_field(&res->hdr, "Last-Modified", last_modified); res->status = http_get_status(200); - if (fastcgi_init(fcgi_cnx, mode, ctx->req_num, client, req, uri) != 0) { + if (fastcgi_init(*fcgi_cnx, mode, ctx->req_num, client, req, uri) != 0) { res->status = http_get_status(503); sprintf(err_msg, "Unable to communicate with FastCGI socket."); return 2; } + fastcgi_handle_connection(ctx, fcgi_cnx); + const char *client_content_length = http_get_header_field(&req->hdr, "Content-Length"); const char *client_transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding"); if (client_content_length != NULL) { unsigned long client_content_len = strtoul(client_content_length, NULL, 10); - ret = fastcgi_receive(fcgi_cnx, client, client_content_len); + ret = fastcgi_receive(*fcgi_cnx, client, client_content_len); } else if (strcontains(client_transfer_encoding, "chunked")) { - ret = fastcgi_receive_chunked(fcgi_cnx, client); + ret = fastcgi_receive_chunked(*fcgi_cnx, client); } else { ret = 0; } @@ -86,10 +96,11 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { res->status = http_get_status(502); return 2; } - fastcgi_close_stdin(fcgi_cnx); + fastcgi_close_stdin(*fcgi_cnx); - ret = fastcgi_header(fcgi_cnx, res, err_msg); + ret = fastcgi_header(*fcgi_cnx, res, err_msg); if (ret != 0) { + res->status = http_get_status(502); return (ret < 0) ? -1 : 1; } @@ -121,7 +132,7 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { ctx->content_length != -1 && ctx->content_length <= sizeof(ctx->msg_content) - 1) { - fastcgi_dump(fcgi_cnx, ctx->msg_content, sizeof(ctx->msg_content)); + fastcgi_dump(*fcgi_cnx, ctx->msg_content, sizeof(ctx->msg_content)); return 1; } @@ -135,19 +146,43 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { return 0; } +static void fastcgi_next_cb(chunk_ctx_t *ctx) { + if(ctx->client->fcgi_cnx) { + fastcgi_close(ctx->client->fcgi_cnx); + ctx->client->fcgi_cnx = NULL; + } + + fastcgi_handle(ctx->client); +} + +static void fastcgi_error_cb(chunk_ctx_t *ctx) { + if (ctx->client->chunks_transferred) + return; + + logger_set_prefix("[%s%*s%s]%s", BLD_STR, ADDRSTRLEN, ctx->client->req_host, CLR_STR, ctx->client->log_prefix); + + warning("Closing connection due to FastCGI error"); + if(ctx->client->fcgi_cnx) { + fastcgi_close(ctx->client->fcgi_cnx); + ctx->client->fcgi_cnx = NULL; + } + + tcp_close(ctx->client); + + errno = 0; +} + static int fastcgi_handler_2(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) { int chunked = strcontains(http_get_header_field(&ctx->res.hdr, "Transfer-Encoding"), "chunked"); - int flags = (chunked ? FASTCGI_CHUNKED : 0); - int ret = fastcgi_send(fcgi_cnx, &ctx->socket, flags); - if (ret < 0) { - ctx->c_keep_alive = 0; - errno = 0; + if (chunked) { + handle_chunks(ctx, &fcgi_cnx->out, SOCK_CHUNKED, fastcgi_next_cb, fastcgi_error_cb); + return 1; + } else { + fastcgi_send(fcgi_cnx, &ctx->socket); + fastcgi_close(ctx->fcgi_cnx); + ctx->fcgi_cnx = NULL; + fastcgi_handle(ctx); + return 2; } - - if (fcgi_cnx->socket.socket != 0) { - sock_close(&fcgi_cnx->socket); - } - - return ret; } diff --git a/src/worker/func.h b/src/worker/func.h index bebae25..74eefed 100644 --- a/src/worker/func.h +++ b/src/worker/func.h @@ -15,6 +15,7 @@ #include "../lib/uri.h" #include "../lib/config.h" #include "../lib/proxy.h" +#include "../lib/fastcgi.h" typedef struct { sock socket; @@ -35,6 +36,7 @@ typedef struct { long content_length; char *msg_buf, *msg_buf_ptr, msg_content[1024]; proxy_ctx_t *proxy; + void *fcgi_cnx; } client_ctx_t; typedef struct { @@ -43,6 +45,12 @@ typedef struct { void *other; } ws_ctx_t; +typedef struct { + int closed:2; + client_ctx_t *client; + fastcgi_cnx_t cnx; +} fastcgi_ctx_t; + typedef struct { client_ctx_t *client; sock *socket; @@ -59,6 +67,8 @@ void local_handler_func(client_ctx_t *ctx); void fastcgi_handler_func(client_ctx_t *ctx); +void fastcgi_frame_handler_func(fastcgi_ctx_t *ctx); + void proxy_handler_func(client_ctx_t *ctx); void ws_frame_handler_func(ws_ctx_t *ctx); @@ -79,4 +89,8 @@ void ws_close(ws_ctx_t *ctx); int handle_chunks(client_ctx_t *ctx, sock *socket, int flags, void (*next_cb)(chunk_ctx_t *), void (*err_cb)(chunk_ctx_t *)); +int fastcgi_handle_connection(client_ctx_t *ctx, fastcgi_cnx_t **cnx); + +void fastcgi_close(fastcgi_ctx_t *ctx); + #endif //SESIMOS_FUNC_H diff --git a/src/worker/request_handler.c b/src/worker/request_handler.c index 9a531c9..76b8284 100644 --- a/src/worker/request_handler.c +++ b/src/worker/request_handler.c @@ -49,6 +49,7 @@ static void init_ctx(client_ctx_t *ctx) { ctx->proxy = NULL; ctx->use_fastcgi = 0; ctx->chunks_transferred = 0; + ctx->fcgi_cnx = NULL; ctx->use_proxy = 0; ctx->ws_close = 0; ctx->proxy = NULL; diff --git a/src/workers.c b/src/workers.c index 3bb27d5..09c78ea 100644 --- a/src/workers.c +++ b/src/workers.c @@ -13,7 +13,7 @@ #include "async.h" static mpmc_t tcp_acceptor_ctx, request_handler_ctx, local_handler_ctx, fastcgi_handler_ctx, proxy_handler_ctx, - ws_frame_handler_ctx, chunk_handler_ctx; + ws_frame_handler_ctx, chunk_handler_ctx, fastcgi_frame_handler_ctx; int workers_init(void) { mpmc_init(&tcp_acceptor_ctx, 8, 64, (void (*)(void *)) tcp_acceptor_func, "tcp"); @@ -23,6 +23,7 @@ int workers_init(void) { mpmc_init(&proxy_handler_ctx, 8, 64, (void (*)(void *)) proxy_handler_func, "proxy"); mpmc_init(&ws_frame_handler_ctx, 8, 64, (void (*)(void *)) ws_frame_handler_func, "ws"); mpmc_init(&chunk_handler_ctx, 8, 64, (void (*)(void *)) chunk_handler_func, "chunk"); + mpmc_init(&fastcgi_frame_handler_ctx, 8, 64, (void (*)(void *)) fastcgi_frame_handler_func, "fcgi_f"); return -1; } @@ -34,6 +35,7 @@ void workers_stop(void) { mpmc_stop(&request_handler_ctx); mpmc_stop(&ws_frame_handler_ctx); mpmc_stop(&chunk_handler_ctx); + mpmc_stop(&fastcgi_frame_handler_ctx); } void workers_destroy(void) { @@ -44,6 +46,7 @@ void workers_destroy(void) { mpmc_destroy(&request_handler_ctx); mpmc_destroy(&ws_frame_handler_ctx); mpmc_destroy(&chunk_handler_ctx); + mpmc_destroy(&fastcgi_frame_handler_ctx); } int tcp_accept(client_ctx_t *ctx) { @@ -74,6 +77,17 @@ int fastcgi_handle(client_ctx_t *ctx) { return mpmc_queue(&fastcgi_handler_ctx, ctx); } +static int fastcgi_handle_frame_cb(fastcgi_ctx_t *ctx) { + return mpmc_queue(&fastcgi_frame_handler_ctx, ctx); +} + +int fastcgi_handle_frame(fastcgi_ctx_t *ctx) { + return async(&ctx->cnx.socket, ASYNC_WAIT_READ, 0, ctx, + (void (*)(void *)) fastcgi_handle_frame_cb, + (void (*)(void *)) fastcgi_close, + (void (*)(void *)) fastcgi_close); +} + int proxy_handle(client_ctx_t *ctx) { return mpmc_queue(&proxy_handler_ctx, ctx); } diff --git a/src/workers.h b/src/workers.h index 37ddfcc..ee87bb8 100644 --- a/src/workers.h +++ b/src/workers.h @@ -25,6 +25,8 @@ int local_handle(client_ctx_t *ctx); int fastcgi_handle(client_ctx_t *ctx); +int fastcgi_handle_frame(fastcgi_ctx_t *ctx); + int proxy_handle(client_ctx_t *ctx); int ws_handle_frame(ws_ctx_t *ctx);