root/cherokee/trunk/cherokee/handler_scgi.c

Revision 4436, 10.1 kB (checked in by alo, 5 days ago)

Fixes a bug in the POST support.

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  */
42 CGI_LIB_INIT (scgi, http_all_methods);
43
44 /* Methods implementation
45  */
46 static ret_t
47 props_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
58 ret_t
59 cherokee_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
108 static void
109 add_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
137 static ret_t
138 read_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
172 ret_t
173 cherokee_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
210 ret_t
211 cherokee_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
228 static ret_t
229 netstringer (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
250 static ret_t
251 build_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
269 static ret_t
270 connect_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
301 static ret_t
302 send_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
328 ret_t
329 cherokee_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
393 ret_t
394 cherokee_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, NULL)) {
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.