]> err.no Git - sope/blob - sope-appserver/mod_ngobjweb/handler.c
milli => micro
[sope] / sope-appserver / mod_ngobjweb / handler.c
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "common.h"
24
25 #define BUFSIZE   2048
26
27 extern int HEAVY_LOG;
28
29 #if WITH_LOGGING
30 static void _logTable(const char *text, apr_table_t *table);
31 #endif
32
33 static ngobjweb_dir_config *_getConfig(request_rec *r) {
34   ngobjweb_dir_config *cfg;
35
36   if (r == NULL) {
37     fprintf(stderr, "%s: missing request !\n", __PRETTY_FUNCTION__);
38     return NULL;
39   }
40   if (r->per_dir_config == NULL) {
41     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
42                  "missing directory config in request ...");
43     return NULL;
44   }
45   
46   cfg = (ngobjweb_dir_config *)
47     ap_get_module_config(r->per_dir_config, &ngobjweb_module);
48   
49   return cfg;
50 }
51
52 static void _extractAppName(const char *uri, char *appName, int maxLen) {
53   char *tmp;
54   
55   /* extract name of application */
56   if ((tmp = index(uri + 1, '/'))) {
57     int len;
58     len = (tmp - (uri + 1));
59     strncpy(appName, (uri + 1), len);
60     appName[len] = '\0';
61   }
62   else {
63     strncpy(appName, (uri + 1), maxLen - 1);
64     appName[maxLen - 1] = '\0';
65   }
66   
67   /* cut off .woa extension from application name */
68   if ((tmp = strstr(appName, ".woa")))
69     *tmp = '\0';
70   
71   /* cut off .sky extension from application name */
72   if ((tmp = strstr(appName, ".sky")))
73     *tmp = '\0';
74 }
75
76 static void *_readRequestBody(request_rec *r, int *requestContentLength) {
77   const char *clen;
78   int  contentLength;
79   void *ptr;
80   int  readBytes, toBeRead;
81   void *requestBody;
82   
83   clen = apr_table_get(r->headers_in, "content-length");
84   contentLength = clen ? atoi(clen) : 0;
85   *requestContentLength = contentLength;
86   
87   /* no content to read ... */
88   if (contentLength == 0) return NULL;
89   
90   /* read content */
91   
92   if (HEAVY_LOG) {
93     ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, 
94                  "going to read %i bytes from browser ...", contentLength);
95   }
96   
97   requestBody = apr_palloc(r->pool, contentLength + 2);
98
99   ptr = requestBody;
100   for (toBeRead = contentLength; toBeRead > 0;) {
101 #ifdef AP_VERSION_1
102     readBytes = ap_bread(r->connection->client, ptr, toBeRead);
103 #else
104                 ap_setup_client_block(r,REQUEST_CHUNKED_DECHUNK);
105     readBytes = ap_get_client_block(r, ptr, toBeRead);
106 #endif
107     toBeRead -= readBytes;
108     ptr += readBytes;
109     if (readBytes == 0) break;
110   }
111   ptr = NULL;
112       
113   if (toBeRead > 0) {
114     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
115                  "couldn't read complete HTTP req body from browser "
116                  "(read %i of %i bytes)",
117                  (contentLength - toBeRead), contentLength);
118     return NULL;
119   }
120   
121   return requestBody;
122 }
123
124 static void
125 _copyHeadersToRequest(request_rec *r, apr_table_t *headers, int *contentLength)
126 {
127   const apr_array_header_t *array;
128   apr_table_entry_t  *entries;
129   int          i;
130   const char   *value;
131   
132   if (headers == NULL) return;
133   
134   value = apr_table_get(headers, "content-type");
135   if (value) r->content_type = value;
136   value = apr_table_get(headers, "content-encoding");
137   if (value) r->content_encoding = value;
138   value = apr_table_get(headers, "content-length");
139   *contentLength = value ? atoi(value) : 0;
140   
141   array   = apr_table_elts(headers);
142   entries = (apr_table_entry_t *)array->elts;
143
144   for (i = 0; i < array->nelts; i++) {
145     apr_table_entry_t *entry = &(entries[i]);
146
147     apr_table_set(r->headers_out, entry->key, entry->val);
148   }
149   // _logTable("out", r->headers_out);
150 }
151
152 static void _logInstanceAddress(request_rec *r, struct sockaddr *address,
153                                 size_t addressLen, int domain)
154 {
155   char buf[1024];
156   
157   if (!HEAVY_LOG) return;
158   
159   apr_snprintf(buf, sizeof(buf), "  => address len=%li domain=%i<", (long int) addressLen, domain);
160   switch (domain) {
161     case AF_INET: strcat(buf, "inet"); break;
162     case AF_UNIX: strcat(buf, "unix"); break;
163     default: strcat(buf, "unknown"); break;
164   }
165   strcat(buf, ">");
166   
167   if (domain == AF_UNIX) {
168     strcat(buf, " path=\"");
169     strcat(buf, ((struct sockaddr_un *)address)->sun_path);
170     strcat(buf, "\"");
171   }
172   else if (domain == AF_INET) {
173     char         *ptr = NULL;
174     int  port;
175     char sport[256];
176     
177     ptr  = inet_ntoa(((struct sockaddr_in *)address)->sin_addr);
178     port = ntohs(((struct sockaddr_in *)address)->sin_port);
179     apr_snprintf(sport, sizeof(sport), "host=\"%s\" port=%i", ptr, port);
180     strcat(buf, sport);
181   }
182   
183   ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, buf);
184 }
185
186 static int _connectInstance(request_rec *r,
187                             int appFd, struct sockaddr *address,
188                             size_t addressLen)
189 {
190   int  result;
191   int  tryCount = 0;
192   char isConnected = 0;
193   
194   result = connect(appFd, address, addressLen);
195   if (result >= 0) return result;
196   
197   while (tryCount < 3) {
198     char *pdelay = NULL; /* pblock_findval("delay", _paras) */
199     int delay    = pdelay ? atoi(pdelay) : 3; // default: 3s
200
201     ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
202                  "sleeping %is ..", delay);
203 #ifdef AP_VERSION_1
204     apr_sleep(delay); /* should be in seconds for Apache 1? */
205 #else
206     apr_sleep(delay * 1000 * 1000 /* in microseconds now! */);
207 #endif
208     
209     ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
210                  "retry connect ..");
211     result = connect(appFd, address, addressLen);
212     
213     if (result >= 0) {
214       isConnected = 1;
215       break;
216     }
217     tryCount++;
218   }
219   
220   if (isConnected == 0) {
221     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
222                  "connect to application instance failed, tried %i times.",
223                  tryCount);
224     close(appFd);
225     return -1;
226   }
227   return result;
228 }
229
230 static int _writeInHeaders(NGBufferedDescriptor *toApp, request_rec *r) {
231   const apr_array_header_t *array;
232   apr_table_entry_t  *entries;
233   int          i;
234   
235   if (r->headers_in == NULL) return 1;
236
237   array   = apr_table_elts(r->headers_in);
238   entries = (apr_table_entry_t *)array->elts;
239
240   for (i = 0; i < array->nelts; i++) {
241     apr_table_entry_t *entry = &(entries[i]);
242         
243     if (!NGBufferedDescriptor_writeHttpHeader(toApp,
244                                               entry->key, entry->val)) {
245       return 0;
246     }
247   }
248   return 1;
249 }
250
251 int ngobjweb_handler(request_rec *r) {
252   struct    sockaddr   *address = NULL;
253   size_t               addressLen;
254   int                  domain;
255   char                 appName[256];
256   NGBufferedDescriptor *toApp = NULL;
257   int                  appFd;
258   int                  result;
259   int                  writeError    = 0;
260   int                  contentLength = 0;
261   int                  statusCode    = 500;
262   ngobjweb_dir_config  *cfg;
263   const char           *uri;
264   unsigned             requestContentLength;
265   void                 *requestBody;
266   
267   uri = r->uri;
268   requestContentLength = 0;
269   requestBody = NULL;
270   
271 #ifndef AP_VERSION_1
272   if (r->handler == NULL)
273     return DECLINED;
274   if (strcmp(r->handler, "ngobjweb-adaptor") != 0)
275     return DECLINED;
276 #endif
277
278   if (uri == NULL)   return DECLINED;
279   if (uri[0] != '/') return DECLINED;
280   if (strstr(uri, "WebServerResources")) return DECLINED;
281
282   /* get directory configuration */
283   
284   if ((cfg = _getConfig(r))) {
285     if (cfg->appPrefix) {
286       if (HEAVY_LOG) {
287         ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
288                      "using prefix '%s'\n", cfg->appPrefix);
289       }
290       uri += strlen(cfg->appPrefix);
291     }
292   }
293   else {
294     return 500;
295   }
296
297   /* find app name in url */
298   _extractAppName(uri, appName, sizeof(appName));
299   
300   /* before continuing, read request body */
301   
302   requestBody = _readRequestBody(r, &contentLength);
303   requestContentLength = contentLength;
304   
305   if ((requestBody == NULL) && (contentLength > 0))
306     /* read failed, error is logged in function */
307     return 500;
308   
309   /* ask SNS for server address */
310
311   if (cfg->snsPort) {
312     address = _sendSNSQuery(r,
313                             r->the_request,
314                             apr_table_get(r->headers_in, "cookie"),
315                             &domain, &addressLen,
316                             appName,
317                             cfg);
318     if (address == NULL) {
319       /* did not find an appropriate application server */
320       ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
321                    "did not find SOPE instance using SNS.");
322       return DECLINED;
323     }
324   }
325   else if (cfg->appPort) {
326     domain = cfg->appPortDomain;
327     
328     if (cfg->appPortDomain == AF_UNIX) {
329       addressLen = sizeof(struct sockaddr_un);
330       address = apr_palloc(r->pool, sizeof(struct sockaddr_un)); 
331       memset(address, 0, sizeof(struct sockaddr_un)); 
332          
333       ((struct sockaddr_un *)address)->sun_family = AF_UNIX; 
334       strncpy(((struct sockaddr_un *)address)->sun_path, 
335               cfg->appPort, 
336               sizeof(((struct sockaddr_un *)address)->sun_path) - 1);
337     }
338     else {
339       struct sockaddr_in *snsi;
340       char *host;
341       int  port;
342       
343       host = "127.0.0.1";
344       port = atoi(cfg->appPort);
345
346       //ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
347       //             "appPort: '%s', cfg 0x%08X", cfg->appPort, cfg);
348       
349       addressLen = sizeof(struct sockaddr_in);
350       address = apr_palloc(r->pool, sizeof(struct sockaddr_in));
351       memset(address, 0, sizeof(struct sockaddr_in)); 
352       snsi = (struct sockaddr_in *)address; 
353          
354       snsi->sin_addr.s_addr = apr_inet_addr(host); 
355       
356       snsi->sin_family = AF_INET; 
357       snsi->sin_port   = htons((short)(port & 0xFFFF)); 
358       
359       if (snsi->sin_addr.s_addr == -1) { 
360         ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
361                      "couldn't convert snsd IP address: %s", host); 
362       } 
363       if (HEAVY_LOG && 0) { 
364         ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
365                      "connect IP address: %s", host); 
366       } 
367     }
368   }
369   else {
370     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
371                  "neither SNS port nor app port are set for request ...");
372     return 500;
373   }
374
375   if (addressLen > 10000) {
376     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
377                  "suspect instance port length (%li) ...", (long int) addressLen);
378     return 500;
379   }
380   
381   _logInstanceAddress(r, address, addressLen, domain);
382   
383   /* setup connection to application server */
384   
385   if ((appFd = socket(domain, SOCK_STREAM, 0)) < 0) {
386     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
387                  "could not create socket in domain %i.", domain);
388     return DECLINED;
389   }
390
391   if ((result = _connectInstance(r, appFd, address, addressLen)) < 0)
392     return 500;
393   
394   toApp = NGBufferedDescriptor_newWithOwnedDescriptorAndSize(appFd, 512);
395   if (toApp == NULL) {
396     close(appFd);
397     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
398                  "could not alloc socket buffer for "
399                  "application server connection");
400     return 500;
401   }
402   
403   /* write request to application server */
404   
405   if (HEAVY_LOG)
406     ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, "transfer reqline");
407
408   {
409     char *reqLine;
410     unsigned toGo;
411
412     reqLine = r->the_request;
413     toGo = reqLine ? strlen(reqLine) : 0;
414     
415     ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
416                  "req is %s(len=%i)", reqLine, toGo);
417
418     if (!NGBufferedDescriptor_safeWrite(toApp, reqLine,
419                                         reqLine ? strlen(reqLine) : 0)) {
420       writeError = 1;
421       goto writeErrorHandler;
422     }
423     if (!NGBufferedDescriptor_safeWrite(toApp, "\r\n", 2)) {
424       writeError = 1;
425       goto writeErrorHandler;
426     }
427   }
428
429   /* transfer headers */
430   
431   if (writeError == 0) {
432     if (HEAVY_LOG)
433       ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, "transfer hdrs");
434     
435     /* extended adaptor headers */
436     {
437       char tmp[256];
438       const char *value;
439       
440       value = r->protocol;
441       value = value ? value : "http";
442       if (value) {
443         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
444                                                   "x-webobjects-server-protocol",
445                                                   value)) {
446           writeError = 1;
447           goto writeErrorHandler;
448         }
449       }
450
451       if ((value = r->connection->remote_ip)) {
452         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
453                                                   "x-webobjects-remote-addr",
454                                                   value)) {
455           writeError = 1;
456           goto writeErrorHandler;
457         }
458       }
459       
460       value = r->connection->remote_host;
461       if (value == NULL) value = r->connection->remote_ip;
462       if (value) {
463         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
464                                                   "x-webobjects-remote-host",
465                                                   value)) {
466           writeError = 1;
467           goto writeErrorHandler;
468         }
469       }
470
471 #ifdef AP_VERSION_1
472       if ((value = r->connection->ap_auth_type)) {
473 #else
474       if ((value = r->ap_auth_type)) {
475 #endif
476         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
477                                                   "x-webobjects-auth-type",
478                                                   value)) {
479           writeError = 1;
480           goto writeErrorHandler;
481         }
482       }
483       
484 #ifdef AP_VERSION_1
485       if ((value = r->connection->user)) {
486 #else
487       if ((value = r->user)) {
488 #endif
489         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
490                                                   "x-webobjects-remote-user",
491                                                   value)) {
492           writeError = 1;
493           goto writeErrorHandler;
494         }
495       }
496
497       if (cfg) {
498         if (cfg->appPrefix) {
499           if (!NGBufferedDescriptor_writeHttpHeader(toApp,
500                 "x-webobjects-adaptor-prefix", cfg->appPrefix)) {
501             writeError = 1;
502             goto writeErrorHandler;
503           }
504         }
505       }
506
507       if (!NGBufferedDescriptor_writeHttpHeader(toApp,
508                                                 "x-webobjects-server-name",
509                                                 r->server->server_hostname)) {
510         writeError = 1;
511         goto writeErrorHandler;
512       }
513
514       apr_snprintf(tmp, sizeof(tmp), "%i", r->server->port);
515       if (!NGBufferedDescriptor_writeHttpHeader(toApp,
516                                                 "x-webobjects-server-port",
517                                                 tmp)) {
518         writeError = 1;
519         goto writeErrorHandler;
520       }
521
522       // TODO: this seems to be broken with some Apache's!
523       // see: http://www.mail-archive.com/modssl-users@modssl.org/msg16396.html
524       apr_snprintf(tmp, sizeof(tmp), "%s://%s:%i",
525               ap_http_method(r),
526               r->server->server_hostname,
527               r->server->port);
528       if (!NGBufferedDescriptor_writeHttpHeader(toApp,
529                                                 "x-webobjects-server-url",
530                                                 tmp)) {
531         writeError = 1;
532         goto writeErrorHandler;
533       }
534
535       /*
536         SSL stuff:
537         x-webobjects-clients-cert
538         x-webobjects-https-enabled
539         x-webobjects-https-keysize
540         x-webobjects-https-secret-keysize
541       */
542     }
543     
544     /* http headers */
545     if (!_writeInHeaders(toApp, r)) {
546       writeError = 1;
547       goto writeErrorHandler;
548     }
549     
550     if (!NGBufferedDescriptor_safeWrite(toApp, "\r\n", 2)) {
551       writeError = 1;
552       goto writeErrorHandler;
553     }
554     if (!NGBufferedDescriptor_flush(toApp))
555       writeError = 1;
556   }
557
558  writeErrorHandler:
559   if (writeError == 1) {
560     if (toApp) NGBufferedDescriptor_free(toApp);
561     
562     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
563                  "socket write error during transfer of HTTP header section");
564     return 500;
565   }
566   
567   /* transfer request body */
568   
569   if (requestContentLength > 0) {
570     if (!NGBufferedDescriptor_safeWrite(toApp,
571                                         requestBody,
572                                         requestContentLength)) {
573       if (toApp) NGBufferedDescriptor_free(toApp);
574       ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
575                    "couldn't transfer HTTP req body to app server (%i bytes)",
576                    contentLength);
577       return 500;
578     }
579     NGBufferedDescriptor_flush(toApp);
580   }
581   else {
582     if (HEAVY_LOG) {
583       ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
584                    "no content in request to transfer");
585     }
586   }
587   
588   /* read response line */
589   
590   if (!NGScanResponseLine(toApp, NULL, &statusCode, NULL)) {
591     if (toApp) NGBufferedDescriptor_free(toApp);
592     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
593                  "error during reading of response line ..");
594     return 500;
595   }
596   r->status      = statusCode;
597   r->status_line = NULL;
598
599   /* process response headers */
600   {
601     apr_table_t *headers = NULL;
602     
603     if (HEAVY_LOG)
604       ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, "scan headers");
605
606     if ((headers = NGScanHeaders(r->pool, toApp)) == NULL) {
607       ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
608                    "error during parsing of response headers ..");
609     }
610     
611     _copyHeadersToRequest(r, headers, &contentLength);
612 #ifdef AP_VERSION_1
613     ap_send_http_header(r);
614 #endif
615   }
616   
617   /* send response content */
618   
619   if (!r->header_only) {
620     if (contentLength > 0) {
621       void *buffer = NULL;
622       
623       if ((buffer = apr_pcalloc(r->pool, contentLength + 1)) == NULL) {
624         ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
625                      "could not allocate response buffer (size=%i)",
626                      contentLength);
627       }
628
629       // read whole response
630       if (!NGBufferedDescriptor_safeRead(toApp, buffer, contentLength)) {
631         if (toApp) NGBufferedDescriptor_free(toApp);
632       }
633
634       // close connection to app
635       if (toApp) {
636         NGBufferedDescriptor_free(toApp);
637         toApp = NULL;
638       }
639
640       ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
641                    "send response (size=%i)",
642                    contentLength);
643       // send response to client
644       ap_rwrite(buffer, contentLength, r);
645       ap_rflush(r);
646     }
647     else if (contentLength == 0) {
648       // no content length header, read until EOF
649       unsigned char buffer[4096];
650       int result = 0;
651       int writeCount = 0;
652
653       do {
654         result = NGBufferedDescriptor_read(toApp, buffer, sizeof(buffer));
655         if (result > 0) {
656           ap_rwrite(buffer, result, r);
657           ap_rflush(r);
658           writeCount += result;
659         }
660       }
661       while (result > 0);
662
663       if (HEAVY_LOG && (writeCount > 0)) {
664         ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
665                      "write %i bytes (without content-length header)",
666                      writeCount);
667       }
668     }
669   }
670   
671   return OK;
672 }
673
674 #if WITH_LOGGING
675 static void test(void) {
676   fprintf(stderr,
677           "%s: called:\n"
678           "  app:      %s\n"
679           "  uri:      %s\n"
680           "  pathinfo: %s\n"
681           "  method:   %s\n"
682           "  protocol: %s\n"
683           "  1st:      %s\n"
684           "  host:     %s\n"
685           "  type:     %s\n"
686           "  handler:  %s\n",
687           __PRETTY_FUNCTION__,
688           appName,
689           r->uri,
690           r->path_info,
691           r->method,
692           r->protocol,
693           r->the_request,
694           apr_table_get(r->headers_in, "content-length"),
695           r->content_type,
696           r->handler
697           );
698
699   _logTable("  out", r->headers_out);
700   _logTable("  err", r->err_headers_out);
701   _logTable("  env", r->subprocess_env);
702   _logTable("  in",  r->headers_in);
703 }
704
705 static void _logTable(const char *text, apr_table_t *table) {
706   apr_array_header_t *array;
707   apr_table_entry_t  *entries;
708   int          i;
709
710   if (table == NULL) {
711     fprintf(stderr, "%s: log NULL table.\n", text);
712     return;
713   }
714
715   array   = apr_table_elts(table);
716   entries = (apr_table_entry_t *)array->elts;
717
718   if (array->nelts == 0) {
719     fprintf(stderr, "%s: empty\n", text);
720     return;
721   }
722
723   for (i = 0; i < array->nelts; i++) {
724     apr_table_entry_t *entry = &(entries[i]);
725     
726     fprintf(stderr, "%s: %s: %s\n", text, entry->key, entry->val);
727   }
728 }
729 #endif