Palm Plugin: fix missed epoll notification
[MonkeyD.git] / plugins / palm / palm.c
blob61865c9f301811a18c013e829ba27a108d184032
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /* Monkey HTTP Daemon
4 * ------------------
5 * Copyright (C) 2001-2010, Eduardo Silva P.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
28 #include "config.h"
29 #include "plugin.h"
30 #include "str.h"
31 #include "http.h"
32 #include "http_status.h"
33 #include "monkey.h"
34 #include "epoll.h"
35 #include "utils.h"
36 #include "header.h"
38 #include "cgi.h"
39 #include "palm.h"
40 #include "request.h"
42 /* Plugin data for register */
43 mk_plugin_data_t _shortname = "palm";
44 mk_plugin_data_t _name = "Palm";
45 mk_plugin_data_t _version = "0.2";
46 mk_plugin_hook_t _hooks = MK_PLUGIN_STAGE_10 | MK_PLUGIN_STAGE_40;
48 /* Read database configuration parameters */
49 int mk_palm_conf(char *confdir)
51 int i, ret = 0;
52 unsigned long len;
53 char *conf_path;
54 struct mk_config *p;
55 struct mk_palm *new, *r;
56 struct mk_string_line *line, *lp;
58 mk_api->str_build(&conf_path, &len, "%s/palm.conf", confdir);
60 p = conf = mk_api->config_create(conf_path);
62 r = palms;
63 while (p) {
64 /* Validate palm configuration line */
65 i = 0;
66 if (strcasecmp(p->key, "Palm") == 0) {
67 line = mk_api->str_split_line(p->val);
68 lp = line;
70 while (lp) {
71 i++;
72 if (lp->len <= 0) {
73 fprintf(stderr, MK_PALM_ERROR_LINE, p->val);
74 _exit(1);
76 lp = lp->next;
79 if (i != 4) {
80 fprintf(stderr, MK_PALM_ERROR_LINE, p->val);
81 _exit(1);
84 lp = line;
86 /* Alloc node */
87 new = mk_api->mem_alloc(sizeof(struct mk_palm));
89 /* Palm file extensions */
90 new->extension = lp->val;
91 lp = lp->next;
93 /* Palm mime type */
94 new->mimetype = lp->val;
95 lp = lp->next;
97 /* Palm server address */
98 new->server_addr = lp->val;
99 lp = lp->next;
101 /* Palm server TCP port */
102 new->server_port = atoi(lp->val);
103 lp = lp->next;
105 new->next = NULL;
107 /* Linking node */
108 if (!palms) {
109 palms = new;
111 else {
112 r = palms;
113 while (r->next) {
114 r = r->next;
116 r->next = new;
118 p = p->next;
121 mk_api->mem_free(conf_path);
122 return ret;
125 struct mk_palm *mk_palm_get_handler(mk_pointer * file)
127 struct mk_palm *p;
128 int j, len, extlen;
130 j = len = file->len;
132 /* looking for extension */
133 while (file->data[j] != '.' && j >= 0) {
134 j--;
137 extlen = file->len - j - 1;
138 if (j == 0) {
139 return NULL;
142 p = palms;
143 while (p) {
144 if (strncasecmp(file->data + j + 1, p->extension, extlen) == 0) {
145 return p;
147 p = p->next;
150 return NULL;
153 void mk_palm_iov_add_header(struct mk_iov *iov,
154 mk_pointer header, mk_pointer value)
156 mk_api->iov_add_entry(iov, header.data, header.len,
157 mk_iov_equal, MK_IOV_NOT_FREE_BUF);
158 mk_api->iov_add_entry(iov, value.data, value.len,
159 mk_iov_crlf, MK_IOV_NOT_FREE_BUF);
162 struct mk_iov *mk_palm_create_env(struct client_request *cr,
163 struct request *sr)
165 struct mk_iov *iov;
167 iov = mk_api->iov_create(100, 0);
168 PLUGIN_TRACE( "Create environment for palm server");
169 mk_api->iov_add_entry(iov, sr->real_path.data,
170 sr->real_path.len, mk_iov_crlf, MK_IOV_NOT_FREE_BUF);
172 mk_api->iov_add_entry(iov, mk_cgi_document_root.data,
173 mk_cgi_document_root.len,
174 mk_iov_equal, MK_IOV_NOT_FREE_BUF);
176 mk_api->iov_add_entry(iov, sr->host_conf->documentroot.data,
177 sr->host_conf->documentroot.len, mk_iov_crlf,
178 MK_IOV_NOT_FREE_BUF);
180 if (sr->method == HTTP_METHOD_POST && sr->content_length > 0) {
181 /* FIX Content length:
182 mk_palm_iov_add_header(iov, mk_cgi_content_length,
183 sr->content_length);
185 mk_palm_iov_add_header(iov, mk_cgi_content_type, sr->content_type);
189 // mk_palm_iov_add_header(iov, mk_cgi_server_addr, mk_api->config->server_addr);
190 mk_palm_iov_add_header(iov, mk_cgi_server_name, sr->host);
191 mk_palm_iov_add_header(iov, mk_cgi_server_protocol, mk_monkey_protocol);
192 mk_palm_iov_add_header(iov, mk_cgi_server_software,
193 mk_api->config->server_software);
194 //mk_palm_iov_add_header(iov, mk_cgi_server_signature, sr->host_conf->host_signature);
196 if (sr->user_agent.data)
197 mk_palm_iov_add_header(iov, mk_cgi_http_user_agent, sr->user_agent);
199 if (sr->accept.data)
200 mk_palm_iov_add_header(iov, mk_cgi_http_accept, sr->accept);
202 if (sr->accept_charset.data)
203 mk_palm_iov_add_header(iov, mk_cgi_http_accept_charset,
204 sr->accept_charset);
206 if (sr->accept_encoding.data)
207 mk_palm_iov_add_header(iov, mk_cgi_http_accept_encoding,
208 sr->accept_encoding);
210 if (sr->accept_language.data)
211 mk_palm_iov_add_header(iov, mk_cgi_http_accept_language,
212 sr->accept_language);
214 if (sr->host.data)
215 mk_palm_iov_add_header(iov, mk_cgi_http_host, sr->host);
217 if (sr->cookies.data)
218 mk_palm_iov_add_header(iov, mk_cgi_http_cookie, sr->cookies);
220 if (sr->referer.data)
221 mk_palm_iov_add_header(iov, mk_cgi_http_referer, sr->referer);
223 // mk_palm_iov_add_header(iov, mk_cgi_server_port, mk_monkey_port);
224 mk_palm_iov_add_header(iov, mk_cgi_gateway_interface, mk_cgi_version);
225 //mk_palm_iov_add_header(iov, mk_cgi_remote_addr, cr->ip);
226 mk_palm_iov_add_header(iov, mk_cgi_request_uri, sr->uri);
227 //mk_palm_iov_add_header(iov, mk_cgi_request_method, sr->method);
228 mk_palm_iov_add_header(iov, mk_cgi_script_name, sr->uri);
231 /* real path is not an mk_pointer */
232 mk_palm_iov_add_header(iov, mk_cgi_script_filename, sr->real_path);
233 //mk_palm_iov_add_header(iov, mk_cgi_remote_port, cr->port);
234 mk_palm_iov_add_header(iov, mk_cgi_query_string, sr->query_string);
235 //mk_palm_iov_add_header(iov, mk_cgi_post_vars, sr->post_variables);
237 /* CRLF */
238 mk_api->iov_add_entry(iov, mk_iov_crlf.data, mk_iov_crlf.len,
239 mk_iov_none, MK_IOV_NOT_FREE_BUF);
240 mk_api->iov_add_entry(iov, mk_iov_crlf.data, mk_iov_crlf.len,
241 mk_iov_none, MK_IOV_NOT_FREE_BUF);
242 mk_api->iov_add_entry(iov, mk_iov_crlf.data, mk_iov_crlf.len,
243 mk_iov_none, MK_IOV_NOT_FREE_BUF);
244 return iov;
248 int mk_palm_send_headers(struct client_request *cr, struct request *sr)
250 int n;
252 sr->headers->status = M_HTTP_OK;
253 sr->headers->cgi = SH_CGI;
255 /* Chunked transfer encoding */
256 if (sr->protocol >= HTTP_PROTOCOL_11) {
257 sr->headers->transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
260 /* Send just headers from buffer */
261 PLUGIN_TRACE("Sending headers to FD %i", cr->socket);
263 n = (int) mk_api->header_send(cr->socket, cr, sr, sr->log);
265 /* Monkey core send_headers set TCP_CORK_ON, we need to get
266 * back the status to OFF
268 mk_api->socket_cork_flag(cr->socket, TCP_CORK_OFF);
269 PLUGIN_TRACE("Send headers returned %i", n);
271 return n;
275 int _mkp_init(void **api, char *confdir)
277 mk_api = *api;
278 palms = 0;
280 /* Init some pointers */
281 mk_api->pointer_set(&mk_monkey_protocol, HTTP_PROTOCOL_11_STR);
282 mk_api->pointer_set(&mk_iov_crlf, MK_IOV_CRLF);
283 mk_api->pointer_set(&mk_iov_equal, MK_IOV_EQUAL);
285 /* Read configuration */
286 mk_palm_conf(confdir);
287 return 0;
290 int _mkp_stage_10(struct server_config *config)
292 mk_cgi_env();
293 return 0;
296 int _mkp_stage_40(struct plugin *plugin, struct client_request *cr, struct request *sr)
298 struct mk_palm *palm;
299 struct mk_palm_request *pr;
301 PLUGIN_TRACE("PALM STAGE 40, requesting '%s'", sr->real_path.data);
303 palm = mk_palm_get_handler(&sr->uri);
304 if (!palm) {
305 PLUGIN_TRACE("PALM NOT ME");
306 return MK_PLUGIN_RET_NOT_ME;
309 /* Connect to server */
310 pr = mk_palm_do_instance(palm, cr, sr);
312 if (!pr) {
313 PLUGIN_TRACE("return %i (MK_PLUGIN_RET_END)", MK_PLUGIN_RET_END);
314 return MK_PLUGIN_RET_END;
317 /* Register Palm instance */
318 mk_palm_request_add(pr);
320 /* Register socket with thread Epoll interface */
321 mk_api->event_add(pr->palm_fd, MK_EPOLL_READ, plugin, cr, sr);
322 PLUGIN_TRACE("Palm: Event registered for palm_socket=%i", pr->palm_fd);
324 /* Send request */
325 mk_palm_send_headers(cr, sr);
326 mk_palm_send_request(cr, sr);
328 PLUGIN_TRACE("return %i (MK_PLUGIN_RET_CONTINUE)", MK_PLUGIN_RET_CONTINUE);
330 return MK_PLUGIN_RET_CONTINUE;
335 struct mk_palm_request *mk_palm_do_instance(struct mk_palm *palm,
336 struct client_request *cr,
337 struct request *sr)
339 int ret;
340 int palm_socket;
342 /* Get Palm handler */
343 // palm = mk_palm_get_handler(&sr->uri);
345 /* Connecting to Palm Server */
346 palm_socket = mk_api->socket_create();
347 ret = mk_api->socket_connect(palm_socket,
348 palm->server_addr,
349 palm->server_port);
351 if (ret < 0) {
352 fprintf(stderr, "\nPalm: Cannot connect to %s on port %i",
353 palm->server_addr, palm->server_port);
354 return NULL;
357 /* Return instance */
358 return mk_palm_request_create(cr->socket, palm_socket, cr, sr, palm);
361 void mk_palm_send_request(struct client_request *cr, struct request *sr)
363 int n;
364 ssize_t bytes_iov=-1;
365 struct mk_iov *iov;
366 struct mk_palm_request *pr;
368 PLUGIN_TRACE("Sending request to Palm Server");
370 pr = mk_palm_request_get(cr->socket);
371 if (pr) {
372 if (pr->bytes_sent == 0) {
374 PLUGIN_TRACE("Palm request: '%s'", sr->real_path.data);
376 /* Palm environment vars */
377 iov = mk_palm_create_env(cr, sr);
379 /* Write request to palm server */
380 bytes_iov = (ssize_t )mk_api->iov_send(pr->palm_fd, iov, MK_IOV_SEND_TO_SOCKET);
382 if (bytes_iov >= 0){
383 pr->bytes_sent += bytes_iov;
384 n = (long) bytes_iov;
387 /* Socket stuff */
388 mk_api->socket_set_tcp_nodelay(pr->palm_fd);
389 mk_api->socket_set_nonblocking(pr->palm_fd);
393 PLUGIN_TRACE("Bytes sent to PALM SERVER: %i", pr->bytes_sent);
396 int mk_palm_send_chunk(int socket, void *buffer, unsigned int len)
398 int n;
399 char *chunk_size=0;
400 unsigned long chunk_len=0;
403 mk_api->socket_cork_flag(socket, TCP_CORK_ON);
404 mk_api->str_build(&chunk_size, &chunk_len, "%x%s", len, MK_CRLF);
405 n = mk_api->socket_send(socket, chunk_size, chunk_len);
406 mk_api->mem_free(chunk_size);
408 if (n < 0) {
409 PLUGIN_TRACE("Error sending chunked header, socket_send() returned %i", n);
410 perror("socket_send");
411 return -1;
414 n = mk_api->socket_send(socket, buffer, len);
415 PLUGIN_TRACE("SEND CHUNK: requested %i, sent %i", len, n);
417 if (n < 0) {
418 PLUGIN_TRACE("Error sending chunked body, socket_send() returned %i", n);
419 perror("socket_send");
420 return -1;
423 mk_api->socket_send(socket, MK_CRLF, 2);
424 mk_api->socket_cork_flag(socket, TCP_CORK_OFF);
425 return n;
428 int _mkp_event_read(struct client_request *cr, struct request *sr)
430 int n;
431 int ret = -1;
432 int headers_end = -1;
433 int read_offset = 0;
434 struct mk_palm_request *pr;
436 pr = mk_palm_request_get(cr->socket);
438 if (!pr){
439 PLUGIN_TRACE("Invalid palm request, not found");
440 return -1;
443 /* Reset read buffer */
444 bzero(pr->data_read, MK_PALM_BUFFER_SIZE);
446 /* Read data */
447 pr->len_read = mk_api->socket_read(pr->palm_fd,
448 pr->data_read,
449 (MK_PALM_BUFFER_SIZE - 1));
451 if (pr->len_read <= 0) {
452 PLUGIN_TRACE("Ending connection: read() = %i", pr->len_read);
453 return MK_PLUGIN_RET_END;
455 else if (pr->len_read > 0) {
456 if (pr->headers_sent == VAR_OFF) {
457 headers_end = (int) mk_api->str_search(pr->data_read,
458 MK_IOV_CRLFCRLF);
459 /* Look for headers end */
460 while (headers_end == -1) {
461 PLUGIN_TRACE("CANNOT FIND HEADERS_END :/");
463 n = mk_api->socket_read(pr->palm_fd,
464 pr->data_read + pr->len_read,
465 (MK_PALM_BUFFER_SIZE -1) - pr->len_read);
467 if (n > 0) {
468 pr->len_read += n;
470 else{
471 PLUGIN_TRACE("********* FIXME ***********");
474 headers_end = (int) mk_api->str_search(pr->data_read,
475 MK_IOV_CRLFCRLF);
478 if (headers_end > 0) {
479 headers_end += 4;
481 else {
482 PLUGIN_TRACE("SOMETHING BAD HAPPENS");
485 /* FIXME: What about if this socket_send wrote partial headers ? ugh! */
486 n = mk_api->socket_send(cr->socket, pr->data_read, headers_end);
488 PLUGIN_TRACE("Headers sent to HTTP client: %i", n);
490 /* Enable headers flag */
491 pr->headers_sent = VAR_ON;
492 read_offset = headers_end;
495 int sent = 0;
496 while (sent != (pr->len_read - read_offset)) {
497 PLUGIN_TRACE("LOOP");
498 n = mk_palm_send_chunk(cr->socket,
499 pr->data_read + read_offset + sent,
500 pr->len_read - read_offset - sent);
502 if (n < 0) {
503 PLUGIN_TRACE("WRITE ERROR");
504 perror("socket_send");
505 return MK_PLUGIN_RET_END;
507 else {
508 PLUGIN_TRACE("BYTES SENT: %i", n);
510 sent += n;
514 mk_api->socket_cork_flag(cr->socket, TCP_CORK_OFF);
515 ret = MK_PLUGIN_RET_CONTINUE;
517 mk_palm_request_update(cr->socket, pr);
519 else {
520 PLUGIN_TRACE("BIG ERROR!");
523 /* Update thread node info */
524 mk_palm_request_update(cr->socket, pr);
526 return ret;
529 void _mkp_event_close(struct client_request *cr, struct request *sr)
531 PLUGIN_TRACE("Closing socket to Palm server");
532 mk_api->socket_close(cr->socket);
535 void _mkp_event_error(struct client_request *cr, struct request *sr)
537 PLUGIN_TRACE( " ERROR ERROR " );
538 mk_api->socket_close(cr->socket);