[PATCH 4/4] httpd: Support caching via ETag header
Denys Vlasenko
vda.linux at googlemail.com
Sun Aug 16 13:40:20 UTC 2020
Applied, thanks
On Sun, Aug 16, 2020 at 12:49 AM Sergey Ponomarev <stokito at gmail.com> wrote:
>
> Awesome, thanks!
>
> I reviewed and tested and all looks fine.
> I don't want to bother you but I see two improvings:
>
> 1. While ETag minimizes load on each load will be sent a new request. To avoid such requests at all for assets we can add a header Cache-Control: public,max-age=31536000,immutable
> But users may want some other values so it would be great to have a general ability to add a custom header.
> This can be implemented just as a regular httpd.conf file in a folder with the value of a custom header.
> The same can be used for Content Security Policy (CSP) headers and many other things.
> Some users are looking for the functionality https://serverfault.com/questions/918602/how-to-set-header-with-busybox-httpd
> Will you accept a patch for custom headers?
>
> 2. BB httpd has a built-in IP ACL i.e. allow/deny by IP. This work can be done by a firewall or iptables. We can also make it configurable:
>
> Subject: [PATCH] httpd: Make Deny/Allow by IP config support optional
>
> Signed-off-by: Sergey Ponomarev <stokito at gmail.com>
> ---
> networking/httpd.c | 27 ++++++++++++++++++++++++++-
> 1 file changed, 26 insertions(+), 1 deletion(-)
>
> diff --git a/networking/httpd.c b/networking/httpd.c
> index 0297cf9ec..7ddf6caaa 100644
> --- a/networking/httpd.c
> +++ b/networking/httpd.c
> @@ -245,6 +245,13 @@
> //config: help
> //config: RFC2616 says that server MUST add Date header to response.
> //config: But it is almost useless and can be omitted.
> +//config:
> +//config:config FEATURE_HTTPD_ACL
> +//config: bool "ACL IP"
> +//config: default y
> +//config: depends on HTTPD
> +//config: help
> +//config: Deny/Allow by IP config support
>
> //applet:IF_HTTPD(APPLET(httpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
>
> @@ -314,6 +321,7 @@ typedef struct Htaccess {
> char before_colon[1]; /* really bigger, must be last */
> } Htaccess;
>
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> /* Must have "next" as a first member */
> typedef struct Htaccess_IP {
> struct Htaccess_IP *next;
> @@ -321,6 +329,7 @@ typedef struct Htaccess_IP {
> unsigned mask;
> int allow_deny;
> } Htaccess_IP;
> +#endif
>
> /* Must have "next" as a first member */
> typedef struct Htaccess_Proxy {
> @@ -449,7 +458,9 @@ struct globals {
>
> const char *found_mime_type;
> const char *found_moved_temporarily;
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> Htaccess_IP *ip_a_d; /* config allow/deny lines */
> +#endif
>
> IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
> IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
> @@ -499,7 +510,9 @@ struct globals {
> #define found_mime_type (G.found_mime_type )
> #define found_moved_temporarily (G.found_moved_temporarily)
> #define last_mod (G.last_mod )
> -#define ip_a_d (G.ip_a_d )
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> +# define ip_a_d (G.ip_a_d )
> +#endif
> #define g_realm (G.g_realm )
> #define remoteuser (G.remoteuser )
> #define file_size (G.file_size )
> @@ -560,6 +573,7 @@ static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
> free_llist((has_next_ptr**)pptr);
> }
>
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
> {
> free_llist((has_next_ptr**)pptr);
> @@ -649,6 +663,7 @@ static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
> *maskp = (uint32_t)(~mask);
> return 0;
> }
> +#endif
>
> /*
> * Parse configuration file into in-memory linked list.
> @@ -677,8 +692,10 @@ static void parse_conf(const char *path, int flag)
> const char *filename;
> char buf[160];
>
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> /* discard old rules */
> free_Htaccess_IP_list(&ip_a_d);
> +#endif
> flg_deny_all = 0;
> /* retain previous auth and mime config only for subdir parse */
> if (flag != SUBDIR_PARSE) {
> @@ -783,6 +800,7 @@ static void parse_conf(const char *path, int flag)
> continue;
> }
>
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> if (ch == 'A' || ch == 'D') {
> Htaccess_IP *pip;
>
> @@ -819,6 +837,7 @@ static void parse_conf(const char *path, int flag)
> }
> continue;
> }
> +#endif
>
> #if ENABLE_FEATURE_HTTPD_ERROR_PAGES
> if (flag == FIRST_PARSE && ch == 'E') {
> @@ -1920,6 +1939,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
> log_and_exit();
> }
>
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
> {
> Htaccess_IP *cur;
> @@ -1949,6 +1969,7 @@ static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
> if (flg_deny_all) /* depends on whether we saw "D:*" */
> send_headers_and_exit(HTTP_FORBIDDEN);
> }
> +#endif
>
> #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
>
> @@ -2228,7 +2249,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
> if (verbose > 2)
> bb_simple_error_msg("connected");
> }
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
> +#endif
>
> /* Install timeout handler. get_line() needs it. */
> signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
> @@ -2378,7 +2401,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
> if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) {
> /* may have subdir config */
> parse_conf(urlcopy + 1, SUBDIR_PARSE);
> +#if ENABLE_FEATURE_HTTPD_ACL_IP
> if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
> +#endif
> }
> *tptr = '/';
> }
> --
> 2.25.1
>
>
>
>
>
> On Sun, 16 Aug 2020 at 00:57, Denys Vlasenko <vda.linux at googlemail.com> wrote:
>>
>> I applied patch 4 as well with some edits.
>> Please test current git.
>> Thank you.
>>
>> On Sat, Aug 15, 2020 at 11:05 PM Denys Vlasenko
>> <vda.linux at googlemail.com> wrote:
>> >
>> > On Sun, Aug 9, 2020 at 12:24 AM Sergey Ponomarev <stokito at gmail.com> wrote:
>> > >
>> > > If server respond with ETag then next time client (browser) resend it via If-None-Match header.
>> > > Then httpd will check if file wasn't modified and if not return 304 Not Modified status code.
>> > > The ETag value is constructed from file's last modification date in unix epoch and it's size:
>> > > "hex(last_mod)-hex(file_size)" e.g. "5e132e20-417" (with quotes).
>> > > That means that it's not completely reliable as hash functions but fair enough.
>> > > The same form of ETag is used by Nginx so load balancing of static content is safe.
>> > >
>> > > Signed-off-by: Sergey Ponomarev <stokito at gmail.com>
>> > > ---
>> > > networking/httpd.c | 73 ++++++++++++++++++++++++++++++++++++++++++++--
>> > > 1 file changed, 70 insertions(+), 3 deletions(-)
>> > >
>> > > diff --git a/networking/httpd.c b/networking/httpd.c
>> > > index 1cea33ddd..cc3f757aa 100644
>> > > --- a/networking/httpd.c
>> > > +++ b/networking/httpd.c
>> > > @@ -215,6 +215,17 @@
>> > > //config: Makes httpd send files using GZIP content encoding if the
>> > > //config: client supports it and a pre-compressed <file>.gz exists.
>> > > //config:
>> > > +//config:config FEATURE_HTTPD_ETAG
>> > > +//config: bool "Support caching via ETag header"
>> > > +//config: default y
>> > > +//config: depends on HTTPD
>> > > +//config: help
>> > > +//config: If server respond with ETag then next time client (browser) resend it via If-None-Match header.
>> > > +//config: Then httpd will check if file wasn't modified and if not return 304 Not Modified status code.
>> > > +//config: The ETag value is constructed from file's last modification date in unix epoch and it's size:
>> > > +//config: "hex(last_mod)-hex(file_size)" e.g. "5e132e20-417" (with quotes).
>> > > +//config: That means that it's not completely reliable as hash functions but fair enough.
>> >
>> > $ make
>> > GEN networking/Config.in
>> > scripts/kconfig/conf -s Config.in
>> > networking/Config.in:294 error: Overlong line
>> > networking/Config.in:295 error: Overlong line
>> > networking/Config.in:296 error: Overlong line
>> > networking/Config.in:298 error: Overlong line
>> > networking/Config.in:306 error: Overlong line
>> > networking/Config.in:307 error: Overlong line
>> > #
>> > # using defaults found in .config
>> >
>> >
>> > > +//config:
>> > > //config:config FEATURE_HTTPD_LAST_MODIFIED
>> > > //config: bool "Add Last-Modified header to response"
>> > > //config: default y
>> > > @@ -266,6 +277,7 @@
>> > >
>> > > #include "libbb.h"
>> > > #include "common_bufsiz.h"
>> > > +#include <inttypes.h>
>> >
>> > libbb.h includes inttypes.h
>> >
>> > > +/*
>> > > + * ETag is "hex(last_mod)-hex(file_size)" e.g. "5e132e20-417"
>> > > + */
>> > > +static char *make_etag(void)
>> > > +{
>> > > + return xasprintf("\"%" PRIx64 "-%" PRIx64 "\"", last_mod, file_size);
>> > > +}
>> > > +
>> >
>> > networking/httpd.c: In function 'make_etag':
>> > networking/httpd.c:1082:19: error: format '%llx' expects argument of
>> > type 'long long unsigned int', but argument 2 has type 'time_t {aka
>> > long int}' [-Werror=format=]
>> > return xasprintf("\"%" PRIx64 "-%" PRIx64 "\"", last_mod, file_size);
>> > ^~~~~
>
>
>
> --
> Sergey Ponomarev, skype:stokito
More information about the busybox
mailing list