root/cherokee/trunk/cherokee/handler_scgi.c

Revision 5347, 10.1 KB (checked in by alo, 5 weeks ago)

Fixes a potential stability related to the POST support. The Reverse
Proxy, FastCGI, CGI, uWSGI and SCGI modules might have been affected
by this issue.

Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3/* Cherokee
4 *
5 * Authors:
6 *      Alvaro Lopez Ortega <alvaro@alobbs.com>
7 *
8 * Copyright (C) 2001-2010 Alvaro Lopez Ortega
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General Public
12 * License as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
23 */
24
25#include "common-internal.h"
26#include "handler_scgi.h"
27
28#include "connection.h"
29#include "source_interpreter.h"
30#include "thread.h"
31#include "util.h"
32#include "connection-protected.h"
33#include "bogotime.h"
34
35#define ENTRIES "handler,cgi"
36
37#define set_env(cgi,key,val,len) \
38        add_env_pair (cgi, key, sizeof(key)-1, val, len)
39
40/* Plug-in initialization
41 */
42CGI_LIB_INIT (scgi, http_all_methods);
43
44/* Methods implementation
45 */
46static ret_t
47props_free (cherokee_handler_scgi_props_t *props)
48{
49        if (props->balancer)
50                cherokee_balancer_free (props->balancer);
51
52        /* TODO: Free scgi_env_ref
53         */
54
55        return cherokee_handler_cgi_base_props_free (PROP_CGI_BASE(props));
56}
57
58ret_t
59cherokee_handler_scgi_configure (cherokee_config_node_t *conf, cherokee_server_t *srv, cherokee_module_props_t **_props)
60{
61        ret_t                          ret;
62        cherokee_list_t               *i;
63        cherokee_handler_scgi_props_t *props;
64
65        /* Instance a new property object
66         */
67        if (*_props == NULL) {
68                CHEROKEE_NEW_STRUCT (n, handler_scgi_props);
69
70                cherokee_handler_cgi_base_props_init_base (PROP_CGI_BASE(n),
71                                                           MODULE_PROPS_FREE(props_free));
72
73                INIT_LIST_HEAD(&n->scgi_env_ref);   /* TODO: finish this */
74                n->balancer = NULL;
75
76                *_props = MODULE_PROPS(n);
77        }
78
79        props = PROP_SCGI(*_props);
80
81        /* Parse the configuration tree
82         */
83        cherokee_config_node_foreach (i, conf) {
84                cherokee_config_node_t *subconf = CONFIG_NODE(i);
85
86                if (equal_buf_str (&subconf->key, "balancer")) {
87                        ret = cherokee_balancer_instance (&subconf->val, subconf, srv, &props->balancer);
88                        if (ret != ret_ok) return ret;
89                }
90        }
91
92        /* Init base class
93         */
94        ret = cherokee_handler_cgi_base_configure (conf, srv, _props);
95        if (ret != ret_ok) return ret;
96
97        /* Final checks
98         */
99        if (props->balancer == NULL) {
100                LOG_CRITICAL_S (CHEROKEE_ERROR_HANDLER_NO_BALANCER);
101                return ret_error;
102        }
103
104        return ret_ok;
105}
106
107
108static void
109add_env_pair (cherokee_handler_cgi_base_t *cgi_base,
110              const char *key, int key_len,
111              const char *val, int val_len)
112{
113        static char              zero = '\0';
114        cherokee_handler_scgi_t *scgi = HDL_SCGI(cgi_base);
115
116#ifdef TRACE_ENABLED
117        cherokee_buffer_t       *tmp  = &HANDLER_THREAD(cgi_base)->tmp_buf2;
118
119        cherokee_buffer_clean   (tmp);
120        cherokee_buffer_add     (tmp, key, key_len);
121        cherokee_buffer_add_str (tmp, " = \"");
122        cherokee_buffer_add     (tmp, val, val_len);
123        cherokee_buffer_add_str (tmp, "\"\n");
124
125        TRACE (ENTRIES, "%s", tmp->buf);
126#endif
127
128        cherokee_buffer_ensure_size (&scgi->header, scgi->header.len + key_len + val_len + 3);
129
130        cherokee_buffer_add (&scgi->header, key, key_len);
131        cherokee_buffer_add (&scgi->header, &zero, 1);
132        cherokee_buffer_add (&scgi->header, val, val_len);
133        cherokee_buffer_add (&scgi->header, &zero, 1);
134}
135
136
137static ret_t
138read_from_scgi (cherokee_handler_cgi_base_t *cgi_base, cherokee_buffer_t *buffer)
139{
140        ret_t                    ret;
141        size_t                   read = 0;
142        cherokee_handler_scgi_t *scgi = HDL_SCGI(cgi_base);
143
144        ret = cherokee_socket_bufread (&scgi->socket, buffer, 4096, &read);
145
146        switch (ret) {
147        case ret_eagain:
148                cherokee_thread_deactive_to_polling (HANDLER_THREAD(cgi_base),
149                                                     HANDLER_CONN(cgi_base),
150                                                     scgi->socket.socket,
151                                                     FDPOLL_MODE_READ, false);
152                return ret_eagain;
153
154        case ret_ok:
155                TRACE (ENTRIES, "%d bytes read\n", read);
156                return ret_ok;
157
158        case ret_eof:
159        case ret_error:
160                cgi_base->got_eof = true;
161                return ret;
162
163        default:
164                RET_UNKNOWN(ret);
165        }
166
167        SHOULDNT_HAPPEN;
168        return ret_error;
169}
170
171
172ret_t
173cherokee_handler_scgi_new (cherokee_handler_t **hdl, void *cnt, cherokee_module_props_t *props)
174{
175        CHEROKEE_NEW_STRUCT (n, handler_scgi);
176
177        /* Init the base class
178         */
179        cherokee_handler_cgi_base_init (
180                        HDL_CGI_BASE(n), cnt,
181                        PLUGIN_INFO_HANDLER_PTR(scgi),
182                        HANDLER_PROPS(props),
183                        add_env_pair, read_from_scgi);
184
185        /* Virtual methods
186         */
187        MODULE(n)->init         = (handler_func_init_t) cherokee_handler_scgi_init;
188        MODULE(n)->free         = (module_func_free_t) cherokee_handler_scgi_free;
189        HANDLER(n)->read_post   = (handler_func_read_post_t) cherokee_handler_scgi_read_post;
190
191        /* Virtual methods: implemented by handler_cgi_base
192         */
193        HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_cgi_base_add_headers;
194        HANDLER(n)->step        = (handler_func_step_t) cherokee_handler_cgi_base_step;
195
196        /* Properties
197         */
198        n->src_ref = NULL;
199
200        cherokee_buffer_init (&n->header);
201        cherokee_socket_init (&n->socket);
202
203        /* Return the object
204         */
205        *hdl = HANDLER(n);
206        return ret_ok;
207}
208
209
210ret_t
211cherokee_handler_scgi_free (cherokee_handler_scgi_t *hdl)
212{
213        /* Free the rest of the handler CGI memory
214         */
215        cherokee_handler_cgi_base_free (HDL_CGI_BASE(hdl));
216
217        /* SCGI stuff
218         */
219        cherokee_socket_close (&hdl->socket);
220        cherokee_socket_mrproper (&hdl->socket);
221
222        cherokee_buffer_mrproper (&hdl->header);
223
224        return ret_ok;
225}
226
227
228static ret_t
229netstringer (cherokee_buffer_t *buf)
230{
231        cint_t len;
232        CHEROKEE_TEMP(num,16);
233
234        len = snprintf (num, num_size, "%d:", buf->len);
235        if (len < 0)
236                return ret_error;
237
238        cherokee_buffer_ensure_size (buf, buf->len + len + 2);
239        cherokee_buffer_prepend (buf, num, len);
240        cherokee_buffer_add (buf, ",", 1);
241
242#if 0
243        cherokee_buffer_print_debug (buf, -1);
244#endif
245
246        return ret_ok;
247}
248
249
250static ret_t
251build_header (cherokee_handler_scgi_t *hdl)
252{
253        cuint_t                len;
254        char                   tmp[64];
255        cherokee_connection_t *conn     = HANDLER_CONN(hdl);
256
257        len = snprintf (tmp, sizeof(tmp), FMT_OFFSET, (CST_OFFSET)conn->post.len);
258
259        set_env (HDL_CGI_BASE(hdl), "CONTENT_LENGTH", tmp, len);
260        set_env (HDL_CGI_BASE(hdl), "SCGI", "1", 1);
261
262        cherokee_handler_cgi_base_build_envp (HDL_CGI_BASE(hdl), conn);
263
264        return netstringer (&hdl->header);
265}
266
267
268
269static ret_t
270connect_to_server (cherokee_handler_scgi_t *hdl)
271{
272        ret_t                          ret;
273        cherokee_connection_t         *conn  = HANDLER_CONN(hdl);
274        cherokee_handler_scgi_props_t *props = HANDLER_SCGI_PROPS(hdl);
275
276        /* Get a reference to the target host
277         */
278        if (hdl->src_ref == NULL) {
279                ret = cherokee_balancer_dispatch (props->balancer, conn, &hdl->src_ref);
280                if (ret != ret_ok)
281                        return ret;
282        }
283
284        /* Try to connect
285         */
286        if (hdl->src_ref->type == source_host) {
287                ret = cherokee_source_connect_polling (hdl->src_ref, &hdl->socket, conn);
288                if ((ret == ret_deny) || (ret == ret_error))
289                {
290                        cherokee_balancer_report_fail (props->balancer, conn, hdl->src_ref);
291                }
292        } else {
293                ret = cherokee_source_interpreter_connect_polling (SOURCE_INT(hdl->src_ref),
294                                                                   &hdl->socket, conn);
295        }
296
297        return ret;
298}
299
300
301static ret_t
302send_header (cherokee_handler_scgi_t *hdl)
303{
304        ret_t                  ret;
305        size_t                 written = 0;
306        cherokee_connection_t *conn    = HANDLER_CONN(hdl);
307
308        ret = cherokee_socket_bufwrite (&hdl->socket, &hdl->header, &written);
309        if (ret != ret_ok) {
310                conn->error_code = http_bad_gateway;
311                return ret;
312        }
313
314#if 0
315        cherokee_buffer_print_debug (&hdl->header, -1);
316#endif
317        cherokee_buffer_move_to_begin (&hdl->header, written);
318
319        TRACE (ENTRIES, "sent remaining=%d\n", hdl->header.len);
320
321        if (! cherokee_buffer_is_empty (&hdl->header))
322                return ret_eagain;
323
324        return ret_ok;
325}
326
327
328ret_t
329cherokee_handler_scgi_init (cherokee_handler_scgi_t *hdl)
330{
331        ret_t                  ret;
332        cherokee_connection_t *conn = HANDLER_CONN(hdl);
333
334        switch (HDL_CGI_BASE(hdl)->init_phase) {
335        case hcgi_phase_build_headers:
336                TRACE (ENTRIES, "Init: %s\n", "begins");
337
338                /* Extracts PATH_INFO and filename from request uri
339                 */
340                ret = cherokee_handler_cgi_base_extract_path (HDL_CGI_BASE(hdl), false);
341                if (unlikely (ret < ret_ok)) {
342                        conn->error_code = http_internal_error;
343                        return ret_error;
344                }
345
346                /* Build the headers
347                 */
348                ret = build_header (hdl);
349                if (unlikely (ret != ret_ok)) {
350                        conn->error_code = http_internal_error;
351                        return ret_error;
352                }
353
354                HDL_CGI_BASE(hdl)->init_phase = hcgi_phase_connect;
355
356        case hcgi_phase_connect:
357                TRACE (ENTRIES, "Init: %s\n", "connect");
358
359                /* Connect
360                 */
361                ret = connect_to_server (hdl);
362                switch (ret) {
363                case ret_ok:
364                        break;
365                case ret_eagain:
366                        return ret_eagain;
367                case ret_deny:
368                        conn->error_code = http_gateway_timeout;
369                        return ret_error;
370                default:
371                        conn->error_code = http_service_unavailable;
372                        return ret_error;
373                }
374
375                HDL_CGI_BASE(hdl)->init_phase = hcgi_phase_send_headers;
376
377        case hcgi_phase_send_headers:
378                TRACE (ENTRIES, "Init: %s\n", "send_headers");
379
380                /* Send the header
381                 */
382                ret = send_header (hdl);
383                if (ret != ret_ok) {
384                        return ret;
385                }
386        }
387
388        TRACE (ENTRIES, "Init: %s\n", "finished");
389        return ret_ok;
390}
391
392
393ret_t
394cherokee_handler_scgi_read_post (cherokee_handler_scgi_t *hdl)
395{
396        ret_t                     ret;
397        cherokee_connection_t    *conn     = HANDLER_CONN(hdl);
398        cherokee_socket_status_t  blocking = socket_closed;
399        cherokee_boolean_t        did_IO   = false;
400
401        /* Client Socket -> Back-end SCGI
402         */
403        ret = cherokee_post_send_to_socket (&conn->post, &conn->socket,
404                                            &hdl->socket, NULL, &blocking, &did_IO);
405        if (did_IO) {
406                cherokee_connection_update_timeout (conn);
407        }
408
409        /* Check return
410         */
411        switch (ret) {
412        case ret_ok:
413                break;
414
415        case ret_eagain:
416                if (blocking == socket_writing) {
417                        cherokee_thread_deactive_to_polling (HANDLER_THREAD(hdl),
418                                                             conn, hdl->socket.socket,
419                                                             FDPOLL_MODE_WRITE, false);
420                        return ret_deny;
421                }
422
423                /* ret_eagain - Block on read
424                 * ret_deny   - Block on back-end write
425                 */
426                if (cherokee_post_has_buffered_info (&conn->post)) {
427                        return ret_deny;
428                }
429                return ret_eagain;
430
431        default:
432                conn->error_code = http_bad_gateway;
433                return ret;
434        }
435
436        return ret_ok;
437}
Note: See TracBrowser for help on using the browser.