On Sat, May 01, 2021 at 09:26:39PM +0000, alloca wrote: > This patch adds a serve_gzip option. When enabled, If the client requests > path, then serve path.gz if it exists and the client accepts > Content-Encoding: gzip. > >
man style > diff -up httpd.orig/config.c httpd/config.c > --- httpd.orig/config.c Sat May 1 15:03:11 2021 > +++ httpd/config.c Sat May 1 15:45:43 2021 > @@ -568,12 +568,12 @@ config_getserver_config(struct httpd *env, struct serv > &parent->default_type, sizeof(struct media_type)); > } > > - f = SRVFLAG_PATH_REWRITE|SRVFLAG_NO_PATH_REWRITE; > +/* f = SRVFLAG_PATH_REWRITE|SRVFLAG_NO_PATH_REWRITE; > if ((srv_conf->flags & f) == 0) { > srv_conf->flags |= parent->flags & f; > (void)strlcpy(srv_conf->path, parent->path, > sizeof(srv_conf->path)); > - } > + } */ > > f = SRVFLAG_SERVER_HSTS; > srv_conf->flags |= parent->flags & f; > diff -up httpd.orig/httpd.conf.5 httpd/httpd.conf.5 > --- httpd.orig/httpd.conf.5 Sat May 1 15:03:11 2021 > +++ httpd/httpd.conf.5 Sat May 1 16:02:44 2021 > @@ -397,6 +397,13 @@ a browser's preload list. > Signal to the receiving user agent that this host and all sub domains > of the host's domain should be considered HSTS hosts. > .El > +.It Ic serve_gzip > +If the client requests > +.Nm path , > +then serve > +.Nm path.gz > +if it exists and the client accepts > +.Nm Content-Encoding: gzip . > .It Ic listen on Ar address Oo Ic tls Oc Ic port Ar number > Set the listen address and port. > This statement can be specified multiple times. > diff -up httpd.orig/httpd.h httpd/httpd.h > --- httpd.orig/httpd.h Sat May 1 15:03:11 2021 > +++ httpd/httpd.h Sat May 1 15:41:58 2021 > @@ -390,17 +390,17 @@ SPLAY_HEAD(client_tree, client); > #define SRVFLAG_SERVER_MATCH 0x00200000 > #define SRVFLAG_SERVER_HSTS 0x00400000 > #define SRVFLAG_DEFAULT_TYPE 0x00800000 > -#define SRVFLAG_PATH_REWRITE 0x01000000 > -#define SRVFLAG_NO_PATH_REWRITE 0x02000000 > +/* #define SRVFLAG_PATH_REWRITE 0x01000000 > +#define SRVFLAG_NO_PATH_REWRITE 0x02000000 */ > #define SRVFLAG_LOCATION_FOUND 0x40000000 > #define SRVFLAG_LOCATION_NOT_FOUND 0x80000000 > - > +#define SRVFLAG_SERVER_GZIP 0x01000000 > #define SRVFLAG_BITS \ > "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" \ > "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG" \ > "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG" \ > "\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK\25LOCATION_MATCH" \ > - "\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE\31PATH\32NO_PATH" \ > + "\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE\31SERVER_GZIP" \ > "\37LOCATION_FOUND\40LOCATION_NOT_FOUND" > > #define TCPFLAG_NODELAY 0x01 > @@ -684,7 +684,7 @@ int server_headers(struct client *, void *, > int (*)(struct client *, struct kv *, void *), void *); > int server_writeresponse_http(struct client *); > int server_response_http(struct client *, unsigned int, > - struct media_type *, off_t, time_t); > + struct media_type *, off_t, time_t, int); > void server_reset_http(struct client *); > void server_close_http(struct client *); > int server_response(struct httpd *, struct client *); > diff -up httpd.orig/parse.y httpd/parse.y > --- httpd.orig/parse.y Sat May 1 15:03:11 2021 > +++ httpd/parse.y Sat May 1 15:48:31 2021 > @@ -138,7 +138,7 @@ typedef struct { > %token COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX IP KEY LIFETIME > %token LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY OCSP ON PORT > PREFORK > %token PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP > TICKET > -%token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD REQUEST > +%token TIMEOUT TLS TYPE TYPES HSTS SERVE_GZIP MAXAGE SUBDOMAINS DEFAULT > PRELOAD REQUEST > %token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS REWRITE > %token CA CLIENT CRL OPTIONAL PARAM FORWARDED FOUND NOT > %token <v.string> STRING > @@ -644,6 +644,9 @@ serveroptsl : LISTEN ON STRING opttls port { > } > srv->srv_conf.flags |= SRVFLAG_SERVER_HSTS; > } > + | SERVE_GZIP { > + srv->srv_conf.flags |= SRVFLAG_SERVER_GZIP; > + } > ; > > optfound : /* empty */ { $$ = 0; } > @@ -925,23 +928,7 @@ requestflags_l : requestflags optcommanl requestflags_ > | requestflags optnl > ; > > -requestflags : REWRITE STRING { > - if (strlcpy(srv->srv_conf.path, $2, > - sizeof(srv->srv_conf.path)) >= > - sizeof(srv->srv_conf.path)) { > - yyerror("request path too long"); > - free($2); > - YYERROR; > - } > - free($2); > - srv->srv_conf.flags |= SRVFLAG_PATH_REWRITE; > - srv->srv_conf.flags &= ~SRVFLAG_NO_PATH_REWRITE; > - } > - | NO REWRITE { > - srv->srv_conf.flags |= SRVFLAG_NO_PATH_REWRITE; > - srv->srv_conf.flags &= ~SRVFLAG_PATH_REWRITE; > - } > - | STRIP NUMBER { > +requestflags : STRIP NUMBER { > if ($2 < 0 || $2 > INT_MAX) { > yyerror("invalid strip number"); > YYERROR; > @@ -1431,6 +1418,7 @@ lookup(char *s) > { "rewrite", REWRITE }, > { "root", ROOT }, > { "sack", SACK }, > + { "serve_gzip", SERVE_GZIP }, > { "server", SERVER }, > { "socket", SOCKET }, > { "strip", STRIP }, > diff -up httpd.orig/server_file.c httpd/server_file.c > --- httpd.orig/server_file.c Sat May 1 15:03:11 2021 > +++ httpd/server_file.c Sat May 1 15:40:14 2021 > @@ -50,7 +50,7 @@ int server_file_modified_since(struct http_descripto > int server_file_method(struct client *); > int parse_range_spec(char *, size_t, struct range *); > int parse_ranges(struct client *, char *, size_t); > - > +int accepts_gzip(struct http_descriptor *); > int > server_file_access(struct httpd *env, struct client *clt, > char *path, size_t len) > @@ -221,30 +221,48 @@ server_file_request(struct httpd *env, struct client * > struct server_config *srv_conf = clt->clt_srv_conf; > struct media_type *media; > const char *errstr = NULL; > - int fd = -1, ret, code = 500; > size_t bufsiz; > - > + char gzip_path[PATH_MAX]; > + int fd = -1, ret, code = 500, len = 0, gzip = 0; > if ((ret = server_file_method(clt)) != 0) { > code = ret; > goto abort; > } > > if ((ret = server_file_modified_since(clt->clt_descreq, st)) != -1) { > - /* send the header without a body */ > + /* send the header without a body */ > media = media_find_config(env, srv_conf, path); > if ((ret = server_response_http(clt, ret, media, -1, > - MINIMUM(time(NULL), st->st_mtim.tv_sec))) == -1) > + MINIMUM(time(NULL), st->st_mtim.tv_sec), 0)) == -1) > goto fail; > goto done; > - } > + } > > - /* Now open the file, should be readable or we have another problem */ > - if ((fd = open(path, O_RDONLY)) == -1) > - goto abort; > +/* If the client accepts gzip, try to find a .gz file */ > + if (srv_conf->flags & SRVFLAG_SERVER_GZIP && > + (gzip = accepts_gzip(clt->clt_descreq))) { > + len = snprintf(gzip_path, sizeof(gzip_path), "%s.gz", path); > + if (len > 0 && len < PATH_MAX) { > + fd = open(gzip_path, O_RDONLY); > + } > > + if (fd == -1) > + gzip = 0; > + else { > + if (fstat(fd, st) == -1) > + goto abort; > + } > + } > + > + /* Otherwise, open the file, should be readable or we have > + * another problem */ > + if (fd == -1) { > + if ((fd = open(path, O_RDONLY)) == -1) > + goto abort; > + } > media = media_find_config(env, srv_conf, path); > ret = server_response_http(clt, 200, media, st->st_size, > - MINIMUM(time(NULL), st->st_mtim.tv_sec)); > + MINIMUM(time(NULL), st->st_mtim.tv_sec), gzip); > switch (ret) { > case -1: > goto fail; > @@ -379,7 +397,7 @@ server_partial_file_request(struct httpd *env, struct > r->range_toread = TOREAD_HTTP_RANGE; > > ret = server_response_http(clt, 206, media, content_length, > - MINIMUM(time(NULL), st->st_mtim.tv_sec)); > + MINIMUM(time(NULL), st->st_mtim.tv_sec), 0); > switch (ret) { > case -1: > goto fail; > @@ -555,7 +573,7 @@ server_file_index(struct httpd *env, struct client *cl > > media = media_find_config(env, srv_conf, "index.html"); > ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb), > - dir_mtime); > + dir_mtime, 0); > switch (ret) { > case -1: > goto fail; > @@ -765,4 +783,34 @@ parse_range_spec(char *str, size_t size, struct range > return (0); > > return (1); > +} > + > +int > +accepts_gzip(struct http_descriptor *desc) { > + struct kv key, *encodings; > + char *cursor; > + char *element; > + > + key.kv_key = "Accept-Encoding"; > + encodings = kv_find(&desc->http_headers, &key); > + if (encodings == NULL) > + return (0); > + > + if (encodings->kv_value == NULL) > + return (0); > + > + cursor = encodings->kv_value; > + while ((element = strsep(&cursor, ",")) != NULL) { > + if (strstr(element, "gzip") == NULL) > + continue; > + > + /* For simplicity, assume that if a qvalue is given, we > + cannot provide gzip */ > + if (strchr(element, ';') != NULL) > + return (0); > + > + return (1); > + } > + > + return (0); > } > diff -up httpd.orig/server_http.c httpd/server_http.c > --- httpd.orig/server_http.c Sat May 1 15:03:11 2021 > +++ httpd/server_http.c Sat May 1 15:44:46 2021 > @@ -1325,18 +1325,18 @@ server_response(struct httpd *httpd, struct client *cl > return (-1); > } > > - /* Optional rewrite */ > + /* Optional rewrite * > if (srv_conf->flags & SRVFLAG_PATH_REWRITE) { > - /* Expand macros */ > + * Expand macros * > if (server_expand_http(clt, srv_conf->path, > path, sizeof(path)) == NULL) > goto fail; > > - /* > + * > * Reset and update the query. The updated query must already > * be URL encoded - either specified by the user or by using the > * original $QUERY_STRING. > - */ > + * > free(desc->http_query_alias); > desc->http_query_alias = NULL; > if ((query = strchr(path, '?')) != NULL) { > @@ -1345,7 +1345,7 @@ server_response(struct httpd *httpd, struct client *cl > goto fail; > } > > - /* Canonicalize the updated request path */ > + * Canonicalize the updated request path * > if (canonicalize_path(path, > path, sizeof(path)) == NULL) > goto fail; > @@ -1358,14 +1358,14 @@ server_response(struct httpd *httpd, struct client *cl > if ((desc->http_path_alias = strdup(path)) == NULL) > goto fail; > > - /* Now search for the updated location */ > + * Now search for the updated location * > if ((srv_conf = server_getlocation(clt, > desc->http_path_alias)) == NULL) { > server_abort_http(clt, 500, desc->http_path_alias); > return (-1); > } > } > - > +*/ > if (clt->clt_toread > 0 && (size_t)clt->clt_toread > > srv_conf->maxrequestbody) { > server_abort_http(clt, 413, NULL); > @@ -1468,7 +1468,7 @@ server_locationaccesstest(struct server_config *srv_co > > int > server_response_http(struct client *clt, unsigned int code, > - struct media_type *media, off_t size, time_t mtime) > + struct media_type *media, off_t size, time_t mtime, int compressed) > { > struct server_config *srv_conf = clt->clt_srv_conf; > struct http_descriptor *desc = clt->clt_descreq; > @@ -1516,7 +1516,15 @@ server_response_http(struct client *clt, unsigned int > if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 || > kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL) > return (-1); > +/* Prevent caches from serving incorrectly encoded content */ > + if (kv_add(&resp->http_headers, "Vary", "Accept-Encoding") == NULL) > + return (-1); > > + /* Set encoding type */ > + if (compressed) { > + if (kv_add(&resp->http_headers, "Content-Encoding", "gzip") == NULL) > + return (-1); > + } > /* HSTS header */ > if (srv_conf->flags & SRVFLAG_SERVER_HSTS && > srv_conf->flags & SRVFLAG_TLS) { >