From 45c5f20345611407a15ac28b03ab7891738df12f Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Wed, 5 May 2021 20:40:23 +0200 Subject: [PATCH] Preparing for transfer encoding compression --- src/client.c | 48 +++++++++++++++++++++++++++++++++------------ src/lib/http.c | 15 ++++++++++++++ src/lib/http.h | 2 ++ src/lib/rev_proxy.c | 29 ++++++++++++++++++++------- src/lib/rev_proxy.h | 7 ++++++- src/lib/utils.c | 44 ++++++++++++++++++++++------------------- 6 files changed, 104 insertions(+), 41 deletions(-) diff --git a/src/client.c b/src/client.c index eceacdb..9e762ac 100644 --- a/src/client.c +++ b/src/client.c @@ -13,6 +13,7 @@ #include "lib/fastcgi.h" #include "lib/cache.h" #include "lib/geoip.h" +#include "lib/compress.h" #include #include @@ -407,17 +408,11 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int content_length = -1; use_fastcgi = 1; - char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding"); - char *content_type = http_get_header_field(&res.hdr, "Content-Type"); - char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding"); - if (mime_is_compressible(content_type) && content_encoding == NULL && accept_encoding != NULL) { - if (strstr(accept_encoding, "br") != NULL) { - http_add_header_field(&res.hdr, "Content-Encoding", "br"); - use_fastcgi |= FASTCGI_COMPRESS_BR; - } else if (strstr(accept_encoding, "gzip") != NULL) { - http_add_header_field(&res.hdr, "Content-Encoding", "gzip"); - use_fastcgi |= FASTCGI_COMPRESS_GZ; - } + int http_comp = http_get_compression(&req, &res); + if (http_comp & COMPRESS_BR) { + use_fastcgi |= FASTCGI_COMPRESS_BR; + } else if (http_comp & COMPRESS_GZ) { + use_fastcgi |= FASTCGI_COMPRESS_GZ; } if (http_get_header_field(&res.hdr, "Content-Length") == NULL) { @@ -428,8 +423,31 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int print("Reverse proxy for " BLD_STR "%s:%i" CLR_STR, conf->rev_proxy.hostname, conf->rev_proxy.port); http_remove_header_field(&res.hdr, "Date", HTTP_REMOVE_ALL); http_remove_header_field(&res.hdr, "Server", HTTP_REMOVE_ALL); + ret = rev_proxy_init(&req, &res, conf, client, &custom_status, err_msg); - use_rev_proxy = ret == 0; + use_rev_proxy = (ret == 0); + + char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding"); + if (use_rev_proxy && transfer_encoding == NULL) { + int http_comp = http_get_compression(&req, &res); + if (http_comp & COMPRESS_BR) { + //use_rev_proxy |= REV_PROXY_COMPRESS_BR; + } else if (http_comp & COMPRESS_GZ) { + //use_rev_proxy |= REV_PROXY_COMPRESS_GZ; + } + } + + int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0; + http_remove_header_field(&res.hdr, "Transfer-Encoding", HTTP_REMOVE_ALL); + printf("%i\n", use_rev_proxy); + ret = sprintf(buf0, "%s%s%s", + (use_rev_proxy & REV_PROXY_COMPRESS_BR) ? "br" : + ((use_rev_proxy & REV_PROXY_COMPRESS_GZ) ? "gzip" : ""), + ((use_rev_proxy & REV_PROXY_COMPRESS) && chunked) ? ", " : "", + chunked ? "chunked" : ""); + if (ret > 0) { + http_add_header_field(&res.hdr, "Transfer-Encoding", buf0); + } } else { print(ERR_STR "Unknown host type: %i" CLR_STR, conf->type); res.status = http_get_status(501); @@ -515,17 +533,21 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int } else if (use_fastcgi) { char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding"); int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0; + int flags = (chunked ? FASTCGI_CHUNKED : 0) | (use_fastcgi & FASTCGI_COMPRESS); fastcgi_send(&php_fpm, client, flags); } else if (use_rev_proxy) { char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding"); int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0; + char *content_len = http_get_header_field(&res.hdr, "Content-Length"); unsigned long len_to_send = 0; if (content_len != NULL) { len_to_send = strtol(content_len, NULL, 10); } - rev_proxy_send(client, chunked, len_to_send); + + int flags = (chunked ? REV_PROXY_CHUNKED : 0) | (use_rev_proxy & REV_PROXY_COMPRESS); + rev_proxy_send(client, len_to_send, flags); } } diff --git a/src/lib/http.c b/src/lib/http.c index 5e87003..2c24dc2 100644 --- a/src/lib/http.c +++ b/src/lib/http.c @@ -7,6 +7,7 @@ #include "http.h" #include "utils.h" +#include "compress.h" #include "../necronda-server.h" #include @@ -312,3 +313,17 @@ const http_doc_info *http_get_status_info(const http_status *status) { } return NULL; } + +int http_get_compression(const http_req *req, const http_res *res) { + char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding"); + char *content_type = http_get_header_field(&res->hdr, "Content-Type"); + char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); + if (mime_is_compressible(content_type) && content_encoding == NULL && accept_encoding != NULL) { + if (strstr(accept_encoding, "br") != NULL) { + return COMPRESS_BR; + } else if (strstr(accept_encoding, "gzip") != NULL) { + return COMPRESS_GZ; + } + } + return 0; +} diff --git a/src/lib/http.h b/src/lib/http.h index 7179e34..dac4d31 100644 --- a/src/lib/http.h +++ b/src/lib/http.h @@ -122,4 +122,6 @@ char *http_get_date(char *buf, size_t size); const http_doc_info *http_get_status_info(const http_status *status); +int http_get_compression(const http_req *req, const http_res *res); + #endif //NECRONDA_SERVER_HTTP_H diff --git a/src/lib/rev_proxy.c b/src/lib/rev_proxy.c index 31b61fe..3187eb5 100644 --- a/src/lib/rev_proxy.c +++ b/src/lib/rev_proxy.c @@ -7,6 +7,7 @@ #include "rev_proxy.h" #include "utils.h" +#include "compress.h" #include "../necronda-server.h" #include #include @@ -377,14 +378,29 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client return -1; } -int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send) { +int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) { + // TODO handle websockets long ret; char buffer[CHUNK_SIZE]; long len, snd_len; - // TODO handle websockets - // TODO compress -> Transfer-Encoding: br/gzip + + compress_ctx comp_ctx; + if (flags & REV_PROXY_COMPRESS_BR) { + flags &= ~REV_PROXY_COMPRESS_GZ; + if (compress_init(&comp_ctx, COMPRESS_BR) != 0) { + print(ERR_STR "Unable to init brotli: %s" CLR_STR, strerror(errno)); + flags &= ~REV_PROXY_COMPRESS_BR; + } + } else if (flags & REV_PROXY_COMPRESS_GZ) { + flags &= ~REV_PROXY_COMPRESS_BR; + if (compress_init(&comp_ctx, COMPRESS_BR) != 0) { + print(ERR_STR "Unable to init gzip: %s" CLR_STR, strerror(errno)); + flags &= ~REV_PROXY_COMPRESS_GZ; + } + } + do { - if (chunked) { + if (flags & REV_PROXY_CHUNKED) { ret = sock_recv(&rev_proxy, buffer, 16, MSG_PEEK); if (ret <= 0) { print("Unable to receive: %s", sock_strerror(&rev_proxy)); @@ -395,7 +411,6 @@ int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send) { char *pos = strstr(buffer, "\r\n"); len = pos - buffer + 2; ret = sock_send(client, buffer, len, 0); - sock_recv(&rev_proxy, buffer, len, 0); if (ret <= 0) break; } @@ -410,7 +425,7 @@ int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send) { snd_len += ret; } if (ret <= 0) break; - if (chunked) { + if (flags & REV_PROXY_CHUNKED) { sock_recv(&rev_proxy, buffer, 2, 0); ret = sock_send(client, "\r\n", 2, 0); if (ret <= 0) { @@ -418,6 +433,6 @@ int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send) { break; } } - } while (chunked && len_to_send > 0); + } while ((flags & REV_PROXY_CHUNKED) && len_to_send > 0); return 0; } diff --git a/src/lib/rev_proxy.h b/src/lib/rev_proxy.h index c77f1bc..11c1684 100644 --- a/src/lib/rev_proxy.h +++ b/src/lib/rev_proxy.h @@ -8,6 +8,11 @@ #ifndef NECRONDA_SERVER_REV_PROXY_H #define NECRONDA_SERVER_REV_PROXY_H +#define REV_PROXY_CHUNKED 1 +#define REV_PROXY_COMPRESS_GZ 2 +#define REV_PROXY_COMPRESS_BR 4 +#define REV_PROXY_COMPRESS 6 + #include "http.h" #include "config.h" @@ -22,6 +27,6 @@ int rev_proxy_response_header(http_req *req, http_res *res); int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client, http_status *custom_status, char *err_msg); -int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send); +int rev_proxy_send(sock *client, unsigned long len_to_send, int flags); #endif //NECRONDA_SERVER_REV_PROXY_H diff --git a/src/lib/utils.c b/src/lib/utils.c index 7d1b277..50a4bb3 100644 --- a/src/lib/utils.c +++ b/src/lib/utils.c @@ -104,25 +104,29 @@ int url_decode(const char *str, char *dec, long *size) { } int mime_is_compressible(const char *type) { + char type_parsed[64]; + strncpy(type_parsed, type, sizeof(type_parsed)); + char *pos = strchr(type_parsed, ';'); + if (pos != NULL) pos[0] = 0; return - strncmp(type, "text/", 5) == 0 || - strncmp(type, "message/", 7) == 0 || - strstr(type, "+xml") != NULL || - strstr(type, "+json") != NULL || - strcmp(type, "application/javascript") == 0 || - strcmp(type, "application/json") == 0 || - strcmp(type, "application/xml") == 0 || - strcmp(type, "application/x-www-form-urlencoded") == 0 || - strcmp(type, "application/x-tex") == 0 || - strcmp(type, "application/x-httpd-php") == 0 || - strcmp(type, "application/x-latex") == 0 || - strcmp(type, "application/vnd.ms-fontobject") == 0 || - strcmp(type, "application/x-font-ttf") == 0 || - strcmp(type, "application/x-javascript") == 0 || - strcmp(type, "application/x-web-app-manifest+json") == 0 || - strcmp(type, "font/eot") == 0 || - strcmp(type, "font/opentype") == 0 || - strcmp(type, "image/bmp") == 0 || - strcmp(type, "image/vnd.microsoft.icon") == 0 || - strcmp(type, "image/x-icon") == 0; + strncmp(type_parsed, "text/", 5) == 0 || + strncmp(type_parsed, "message/", 7) == 0 || + strstr(type_parsed, "+xml") != NULL || + strstr(type_parsed, "+json") != NULL || + strcmp(type_parsed, "application/javascript") == 0 || + strcmp(type_parsed, "application/json") == 0 || + strcmp(type_parsed, "application/xml") == 0 || + strcmp(type_parsed, "application/x-www-form-urlencoded") == 0 || + strcmp(type_parsed, "application/x-tex") == 0 || + strcmp(type_parsed, "application/x-httpd-php") == 0 || + strcmp(type_parsed, "application/x-latex") == 0 || + strcmp(type_parsed, "application/vnd.ms-fontobject") == 0 || + strcmp(type_parsed, "application/x-font-ttf") == 0 || + strcmp(type_parsed, "application/x-javascript") == 0 || + strcmp(type_parsed, "application/x-web-app-manifest+json") == 0 || + strcmp(type_parsed, "font/eot") == 0 || + strcmp(type_parsed, "font/opentype") == 0 || + strcmp(type_parsed, "image/bmp") == 0 || + strcmp(type_parsed, "image/vnd.microsoft.icon") == 0 || + strcmp(type_parsed, "image/x-icon") == 0; }