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