1e18a033bSKonstantin Ananyev
2e18a033bSKonstantin Ananyev/*
3e18a033bSKonstantin Ananyev * Copyright (C) Igor Sysoev
4e18a033bSKonstantin Ananyev * Copyright (C) Nginx, Inc.
5e18a033bSKonstantin Ananyev */
6e18a033bSKonstantin Ananyev
7e18a033bSKonstantin Ananyev
8e18a033bSKonstantin Ananyev#include <ngx_config.h>
9e18a033bSKonstantin Ananyev#include <ngx_core.h>
10e18a033bSKonstantin Ananyev#include <ngx_http.h>
11e18a033bSKonstantin Ananyev
12e18a033bSKonstantin Ananyev
13e18a033bSKonstantin Ananyevtypedef struct {
14e18a033bSKonstantin Ananyev    size_t      sbrk_size;
15e18a033bSKonstantin Ananyev} ngx_http_degradation_main_conf_t;
16e18a033bSKonstantin Ananyev
17e18a033bSKonstantin Ananyev
18e18a033bSKonstantin Ananyevtypedef struct {
19e18a033bSKonstantin Ananyev    ngx_uint_t  degrade;
20e18a033bSKonstantin Ananyev} ngx_http_degradation_loc_conf_t;
21e18a033bSKonstantin Ananyev
22e18a033bSKonstantin Ananyev
23e18a033bSKonstantin Ananyevstatic ngx_conf_enum_t  ngx_http_degrade[] = {
24e18a033bSKonstantin Ananyev    { ngx_string("204"), 204 },
25e18a033bSKonstantin Ananyev    { ngx_string("444"), 444 },
26e18a033bSKonstantin Ananyev    { ngx_null_string, 0 }
27e18a033bSKonstantin Ananyev};
28e18a033bSKonstantin Ananyev
29e18a033bSKonstantin Ananyev
30e18a033bSKonstantin Ananyevstatic void *ngx_http_degradation_create_main_conf(ngx_conf_t *cf);
31e18a033bSKonstantin Ananyevstatic void *ngx_http_degradation_create_loc_conf(ngx_conf_t *cf);
32e18a033bSKonstantin Ananyevstatic char *ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent,
33e18a033bSKonstantin Ananyev    void *child);
34e18a033bSKonstantin Ananyevstatic char *ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd,
35e18a033bSKonstantin Ananyev    void *conf);
36e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_http_degradation_init(ngx_conf_t *cf);
37e18a033bSKonstantin Ananyev
38e18a033bSKonstantin Ananyev
39e18a033bSKonstantin Ananyevstatic ngx_command_t  ngx_http_degradation_commands[] = {
40e18a033bSKonstantin Ananyev
41e18a033bSKonstantin Ananyev    { ngx_string("degradation"),
42e18a033bSKonstantin Ananyev      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
43e18a033bSKonstantin Ananyev      ngx_http_degradation,
44e18a033bSKonstantin Ananyev      NGX_HTTP_MAIN_CONF_OFFSET,
45e18a033bSKonstantin Ananyev      0,
46e18a033bSKonstantin Ananyev      NULL },
47e18a033bSKonstantin Ananyev
48e18a033bSKonstantin Ananyev    { ngx_string("degrade"),
49e18a033bSKonstantin Ananyev      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
50e18a033bSKonstantin Ananyev      ngx_conf_set_enum_slot,
51e18a033bSKonstantin Ananyev      NGX_HTTP_LOC_CONF_OFFSET,
52e18a033bSKonstantin Ananyev      offsetof(ngx_http_degradation_loc_conf_t, degrade),
53e18a033bSKonstantin Ananyev      &ngx_http_degrade },
54e18a033bSKonstantin Ananyev
55e18a033bSKonstantin Ananyev      ngx_null_command
56e18a033bSKonstantin Ananyev};
57e18a033bSKonstantin Ananyev
58e18a033bSKonstantin Ananyev
59e18a033bSKonstantin Ananyevstatic ngx_http_module_t  ngx_http_degradation_module_ctx = {
60e18a033bSKonstantin Ananyev    NULL,                                  /* preconfiguration */
61e18a033bSKonstantin Ananyev    ngx_http_degradation_init,             /* postconfiguration */
62e18a033bSKonstantin Ananyev
63e18a033bSKonstantin Ananyev    ngx_http_degradation_create_main_conf, /* create main configuration */
64e18a033bSKonstantin Ananyev    NULL,                                  /* init main configuration */
65e18a033bSKonstantin Ananyev
66e18a033bSKonstantin Ananyev    NULL,                                  /* create server configuration */
67e18a033bSKonstantin Ananyev    NULL,                                  /* merge server configuration */
68e18a033bSKonstantin Ananyev
69e18a033bSKonstantin Ananyev    ngx_http_degradation_create_loc_conf,  /* create location configuration */
70e18a033bSKonstantin Ananyev    ngx_http_degradation_merge_loc_conf    /* merge location configuration */
71e18a033bSKonstantin Ananyev};
72e18a033bSKonstantin Ananyev
73e18a033bSKonstantin Ananyev
74e18a033bSKonstantin Ananyevngx_module_t  ngx_http_degradation_module = {
75e18a033bSKonstantin Ananyev    NGX_MODULE_V1,
76e18a033bSKonstantin Ananyev    &ngx_http_degradation_module_ctx,      /* module context */
77e18a033bSKonstantin Ananyev    ngx_http_degradation_commands,         /* module directives */
78e18a033bSKonstantin Ananyev    NGX_HTTP_MODULE,                       /* module type */
79e18a033bSKonstantin Ananyev    NULL,                                  /* init master */
80e18a033bSKonstantin Ananyev    NULL,                                  /* init module */
81e18a033bSKonstantin Ananyev    NULL,                                  /* init process */
82e18a033bSKonstantin Ananyev    NULL,                                  /* init thread */
83e18a033bSKonstantin Ananyev    NULL,                                  /* exit thread */
84e18a033bSKonstantin Ananyev    NULL,                                  /* exit process */
85e18a033bSKonstantin Ananyev    NULL,                                  /* exit master */
86e18a033bSKonstantin Ananyev    NGX_MODULE_V1_PADDING
87e18a033bSKonstantin Ananyev};
88e18a033bSKonstantin Ananyev
89e18a033bSKonstantin Ananyev
90e18a033bSKonstantin Ananyevstatic ngx_int_t
91e18a033bSKonstantin Ananyevngx_http_degradation_handler(ngx_http_request_t *r)
92e18a033bSKonstantin Ananyev{
93e18a033bSKonstantin Ananyev    ngx_http_degradation_loc_conf_t  *dlcf;
94e18a033bSKonstantin Ananyev
95e18a033bSKonstantin Ananyev    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_degradation_module);
96e18a033bSKonstantin Ananyev
97e18a033bSKonstantin Ananyev    if (dlcf->degrade && ngx_http_degraded(r)) {
98e18a033bSKonstantin Ananyev        return dlcf->degrade;
99e18a033bSKonstantin Ananyev    }
100e18a033bSKonstantin Ananyev
101e18a033bSKonstantin Ananyev    return NGX_DECLINED;
102e18a033bSKonstantin Ananyev}
103e18a033bSKonstantin Ananyev
104e18a033bSKonstantin Ananyev
105e18a033bSKonstantin Ananyevngx_uint_t
106e18a033bSKonstantin Ananyevngx_http_degraded(ngx_http_request_t *r)
107e18a033bSKonstantin Ananyev{
108e18a033bSKonstantin Ananyev    time_t                             now;
109e18a033bSKonstantin Ananyev    ngx_uint_t                         log;
110e18a033bSKonstantin Ananyev    static size_t                      sbrk_size;
111e18a033bSKonstantin Ananyev    static time_t                      sbrk_time;
112e18a033bSKonstantin Ananyev    ngx_http_degradation_main_conf_t  *dmcf;
113e18a033bSKonstantin Ananyev
114e18a033bSKonstantin Ananyev    dmcf = ngx_http_get_module_main_conf(r, ngx_http_degradation_module);
115e18a033bSKonstantin Ananyev
116e18a033bSKonstantin Ananyev    if (dmcf->sbrk_size) {
117e18a033bSKonstantin Ananyev
118e18a033bSKonstantin Ananyev        log = 0;
119e18a033bSKonstantin Ananyev        now = ngx_time();
120e18a033bSKonstantin Ananyev
121e18a033bSKonstantin Ananyev        /* lock mutex */
122e18a033bSKonstantin Ananyev
123e18a033bSKonstantin Ananyev        if (now != sbrk_time) {
124e18a033bSKonstantin Ananyev
125e18a033bSKonstantin Ananyev            /*
126e18a033bSKonstantin Ananyev             * ELF/i386 is loaded at 0x08000000, 128M
127e18a033bSKonstantin Ananyev             * ELF/amd64 is loaded at 0x00400000, 4M
128e18a033bSKonstantin Ananyev             *
129e18a033bSKonstantin Ananyev             * use a function address to subtract the loading address
130e18a033bSKonstantin Ananyev             */
131e18a033bSKonstantin Ananyev
132e18a033bSKonstantin Ananyev            sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF);
133e18a033bSKonstantin Ananyev            sbrk_time = now;
134e18a033bSKonstantin Ananyev            log = 1;
135e18a033bSKonstantin Ananyev        }
136e18a033bSKonstantin Ananyev
137e18a033bSKonstantin Ananyev        /* unlock mutex */
138e18a033bSKonstantin Ananyev
139e18a033bSKonstantin Ananyev        if (sbrk_size >= dmcf->sbrk_size) {
140e18a033bSKonstantin Ananyev            if (log) {
141e18a033bSKonstantin Ananyev                ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
142e18a033bSKonstantin Ananyev                              "degradation sbrk:%uzM",
143e18a033bSKonstantin Ananyev                              sbrk_size / (1024 * 1024));
144e18a033bSKonstantin Ananyev            }
145e18a033bSKonstantin Ananyev
146e18a033bSKonstantin Ananyev            return 1;
147e18a033bSKonstantin Ananyev        }
148e18a033bSKonstantin Ananyev    }
149e18a033bSKonstantin Ananyev
150e18a033bSKonstantin Ananyev    return 0;
151e18a033bSKonstantin Ananyev}
152e18a033bSKonstantin Ananyev
153e18a033bSKonstantin Ananyev
154e18a033bSKonstantin Ananyevstatic void *
155e18a033bSKonstantin Ananyevngx_http_degradation_create_main_conf(ngx_conf_t *cf)
156e18a033bSKonstantin Ananyev{
157e18a033bSKonstantin Ananyev    ngx_http_degradation_main_conf_t  *dmcf;
158e18a033bSKonstantin Ananyev
159e18a033bSKonstantin Ananyev    dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_degradation_main_conf_t));
160e18a033bSKonstantin Ananyev    if (dmcf == NULL) {
161e18a033bSKonstantin Ananyev        return NULL;
162e18a033bSKonstantin Ananyev    }
163e18a033bSKonstantin Ananyev
164e18a033bSKonstantin Ananyev    return dmcf;
165e18a033bSKonstantin Ananyev}
166e18a033bSKonstantin Ananyev
167e18a033bSKonstantin Ananyev
168e18a033bSKonstantin Ananyevstatic void *
169e18a033bSKonstantin Ananyevngx_http_degradation_create_loc_conf(ngx_conf_t *cf)
170e18a033bSKonstantin Ananyev{
171e18a033bSKonstantin Ananyev    ngx_http_degradation_loc_conf_t  *conf;
172e18a033bSKonstantin Ananyev
173e18a033bSKonstantin Ananyev    conf = ngx_palloc(cf->pool, sizeof(ngx_http_degradation_loc_conf_t));
174e18a033bSKonstantin Ananyev    if (conf == NULL) {
175e18a033bSKonstantin Ananyev        return NULL;
176e18a033bSKonstantin Ananyev    }
177e18a033bSKonstantin Ananyev
178e18a033bSKonstantin Ananyev    conf->degrade = NGX_CONF_UNSET_UINT;
179e18a033bSKonstantin Ananyev
180e18a033bSKonstantin Ananyev    return conf;
181e18a033bSKonstantin Ananyev}
182e18a033bSKonstantin Ananyev
183e18a033bSKonstantin Ananyev
184e18a033bSKonstantin Ananyevstatic char *
185e18a033bSKonstantin Ananyevngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
186e18a033bSKonstantin Ananyev{
187e18a033bSKonstantin Ananyev    ngx_http_degradation_loc_conf_t  *prev = parent;
188e18a033bSKonstantin Ananyev    ngx_http_degradation_loc_conf_t  *conf = child;
189e18a033bSKonstantin Ananyev
190e18a033bSKonstantin Ananyev    ngx_conf_merge_uint_value(conf->degrade, prev->degrade, 0);
191e18a033bSKonstantin Ananyev
192e18a033bSKonstantin Ananyev    return NGX_CONF_OK;
193e18a033bSKonstantin Ananyev}
194e18a033bSKonstantin Ananyev
195e18a033bSKonstantin Ananyev
196e18a033bSKonstantin Ananyevstatic char *
197e18a033bSKonstantin Ananyevngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
198e18a033bSKonstantin Ananyev{
199e18a033bSKonstantin Ananyev    ngx_http_degradation_main_conf_t  *dmcf = conf;
200e18a033bSKonstantin Ananyev
201e18a033bSKonstantin Ananyev    ngx_str_t  *value, s;
202e18a033bSKonstantin Ananyev
203e18a033bSKonstantin Ananyev    value = cf->args->elts;
204e18a033bSKonstantin Ananyev
205e18a033bSKonstantin Ananyev    if (ngx_strncmp(value[1].data, "sbrk=", 5) == 0) {
206e18a033bSKonstantin Ananyev
207e18a033bSKonstantin Ananyev        s.len = value[1].len - 5;
208e18a033bSKonstantin Ananyev        s.data = value[1].data + 5;
209e18a033bSKonstantin Ananyev
210e18a033bSKonstantin Ananyev        dmcf->sbrk_size = ngx_parse_size(&s);
211e18a033bSKonstantin Ananyev        if (dmcf->sbrk_size == (size_t) NGX_ERROR) {
212e18a033bSKonstantin Ananyev            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
213e18a033bSKonstantin Ananyev                               "invalid sbrk size \"%V\"", &value[1]);
214e18a033bSKonstantin Ananyev            return NGX_CONF_ERROR;
215e18a033bSKonstantin Ananyev        }
216e18a033bSKonstantin Ananyev
217e18a033bSKonstantin Ananyev        return NGX_CONF_OK;
218e18a033bSKonstantin Ananyev    }
219e18a033bSKonstantin Ananyev
220e18a033bSKonstantin Ananyev    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
221e18a033bSKonstantin Ananyev                       "invalid parameter \"%V\"", &value[1]);
222e18a033bSKonstantin Ananyev
223e18a033bSKonstantin Ananyev    return NGX_CONF_ERROR;
224e18a033bSKonstantin Ananyev}
225e18a033bSKonstantin Ananyev
226e18a033bSKonstantin Ananyev
227e18a033bSKonstantin Ananyevstatic ngx_int_t
228e18a033bSKonstantin Ananyevngx_http_degradation_init(ngx_conf_t *cf)
229e18a033bSKonstantin Ananyev{
230e18a033bSKonstantin Ananyev    ngx_http_handler_pt        *h;
231e18a033bSKonstantin Ananyev    ngx_http_core_main_conf_t  *cmcf;
232e18a033bSKonstantin Ananyev
233e18a033bSKonstantin Ananyev    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
234e18a033bSKonstantin Ananyev
235e18a033bSKonstantin Ananyev    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
236e18a033bSKonstantin Ananyev    if (h == NULL) {
237e18a033bSKonstantin Ananyev        return NGX_ERROR;
238e18a033bSKonstantin Ananyev    }
239e18a033bSKonstantin Ananyev
240e18a033bSKonstantin Ananyev    *h = ngx_http_degradation_handler;
241e18a033bSKonstantin Ananyev
242e18a033bSKonstantin Ananyev    return NGX_OK;
243e18a033bSKonstantin Ananyev}
244