diff --git a/src/lib/http.c b/src/lib/http.c index 4220b9a..bca8f3f 100644 --- a/src/lib/http.c +++ b/src/lib/http.c @@ -387,6 +387,14 @@ int http_send_request(sock *server, http_req *req) { return 0; } +int http_send_100_continue(sock *client) { + char buf[256]; + char date_buf[64]; + int size = sprintf(buf, "HTTP/1.1 100 Continue\r\nDate: %s\r\nServer: " SERVER_STR "\r\n\r\n", + http_get_date(date_buf, sizeof(date_buf))); + return sock_send_x(client, buf, size, 0) == -1 ? -1 : 0; +} + const http_status *http_get_status(status_code_t status_code) { for (int i = 0; i < http_statuses_size; i++) { if (http_statuses[i].code == status_code) { diff --git a/src/lib/http.h b/src/lib/http.h index 5371a37..3a822d8 100644 --- a/src/lib/http.h +++ b/src/lib/http.h @@ -174,6 +174,8 @@ int http_send_response(sock *client, http_res *res); int http_send_request(sock *server, http_req *req); +int http_send_100_continue(sock *client); + const http_status *http_get_status(status_code_t status_code); const http_status_msg *http_get_error_msg(status_code_t status_code); diff --git a/src/lib/proxy.c b/src/lib/proxy.c index 90306c5..ed03553 100644 --- a/src/lib/proxy.c +++ b/src/lib/proxy.c @@ -440,35 +440,74 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu break; } - const char *content_length = http_get_header_field(&req->hdr, "Content-Length"); - unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0; - const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding"); - - ret = 0; - if (content_len > 0) { - ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len); - } else if (strcontains(transfer_encoding, "chunked")) { - ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer), SOCK_CHUNKED); - } - - if (ret < 0 || (content_len != 0 && ret != content_len)) { - if (ret == -1 && errno != EPROTO) { - res->status = http_get_status(502); - ctx->origin = SERVER_REQ; - error("Unable to send request to server (2)"); - sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf))); - return -1; - } else if (ret == -1) { - res->status = http_get_status(400); - ctx->origin = CLIENT_REQ; - error("Unable to receive request from client"); - sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf))); + const char *client_expect = http_get_header_field(&req->hdr, "Expect"); + int expect_100_continue = (client_expect != NULL && strcasecmp(client_expect, "100-continue") == 0); + int ignore_content = 0; + if (expect_100_continue) { + http_res tmp_res = { + .version = "1.1", + .status = http_get_status(501), + }; + if (http_init_hdr(&tmp_res.hdr) != 0) { + res->status = http_get_status(500); + ctx->origin = INTERNAL; + error("Unable to initialize http header"); + return -1; + } + + ret = proxy_peek_response(proxy, &tmp_res, ctx, custom_status, err_msg); + if (ret < 0) + return (int) ret; + + if (tmp_res.status->code == 100) { + if (sock_recv_x(&proxy->proxy, buffer, ret, 0) == -1) { + res->status = http_get_status(502); + ctx->origin = SERVER_RES; + error("Unable to receive from server"); + return -1; + } + if (http_send_response(client, &tmp_res) != 0) { + res->status = http_get_status(400); + ctx->origin = CLIENT_RES; + error("Unable to send to client"); + return -1; + } + } else { + ignore_content = 1; + } + } + + if (!ignore_content) { + const char *content_length = http_get_header_field(&req->hdr, "Content-Length"); + unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0; + const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding"); + + ret = 0; + if (content_len > 0) { + ret = sock_splice(&proxy->proxy, client, buffer, sizeof(buffer), content_len); + } else if (strcontains(transfer_encoding, "chunked")) { + ret = sock_splice_chunked(&proxy->proxy, client, buffer, sizeof(buffer), SOCK_CHUNKED); + } + + if (ret < 0 || (content_len != 0 && ret != content_len)) { + if (ret == -1 && errno != EPROTO) { + res->status = http_get_status(502); + ctx->origin = SERVER_REQ; + error("Unable to send request to server (2)"); + sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf))); + return -1; + } else if (ret == -1) { + res->status = http_get_status(400); + ctx->origin = CLIENT_REQ; + error("Unable to receive request from client"); + sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf))); + return -1; + } + res->status = http_get_status(500); + ctx->origin = INTERNAL; + error("Unknown Error"); return -1; } - res->status = http_get_status(500); - ctx->origin = INTERNAL; - error("Unknown Error"); - return -1; } if (sock_set_socket_timeout(&proxy->proxy, SERVER_SOCKET_TIMEOUT_RES) != 0) { @@ -478,6 +517,48 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu return -1; } + while (1) { + ret = proxy_peek_response(proxy, res, ctx, custom_status, err_msg); + if (ret < 0) { + return (int) ret; + } else if (sock_recv_x(&proxy->proxy, buffer, ret, 0) == -1) { + res->status = http_get_status(502); + ctx->origin = SERVER_RES; + error("Unable to receive from server"); + return -1; + } + if (res->status->code == 100) { + if (http_send_response(client, res) != 0) { + res->status = http_get_status(400); + ctx->origin = CLIENT_RES; + error("Unable to send to client"); + return -1; + } + } else { + break; + } + } + + long keep_alive_timeout = http_get_keep_alive_timeout(&res->hdr); + proxy->http_timeout = (keep_alive_timeout > 0) ? keep_alive_timeout * 1000000 : 0; + + connection = http_get_header_field(&res->hdr, "Connection"); + proxy->close = !streq(res->version, "1.1") || strcontains(connection, "close") || strcontains(connection, "Close"); + + ret = proxy_response_header(req, res, conf); + if (ret != 0) { + res->status = http_get_status(500); + ctx->origin = INTERNAL; + return -1; + } + + return 0; +} + +int proxy_peek_response(proxy_ctx_t *proxy, http_res *res, http_status_ctx *ctx, http_status *custom_status, char *err_msg) { + char buffer[CHUNK_SIZE], err_buf[256]; + long ret; + ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer) - 1, MSG_PEEK); if (ret <= 0) { int e_sys = error_get_sys(), e_ssl = error_get_ssl(); @@ -569,23 +650,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu } ptr = pos0 + 2; } - if (sock_recv_x(&proxy->proxy, buffer, header_len, 0) == -1) - return -1; - - long keep_alive_timeout = http_get_keep_alive_timeout(&res->hdr); - proxy->http_timeout = (keep_alive_timeout > 0) ? keep_alive_timeout * 1000000 : 0; - - connection = http_get_header_field(&res->hdr, "Connection"); - proxy->close = !streq(res->version, "1.1") || strcontains(connection, "close") || strcontains(connection, "Close"); - - ret = proxy_response_header(req, res, conf); - if (ret != 0) { - res->status = http_get_status(500); - ctx->origin = INTERNAL; - return -1; - } - - return 0; + return header_len; } int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags) { diff --git a/src/lib/proxy.h b/src/lib/proxy.h index 1eee93f..f66b7df 100644 --- a/src/lib/proxy.h +++ b/src/lib/proxy.h @@ -43,6 +43,8 @@ int proxy_response_header(http_req *req, http_res *res, host_config_t *conf); int proxy_init(proxy_ctx_t **proxy, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg); +int proxy_peek_response(proxy_ctx_t *proxy, http_res *res, http_status_ctx *ctx, http_status *custom_status, char *err_msg); + int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int flags); int proxy_dump(proxy_ctx_t *proxy, char *buf, long len); diff --git a/src/lib/sock.c b/src/lib/sock.c index 4465c2f..cffe107 100644 --- a/src/lib/sock.c +++ b/src/lib/sock.c @@ -211,15 +211,8 @@ long sock_send(sock *s, void *buf, unsigned long len, int flags) { long ret; if (s->enc) { - while (1) { - ret = SSL_write(s->ssl, buf, (int) len); - if (ret <= 0) { - sock_error(s, (int) ret); - if (error_get_ssl() == SSL_ERROR_WANT_WRITE) - continue; - } - break; - } + ret = SSL_write(s->ssl, buf, (int) len); + if (ret <= 0) sock_error(s, (int) ret); } else if (s->pipe) { if (flags & ~MSG_MORE) { errno = EINVAL; @@ -262,15 +255,8 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags) { long ret; if (s->enc) { int (*func)(SSL *, void *, int) = (flags & MSG_PEEK) ? SSL_peek : SSL_read; - while (1) { - ret = func(s->ssl, buf, (int) len); - if (ret <= 0) { - sock_error(s, (int) ret); - if (error_get_ssl() == SSL_ERROR_WANT_READ) - continue; - } - break; - } + ret = func(s->ssl, buf, (int) len); + if (ret <= 0) sock_error(s, (int) ret); } else if (s->pipe) { if (flags & ~MSG_WAITALL) { errno = EINVAL; diff --git a/src/worker/fastcgi_handler.c b/src/worker/fastcgi_handler.c index 5776595..237ee7b 100644 --- a/src/worker/fastcgi_handler.c +++ b/src/worker/fastcgi_handler.c @@ -77,13 +77,32 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t **fcgi_cnx) { (*fcgi_cnx) = &fcgi_cnx_buf; fastcgi_handle_connection(ctx, fcgi_cnx); + int expect_100_continue = 0; + const char *client_expect = http_get_header_field(&req->hdr, "Expect"); + if (client_expect != NULL && strcasecmp(client_expect, "100-continue") == 0) { + expect_100_continue = 1; + } else if (client_expect != NULL) { + fastcgi_close_cnx((&fcgi_cnx_buf)); + res->status = http_get_status(417); + return 3; + } 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) { + if (expect_100_continue) { + http_send_100_continue(client); + } unsigned long client_content_len = strtoul(client_content_length, NULL, 10); ret = fastcgi_receive(*fcgi_cnx, client, client_content_len); } else if (strcontains(client_transfer_encoding, "chunked")) { + if (expect_100_continue) { + http_send_100_continue(client); + } ret = fastcgi_receive_chunked(*fcgi_cnx, client); + } else if (expect_100_continue) { + fastcgi_close_cnx((&fcgi_cnx_buf)); + res->status = http_get_status(417); + return 3; } else { ret = 0; } diff --git a/src/worker/local_handler.c b/src/worker/local_handler.c index c4bf55d..5313a31 100644 --- a/src/worker/local_handler.c +++ b/src/worker/local_handler.c @@ -159,6 +159,12 @@ static int local_handler(client_ctx_t *ctx) { return 1; } + const char *client_expect = http_get_header_field(&req->hdr, "Expect"); + if (client_expect != NULL) { + res->status = http_get_status(417); + return 0; + } + res->status = http_get_status(200); cache_init_uri(ctx->conf->cache, uri);