1 #include "first.h" 2 3 #include <sys/types.h> 4 #include <limits.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #include "gw_backend.h" 9 typedef gw_plugin_config plugin_config; 10 typedef gw_plugin_data plugin_data; 11 typedef gw_handler_ctx handler_ctx; 12 13 #include "base.h" 14 #include "buffer.h" 15 #include "http_cgi.h" 16 #include "log.h" 17 #include "status_counter.h" 18 19 enum { LI_PROTOCOL_SCGI, LI_PROTOCOL_UWSGI }; 20 21 static void mod_scgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 22 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 23 case 0: /* scgi.server */ 24 if (cpv->vtype == T_CONFIG_LOCAL) { 25 gw_plugin_config * const gw = cpv->v.v; 26 pconf->exts = gw->exts; 27 pconf->exts_auth = gw->exts_auth; 28 pconf->exts_resp = gw->exts_resp; 29 } 30 break; 31 case 1: /* scgi.balance */ 32 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 33 pconf->balance = (int)cpv->v.u; 34 break; 35 case 2: /* scgi.debug */ 36 pconf->debug = (int)cpv->v.u; 37 break; 38 case 3: /* scgi.map-extensions */ 39 pconf->ext_mapping = cpv->v.a; 40 break; 41 case 4: /* scgi.protocol */ 42 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 43 pconf->proto = (int)cpv->v.u; 44 break; 45 default:/* should not happen */ 46 return; 47 } 48 } 49 50 static void mod_scgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 51 do { 52 mod_scgi_merge_config_cpv(pconf, cpv); 53 } while ((++cpv)->k_id != -1); 54 } 55 56 static void mod_scgi_patch_config(request_st * const r, plugin_data * const p) { 57 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 58 for (int i = 1, used = p->nconfig; i < used; ++i) { 59 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 60 mod_scgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 61 } 62 } 63 64 SETDEFAULTS_FUNC(mod_scgi_set_defaults) { 65 static const config_plugin_keys_t cpk[] = { 66 { CONST_STR_LEN("scgi.server"), 67 T_CONFIG_ARRAY_KVARRAY, 68 T_CONFIG_SCOPE_CONNECTION } 69 ,{ CONST_STR_LEN("scgi.balance"), 70 T_CONFIG_STRING, 71 T_CONFIG_SCOPE_CONNECTION } 72 ,{ CONST_STR_LEN("scgi.debug"), 73 T_CONFIG_INT, 74 T_CONFIG_SCOPE_CONNECTION } 75 ,{ CONST_STR_LEN("scgi.map-extensions"), 76 T_CONFIG_ARRAY_KVSTRING, 77 T_CONFIG_SCOPE_CONNECTION } 78 ,{ CONST_STR_LEN("scgi.protocol"), 79 T_CONFIG_STRING, 80 T_CONFIG_SCOPE_CONNECTION } 81 ,{ NULL, 0, 82 T_CONFIG_UNSET, 83 T_CONFIG_SCOPE_UNSET } 84 }; 85 86 plugin_data * const p = p_d; 87 if (!config_plugin_values_init(srv, p, cpk, "mod_scgi")) 88 return HANDLER_ERROR; 89 90 /* process and validate config directives 91 * (init i to 0 if global context; to 1 to skip empty global context) */ 92 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 93 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 94 for (; -1 != cpv->k_id; ++cpv) { 95 switch (cpv->k_id) { 96 case 0:{/* scgi.server */ 97 gw_plugin_config *gw = calloc(1, sizeof(gw_plugin_config)); 98 force_assert(gw); 99 if (!gw_set_defaults_backend(srv, p, cpv->v.a, gw, 1, 100 cpk[cpv->k_id].k)) { 101 gw_plugin_config_free(gw); 102 return HANDLER_ERROR; 103 } 104 cpv->v.v = gw; 105 cpv->vtype = T_CONFIG_LOCAL; 106 break; 107 } 108 case 1: /* scgi.balance */ 109 cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); 110 break; 111 case 2: /* scgi.debug */ 112 case 3: /* scgi.map-extensions */ 113 break; 114 case 4: /* scgi.protocol */ 115 if (buffer_eq_slen(cpv->v.b, CONST_STR_LEN("scgi"))) 116 cpv->v.u = LI_PROTOCOL_SCGI; 117 else if (buffer_eq_slen(cpv->v.b, CONST_STR_LEN("uwsgi"))) 118 cpv->v.u = LI_PROTOCOL_UWSGI; 119 else { 120 log_error(srv->errh, __FILE__, __LINE__, 121 "unexpected type for key: %s" 122 "expected \"scgi\" or \"uwsgi\"", cpk[cpv->k_id].k); 123 return HANDLER_ERROR; 124 } 125 break; 126 default:/* should not happen */ 127 break; 128 } 129 } 130 } 131 132 /* default is 0 */ 133 /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/ 134 /*p->defaults.proto = LI_PROTOCOL_SCGI;*//*(default)*/ 135 136 /* initialize p->defaults from global config context */ 137 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 138 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 139 if (-1 != cpv->k_id) 140 mod_scgi_merge_config(&p->defaults, cpv); 141 } 142 143 return HANDLER_GO_ON; 144 } 145 146 static int scgi_env_add_scgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 147 buffer *env = venv; 148 size_t len; 149 150 if (!key || (!val && val_len)) return -1; 151 152 len = key_len + val_len + 2; 153 154 char *dst = buffer_extend(env, len); 155 memcpy(dst, key, key_len); 156 dst[key_len] = '\0'; 157 dst += key_len + 1; 158 memcpy(dst, val, val_len); 159 dst[val_len] = '\0'; 160 161 return 0; 162 } 163 164 165 static int scgi_env_add_uwsgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 166 if (!key || (!val && val_len)) return -1; 167 if (key_len > USHRT_MAX || val_len > USHRT_MAX) return -1; 168 169 char *dst = buffer_extend(venv, 2 + key_len + 2 + val_len); 170 dst[0] = key_len & 0xff; /* little-endian */ 171 dst[1] = (key_len >> 8) & 0xff; 172 memcpy(dst + 2, key, key_len); 173 dst += 2+key_len; 174 dst[0] = val_len & 0xff; /* little-endian */ 175 dst[1] = (val_len >> 8) & 0xff; 176 memcpy(dst + 2, val, val_len); 177 178 return 0; 179 } 180 181 182 static handler_t scgi_create_env(handler_ctx *hctx) { 183 gw_host *host = hctx->host; 184 request_st * const r = hctx->r; 185 http_cgi_opts opts = { 0, 0, host->docroot, NULL }; 186 http_cgi_header_append_cb scgi_env_add = hctx->conf.proto == LI_PROTOCOL_SCGI 187 ? scgi_env_add_scgi 188 : scgi_env_add_uwsgi; 189 size_t offset; 190 size_t rsz = (size_t)(r->read_queue.bytes_out - hctx->wb.bytes_in); 191 if (rsz >= 65536) rsz = r->rqst_header_len; 192 buffer * const b = chunkqueue_prepend_buffer_open_sz(&hctx->wb, rsz); 193 194 /* save space for 9 digits (plus ':'), though incoming HTTP request 195 * currently limited to 64k (65535, so 5 chars) */ 196 buffer_copy_string_len(b, CONST_STR_LEN(" ")); 197 198 if (0 != http_cgi_headers(r, &opts, scgi_env_add, b)) { 199 r->http_status = 400; 200 r->handler_module = NULL; 201 buffer_clear(b); 202 chunkqueue_remove_finished_chunks(&hctx->wb); 203 return HANDLER_FINISHED; 204 } 205 206 if (hctx->conf.proto == LI_PROTOCOL_SCGI) { 207 buffer * const tb = r->tmp_buf; 208 size_t len; 209 scgi_env_add(b, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1")); 210 buffer_clear(tb); 211 buffer_append_int(tb, buffer_clen(b)-10); 212 buffer_append_string_len(tb, CONST_STR_LEN(":")); 213 len = buffer_clen(tb); 214 offset = 10 - len; 215 memcpy(b->ptr+offset, tb->ptr, len); 216 buffer_append_string_len(b, CONST_STR_LEN(",")); 217 } else { /* LI_PROTOCOL_UWSGI */ 218 /* http://uwsgi-docs.readthedocs.io/en/latest/Protocol.html */ 219 size_t len = buffer_clen(b)-10; 220 if (len > USHRT_MAX) { 221 r->http_status = 431; /* Request Header Fields Too Large */ 222 r->handler_module = NULL; 223 buffer_clear(b); 224 chunkqueue_remove_finished_chunks(&hctx->wb); 225 return HANDLER_FINISHED; 226 } 227 offset = 10 - 4; 228 b->ptr[offset] = 0; 229 b->ptr[offset+1] = len & 0xff; /* little-endian */ 230 b->ptr[offset+2] = (len >> 8) & 0xff; 231 b->ptr[offset+3] = 0; 232 } 233 234 hctx->wb_reqlen = buffer_clen(b) - offset; 235 chunkqueue_prepend_buffer_commit(&hctx->wb); 236 chunkqueue_mark_written(&hctx->wb, offset); 237 hctx->wb.bytes_in -= (off_t)offset; 238 hctx->wb.bytes_out -= (off_t)offset; 239 240 if (r->reqbody_length) { 241 chunkqueue_append_chunkqueue(&hctx->wb, &r->reqbody_queue); 242 if (r->reqbody_length > 0) 243 hctx->wb_reqlen += r->reqbody_length; /* total req size */ 244 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ 245 hctx->wb_reqlen = -hctx->wb_reqlen; 246 } 247 248 status_counter_inc(CONST_STR_LEN("scgi.requests")); 249 return HANDLER_GO_ON; 250 } 251 252 253 static handler_t scgi_check_extension(request_st * const r, void *p_d, int uri_path_handler) { 254 plugin_data *p = p_d; 255 handler_t rc; 256 257 if (NULL != r->handler_module) return HANDLER_GO_ON; 258 259 mod_scgi_patch_config(r, p); 260 if (NULL == p->conf.exts) return HANDLER_GO_ON; 261 262 rc = gw_check_extension(r, p, uri_path_handler, 0); 263 if (HANDLER_GO_ON != rc) return rc; 264 265 if (r->handler_module == p->self) { 266 handler_ctx *hctx = r->plugin_ctx[p->id]; 267 hctx->opts.backend = BACKEND_SCGI; 268 hctx->create_env = scgi_create_env; 269 hctx->response = chunk_buffer_acquire(); 270 } 271 272 return HANDLER_GO_ON; 273 } 274 275 /* uri-path handler */ 276 static handler_t scgi_check_extension_1(request_st * const r, void *p_d) { 277 return scgi_check_extension(r, p_d, 1); 278 } 279 280 /* start request handler */ 281 static handler_t scgi_check_extension_2(request_st * const r, void *p_d) { 282 return scgi_check_extension(r, p_d, 0); 283 } 284 285 286 287 int mod_scgi_plugin_init(plugin *p); 288 int mod_scgi_plugin_init(plugin *p) { 289 p->version = LIGHTTPD_VERSION_ID; 290 p->name = "scgi"; 291 292 p->init = gw_init; 293 p->cleanup = gw_free; 294 p->set_defaults = mod_scgi_set_defaults; 295 p->handle_request_reset = gw_handle_request_reset; 296 p->handle_uri_clean = scgi_check_extension_1; 297 p->handle_subrequest_start = scgi_check_extension_2; 298 p->handle_subrequest = gw_handle_subrequest; 299 p->handle_trigger = gw_handle_trigger; 300 p->handle_waitpid = gw_handle_waitpid_cb; 301 302 return 0; 303 } 304