[mod_auth] require digest uri= match original URI
[lighttpd.git] / src / mod_scgi.c
blob6784a9d40e403bf9acab822b0f9fe781649e6ede
1 #include "first.h"
3 #include <sys/types.h>
4 #include <errno.h>
5 #include <limits.h>
6 #include <stdlib.h>
7 #include <string.h>
9 #include "gw_backend.h"
10 typedef gw_plugin_config plugin_config;
11 typedef gw_plugin_data plugin_data;
12 typedef gw_handler_ctx handler_ctx;
14 #include "base.h"
15 #include "buffer.h"
16 #include "log.h"
17 #include "status_counter.h"
19 #include "sys-endian.h"
21 enum { LI_PROTOCOL_SCGI, LI_PROTOCOL_UWSGI };
23 SETDEFAULTS_FUNC(mod_scgi_set_defaults) {
24 plugin_data *p = p_d;
25 data_unset *du;
26 size_t i = 0;
28 config_values_t cv[] = {
29 { "scgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
30 { "scgi.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
31 { "scgi.protocol", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
32 { "scgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
33 { "scgi.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
34 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
37 p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
38 force_assert(p->config_storage);
40 for (i = 0; i < srv->config_context->used; i++) {
41 data_config const* config = (data_config const*)srv->config_context->data[i];
42 plugin_config *s;
44 s = calloc(1, sizeof(plugin_config));
45 force_assert(s);
46 s->exts = NULL;
47 s->exts_auth = NULL;
48 s->exts_resp = NULL;
49 s->debug = 0;
50 s->proto = LI_PROTOCOL_SCGI;
51 s->ext_mapping = array_init();
53 cv[0].destination = s->exts; /* not used; T_CONFIG_LOCAL */
54 cv[1].destination = &(s->debug);
55 cv[2].destination = NULL; /* not used; T_CONFIG_LOCAL */
56 cv[3].destination = s->ext_mapping;
57 cv[4].destination = NULL; /* not used; T_CONFIG_LOCAL */
59 p->config_storage[i] = s;
61 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
62 return HANDLER_ERROR;
65 du = array_get_element(config->value, "scgi.server");
66 if (!gw_set_defaults_backend(srv, p, du, i, 1)) {
67 return HANDLER_ERROR;
70 du = array_get_element(config->value, "scgi.balance");
71 if (!gw_set_defaults_balance(srv, s, du)) {
72 return HANDLER_ERROR;
75 if (NULL != (du = array_get_element(config->value, "scgi.protocol"))) {
76 data_string *ds = (data_string *)du;
77 if (du->type == TYPE_STRING
78 && buffer_is_equal_string(ds->value, CONST_STR_LEN("scgi"))) {
79 s->proto = LI_PROTOCOL_SCGI;
80 } else if (du->type == TYPE_STRING
81 && buffer_is_equal_string(ds->value, CONST_STR_LEN("uwsgi"))) {
82 s->proto = LI_PROTOCOL_UWSGI;
83 } else {
84 log_error_write(srv, __FILE__, __LINE__, "sss",
85 "unexpected type for key: ", "scgi.protocol", "expected \"scgi\" or \"uwsgi\"");
87 return HANDLER_ERROR;
92 return HANDLER_GO_ON;
95 static int scgi_env_add_scgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
96 buffer *env = venv;
97 char *dst;
98 size_t len;
100 if (!key || !val) return -1;
102 len = key_len + val_len + 2;
104 if (buffer_string_space(env) < len) {
105 size_t extend = env->size * 2 - buffer_string_length(env);
106 extend = extend > len ? extend : len + 4095;
107 buffer_string_prepare_append(env, extend);
110 dst = buffer_string_prepare_append(env, len);
111 memcpy(dst, key, key_len);
112 dst[key_len] = '\0';
113 memcpy(dst + key_len + 1, val, val_len);
114 dst[key_len + 1 + val_len] = '\0';
115 buffer_commit(env, len);
117 return 0;
121 #ifdef __LITTLE_ENDIAN__
122 #define uwsgi_htole16(x) (x)
123 #else /* __BIG_ENDIAN__ */
124 #define uwsgi_htole16(x) ((uint16_t) (((x) & 0xff) << 8 | ((x) & 0xff00) >> 8))
125 #endif
128 static int scgi_env_add_uwsgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
129 buffer *env = venv;
130 char *dst;
131 size_t len;
132 uint16_t uwlen;
134 if (!key || !val) return -1;
135 if (key_len > USHRT_MAX || val_len > USHRT_MAX) return -1;
137 len = 2 + key_len + 2 + val_len;
139 if (buffer_string_space(env) < len) {
140 size_t extend = env->size * 2 - buffer_string_length(env);
141 extend = extend > len ? extend : len + 4095;
142 buffer_string_prepare_append(env, extend);
145 dst = buffer_string_prepare_append(env, len);
146 uwlen = uwsgi_htole16((uint16_t)key_len);
147 memcpy(dst, (char *)&uwlen, 2);
148 memcpy(dst + 2, key, key_len);
149 uwlen = uwsgi_htole16((uint16_t)val_len);
150 memcpy(dst + 2 + key_len, (char *)&uwlen, 2);
151 memcpy(dst + 2 + key_len + 2, val, val_len);
152 buffer_commit(env, len);
154 return 0;
158 static handler_t scgi_create_env(server *srv, handler_ctx *hctx) {
159 gw_host *host = hctx->host;
160 connection *con = hctx->remote_conn;
161 http_cgi_opts opts = { 0, 0, host->docroot, NULL };
162 http_cgi_header_append_cb scgi_env_add = hctx->conf.proto == LI_PROTOCOL_SCGI
163 ? scgi_env_add_scgi
164 : scgi_env_add_uwsgi;
165 size_t offset;
166 size_t rsz = (size_t)(con->read_queue->bytes_out - hctx->wb->bytes_in);
167 buffer * const b = chunkqueue_prepend_buffer_open_sz(hctx->wb, rsz < 65536 ? rsz : con->header_len);
169 /* save space for 9 digits (plus ':'), though incoming HTTP request
170 * currently limited to 64k (65535, so 5 chars) */
171 buffer_copy_string_len(b, CONST_STR_LEN(" "));
173 if (0 != http_cgi_headers(srv, con, &opts, scgi_env_add, b)) {
174 con->http_status = 400;
175 con->mode = DIRECT;
176 buffer_clear(b);
177 chunkqueue_remove_finished_chunks(hctx->wb);
178 return HANDLER_FINISHED;
181 if (hctx->conf.proto == LI_PROTOCOL_SCGI) {
182 size_t len;
183 scgi_env_add(b, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1"));
184 buffer_clear(srv->tmp_buf);
185 buffer_append_int(srv->tmp_buf, buffer_string_length(b)-10);
186 buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(":"));
187 len = buffer_string_length(srv->tmp_buf);
188 offset = 10 - len;
189 memcpy(b->ptr+offset, srv->tmp_buf->ptr, len);
190 buffer_append_string_len(b, CONST_STR_LEN(","));
191 } else { /* LI_PROTOCOL_UWSGI */
192 /* http://uwsgi-docs.readthedocs.io/en/latest/Protocol.html */
193 size_t len = buffer_string_length(b)-10;
194 uint32_t uwsgi_header;
195 if (len > USHRT_MAX) {
196 con->http_status = 431; /* Request Header Fields Too Large */
197 con->mode = DIRECT;
198 buffer_clear(b);
199 chunkqueue_remove_finished_chunks(hctx->wb);
200 return HANDLER_FINISHED;
202 offset = 10 - 4;
203 uwsgi_header = ((uint32_t)uwsgi_htole16((uint16_t)len)) << 8;
204 memcpy(b->ptr+offset, (char *)&uwsgi_header, 4);
207 hctx->wb_reqlen = buffer_string_length(b) - offset;
208 chunkqueue_prepend_buffer_commit(hctx->wb);
209 #if 0
210 hctx->wb->first->offset += (off_t)offset;
211 hctx->wb->bytes_in -= (off_t)offset;
212 #else
213 chunkqueue_mark_written(hctx->wb, offset);
214 #endif
216 if (con->request.content_length) {
217 chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue);
218 if (con->request.content_length > 0)
219 hctx->wb_reqlen += con->request.content_length; /* total req size */
220 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/
221 hctx->wb_reqlen = -hctx->wb_reqlen;
224 status_counter_inc(srv, CONST_STR_LEN("scgi.requests"));
225 return HANDLER_GO_ON;
228 #define PATCH(x) \
229 p->conf.x = s->x;
230 static int scgi_patch_connection(server *srv, connection *con, plugin_data *p) {
231 size_t i, j;
232 plugin_config *s = p->config_storage[0];
234 PATCH(exts);
235 PATCH(exts_auth);
236 PATCH(exts_resp);
237 PATCH(proto);
238 PATCH(debug);
239 PATCH(balance);
240 PATCH(ext_mapping);
242 /* skip the first, the global context */
243 for (i = 1; i < srv->config_context->used; i++) {
244 data_config *dc = (data_config *)srv->config_context->data[i];
245 s = p->config_storage[i];
247 /* condition didn't match */
248 if (!config_check_cond(srv, con, dc)) continue;
250 /* merge config */
251 for (j = 0; j < dc->value->used; j++) {
252 data_unset *du = dc->value->data[j];
254 if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.server"))) {
255 PATCH(exts);
256 PATCH(exts_auth);
257 PATCH(exts_resp);
258 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.protocol"))) {
259 PATCH(proto);
260 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.balance"))) {
261 PATCH(balance);
262 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.debug"))) {
263 PATCH(debug);
264 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.map-extensions"))) {
265 PATCH(ext_mapping);
270 return 0;
272 #undef PATCH
275 static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
276 plugin_data *p = p_d;
277 handler_t rc;
279 if (con->mode != DIRECT) return HANDLER_GO_ON;
281 scgi_patch_connection(srv, con, p);
282 if (NULL == p->conf.exts) return HANDLER_GO_ON;
284 rc = gw_check_extension(srv, con, p, uri_path_handler, 0);
285 if (HANDLER_GO_ON != rc) return rc;
287 if (con->mode == p->id) {
288 handler_ctx *hctx = con->plugin_ctx[p->id];
289 hctx->opts.backend = BACKEND_SCGI;
290 hctx->create_env = scgi_create_env;
291 hctx->response = chunk_buffer_acquire();
294 return HANDLER_GO_ON;
297 /* uri-path handler */
298 static handler_t scgi_check_extension_1(server *srv, connection *con, void *p_d) {
299 return scgi_check_extension(srv, con, p_d, 1);
302 /* start request handler */
303 static handler_t scgi_check_extension_2(server *srv, connection *con, void *p_d) {
304 return scgi_check_extension(srv, con, p_d, 0);
309 int mod_scgi_plugin_init(plugin *p);
310 int mod_scgi_plugin_init(plugin *p) {
311 p->version = LIGHTTPD_VERSION_ID;
312 p->name = buffer_init_string("scgi");
314 p->init = gw_init;
315 p->cleanup = gw_free;
316 p->set_defaults = mod_scgi_set_defaults;
317 p->connection_reset = gw_connection_reset;
318 p->handle_uri_clean = scgi_check_extension_1;
319 p->handle_subrequest_start = scgi_check_extension_2;
320 p->handle_subrequest = gw_handle_subrequest;
321 p->handle_trigger = gw_handle_trigger;
322 p->handle_waitpid = gw_handle_waitpid_cb;
324 p->data = NULL;
326 return 0;