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
;
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
) {
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
];
44 s
= calloc(1, sizeof(plugin_config
));
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
)) {
65 du
= array_get_element(config
->value
, "scgi.server");
66 if (!gw_set_defaults_backend(srv
, p
, du
, i
, 1)) {
70 du
= array_get_element(config
->value
, "scgi.balance");
71 if (!gw_set_defaults_balance(srv
, s
, du
)) {
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
;
84 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
85 "unexpected type for key: ", "scgi.protocol", "expected \"scgi\" or \"uwsgi\"");
95 static int scgi_env_add_scgi(void *venv
, const char *key
, size_t key_len
, const char *val
, size_t val_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
);
113 memcpy(dst
+ key_len
+ 1, val
, val_len
);
114 dst
[key_len
+ 1 + val_len
] = '\0';
115 buffer_commit(env
, len
);
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))
128 static int scgi_env_add_uwsgi(void *venv
, const char *key
, size_t key_len
, const char *val
, size_t val_len
) {
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
);
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
164 : scgi_env_add_uwsgi
;
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;
177 chunkqueue_remove_finished_chunks(hctx
->wb
);
178 return HANDLER_FINISHED
;
181 if (hctx
->conf
.proto
== LI_PROTOCOL_SCGI
) {
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
);
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 */
199 chunkqueue_remove_finished_chunks(hctx
->wb
);
200 return HANDLER_FINISHED
;
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
);
210 hctx
->wb
->first
->offset
+= (off_t
)offset
;
211 hctx
->wb
->bytes_in
-= (off_t
)offset
;
213 chunkqueue_mark_written(hctx
->wb
, offset
);
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
;
230 static int scgi_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
232 plugin_config
*s
= p
->config_storage
[0];
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;
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"))) {
258 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("scgi.protocol"))) {
260 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("scgi.balance"))) {
262 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("scgi.debug"))) {
264 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("scgi.map-extensions"))) {
275 static handler_t
scgi_check_extension(server
*srv
, connection
*con
, void *p_d
, int uri_path_handler
) {
276 plugin_data
*p
= p_d
;
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");
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
;