[PATCH] Memcached: memcached_flags_set directive

J Carter jordanc.carter at outlook.com
Mon May 27 05:58:50 UTC 2024


Fixed a typo in patch description, and overlooked buffer overflow...

# HG changeset patch
# User J Carter <jordanc.carter at outlook.com>
# Date 1716779649 -3600
#      Mon May 27 04:14:09 2024 +0100
# Node ID e2ebeb2b05062369626e2de1ba753f318f821d9c
# Parent  f58b6f6362387eeace46043a6fc0bceb56a6786a
Memcached: memcached_flags_set directive.

This directive creates a variable that returns the masked value of
memcached response flags, by bitwise ANDing flags with specified
mask.

The optional 'shift' parameter may be used right bit-shift extracted
value, for unpacking values.

memcached_flags_set <variable> <mask> <shift>

This directive may be specified in http context only.

The purpose of this directive is to provide a more generalized form
of the memcached_gzip_flag directive, to provide the means of
conditionally adding other headers to response, such as cache control
headers, and of setting their value dynamically using packed values
in flags.

Example #1:

memcached_flags_set $flags_expire 1;

map $flags_expire $expires_value {
    0 off;
    1 epoch;
}

server {
    location / {
        expires $expires_value;
        memcached_pass ...;
    }
}

Example #2:

memcached_flags_set $flags_expire 1 0;
memcached_flags_set $flags_expire_hour 62 1;

map $flags_expire $expires_value {
    0 off;
    1 ${flags_expire_hour}h; #0-24h
}

server {
    location / {
        expires $expires_value;
        memcached_pass ...;
    }
}

diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -21,9 +21,16 @@
     size_t                     rest;
     ngx_http_request_t        *request;
     ngx_str_t                  key;
+    ngx_uint_t                 flags;
 } ngx_http_memcached_ctx_t;
 
 
+typedef struct {
+    ngx_uint_t                 mask;
+    ngx_uint_t                 shift;
+} ngx_http_memcached_set_t;
+
+
 static ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);
@@ -33,12 +40,16 @@
 static void ngx_http_memcached_finalize_request(ngx_http_request_t *r,
     ngx_int_t rc);
 
+static ngx_int_t ngx_http_memcached_variable_set(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static void *ngx_http_memcached_create_loc_conf(ngx_conf_t *cf);
 static char *ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf,
     void *parent, void *child);
 
 static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_memcached_set(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 
 static ngx_conf_bitmask_t  ngx_http_memcached_next_upstream_masks[] = {
@@ -130,6 +141,13 @@
       offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),
       NULL },
 
+    { ngx_string("memcached_flags_set"),
+      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE23,
+      ngx_http_memcached_set,
+      0,
+      0,
+      NULL },
+
       ngx_null_command
 };
 
@@ -219,6 +237,7 @@
     }
 
     ctx->request = r;
+    ctx->flags = 0;
 
     ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);
 
@@ -370,20 +389,12 @@
 
         start = p;
 
-        while (*p) {
-            if (*p++ == ' ') {
-                if (mlcf->gzip_flag) {
-                    goto flags;
-                } else {
-                    goto length;
-                }
+        while (*p++ != ' ') {
+            if (*p == '\0') {
+                goto no_valid;
             }
         }
 
-        goto no_valid;
-
-    flags:
-
         flags = ngx_atoi(start, p - start - 1);
 
         if (flags == (ngx_uint_t) NGX_ERROR) {
@@ -407,7 +418,9 @@
             r->headers_out.content_encoding = h;
         }
 
-    length:
+        ctx->flags = flags;
+
+        /* length */
 
         start = p;
         p = line.data + line.len;
@@ -587,6 +600,42 @@
 }
 
 
+static ngx_int_t
+ngx_http_memcached_variable_set(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_memcached_ctx_t    *ctx;
+    ngx_http_memcached_set_t    *set;
+    ngx_uint_t                   value;
+
+    set = (ngx_http_memcached_set_t *) data;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
+    if (ctx == NULL) {
+       v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->data = ngx_pnalloc(r->pool, sizeof("4294967295") - 1);
+    if (v->data == NULL) {
+        return NGX_ERROR;
+    }
+
+    v->len = 0;
+    v->valid = 1;
+    v->no_cacheable = 1;
+    v->not_found = 0;
+
+    value = (ctx->flags & set->mask) >> set->shift;
+
+    if (value <= 4294967295) {
+        v->len = ngx_sprintf(v->data, "%ui", value) - v->data;
+    }
+
+    return NGX_OK;
+}
+
+
 static void *
 ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
 {
@@ -734,3 +783,61 @@
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_http_memcached_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_str_t                   *value;
+    ngx_http_variable_t         *v;
+    ngx_http_memcached_set_t    *set;
+
+    value = cf->args->elts;
+
+    if (value[1].data[0] != '$') {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid variable name \"%V\"", &value[1]);
+        return NGX_CONF_ERROR;
+    }
+
+    value[1].len--;
+    value[1].data++;
+
+    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_NOCACHEABLE);
+    if (v == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    set = ngx_palloc(cf->pool, sizeof(ngx_http_memcached_set_t));
+    if (set == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    set->mask = ngx_atoi(value[2].data, value[2].len);
+
+    if (set->mask == (ngx_uint_t) NGX_ERROR
+        || set->mask > NGX_MAX_UINT32_VALUE)
+    {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid mask \"%V\"", &value[2]);
+        return NGX_CONF_ERROR;
+    }
+
+    set->shift = 0;
+
+    if (cf->args->nelts > 3) {
+
+        set->shift = ngx_atoi(value[3].data, value[3].len);
+
+        if (set->shift == (ngx_uint_t) NGX_ERROR || set->shift > 31) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "invalid shift \"%V\"", &value[3]);
+                return NGX_CONF_ERROR;
+        }
+    }
+
+    v->data = (uintptr_t) set;
+    v->get_handler = ngx_http_memcached_variable_set;
+
+    return NGX_CONF_OK;
+}


More information about the nginx-devel mailing list