]> err.no Git - sope/blob - sope-appserver/mod_ngobjweb/handler.c
Xcode build fixed for linking against libical.framework
[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     apr_sleep(delay);
204     
205     ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
206                  "retry connect ..");
207     result = connect(appFd, address, addressLen);
208     
209     if (result >= 0) {
210       isConnected = 1;
211       break;
212     }
213     tryCount++;
214   }
215   
216   if (isConnected == 0) {
217     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
218                  "connect to application instance failed, tried %i times.",
219                  tryCount);
220     close(appFd);
221     return -1;
222   }
223   return result;
224 }
225
226 static int _writeInHeaders(NGBufferedDescriptor *toApp, request_rec *r) {
227   const apr_array_header_t *array;
228   apr_table_entry_t  *entries;
229   int          i;
230   
231   if (r->headers_in == NULL) return 1;
232
233   array   = apr_table_elts(r->headers_in);
234   entries = (apr_table_entry_t *)array->elts;
235
236   for (i = 0; i < array->nelts; i++) {
237     apr_table_entry_t *entry = &(entries[i]);
238         
239     if (!NGBufferedDescriptor_writeHttpHeader(toApp,
240                                               entry->key, entry->val)) {
241       return 0;
242     }
243   }
244   return 1;
245 }
246
247 int ngobjweb_handler(request_rec *r) {
248   struct    sockaddr   *address = NULL;
249   size_t               addressLen;
250   int                  domain;
251   char                 appName[256];
252   NGBufferedDescriptor *toApp = NULL;
253   int                  appFd;
254   int                  result;
255   int                  writeError    = 0;
256   int                  contentLength = 0;
257   int                  statusCode    = 500;
258   ngobjweb_dir_config  *cfg;
259   const char           *uri;
260   unsigned             requestContentLength;
261   void                 *requestBody;
262   
263   uri = r->uri;
264   requestContentLength = 0;
265   requestBody = NULL;
266   
267 #ifndef AP_VERSION_1
268   if (r->handler == NULL)
269     return DECLINED;
270   if (strcmp(r->handler, "ngobjweb-adaptor") != 0)
271     return DECLINED;
272 #endif
273
274   if (uri == NULL)   return DECLINED;
275   if (uri[0] != '/') return DECLINED;
276   if (strstr(uri, "WebServerResources")) return DECLINED;
277
278   /* get directory configuration */
279   
280   if ((cfg = _getConfig(r))) {
281     if (cfg->appPrefix) {
282       if (HEAVY_LOG) {
283         ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
284                      "using prefix '%s'\n", cfg->appPrefix);
285       }
286       uri += strlen(cfg->appPrefix);
287     }
288   }
289   else {
290     return 500;
291   }
292
293   /* find app name in url */
294   _extractAppName(uri, appName, sizeof(appName));
295   
296   /* before continuing, read request body */
297   
298   requestBody = _readRequestBody(r, &contentLength);
299   requestContentLength = contentLength;
300   
301   if ((requestBody == NULL) && (contentLength > 0))
302     /* read failed, error is logged in function */
303     return 500;
304   
305   /* ask SNS for server address */
306
307   if (cfg->snsPort) {
308     address = _sendSNSQuery(r,
309                             r->the_request,
310                             apr_table_get(r->headers_in, "cookie"),
311                             &domain, &addressLen,
312                             appName,
313                             cfg);
314     if (address == NULL) {
315       /* did not find an appropriate application server */
316       ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
317                    "did not find SOPE instance using SNS.");
318       return DECLINED;
319     }
320   }
321   else if (cfg->appPort) {
322     domain = cfg->appPortDomain;
323     
324     if (cfg->appPortDomain == AF_UNIX) {
325       addressLen = sizeof(struct sockaddr_un);
326       address = apr_palloc(r->pool, sizeof(struct sockaddr_un)); 
327       memset(address, 0, sizeof(struct sockaddr_un)); 
328          
329       ((struct sockaddr_un *)address)->sun_family = AF_UNIX; 
330       strncpy(((struct sockaddr_un *)address)->sun_path, 
331               cfg->appPort, 
332               sizeof(((struct sockaddr_un *)address)->sun_path) - 1);
333     }
334     else {
335       struct sockaddr_in *snsi;
336       char *host;
337       int  port;
338       
339       host = "127.0.0.1";
340       port = atoi(cfg->appPort);
341
342       //ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
343       //             "appPort: '%s', cfg 0x%08X", cfg->appPort, cfg);
344       
345       addressLen = sizeof(struct sockaddr_in);
346       address = apr_palloc(r->pool, sizeof(struct sockaddr_in));
347       memset(address, 0, sizeof(struct sockaddr_in)); 
348       snsi = (struct sockaddr_in *)address; 
349          
350       snsi->sin_addr.s_addr = apr_inet_addr(host); 
351       
352       snsi->sin_family = AF_INET; 
353       snsi->sin_port   = htons((short)(port & 0xFFFF)); 
354       
355       if (snsi->sin_addr.s_addr == -1) { 
356         ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
357                      "couldn't convert snsd IP address: %s", host); 
358       } 
359       if (HEAVY_LOG && 0) { 
360         ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
361                      "connect IP address: %s", host); 
362       } 
363     }
364   }
365   else {
366     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
367                  "neither SNS port nor app port are set for request ...");
368     return 500;
369   }
370
371   if (addressLen > 10000) {
372     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
373                  "suspect instance port length (%li) ...", (long int) addressLen);
374     return 500;
375   }
376   
377   _logInstanceAddress(r, address, addressLen, domain);
378   
379   /* setup connection to application server */
380   
381   if ((appFd = socket(domain, SOCK_STREAM, 0)) < 0) {
382     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
383                  "could not create socket in domain %i.", domain);
384     return DECLINED;
385   }
386
387   if ((result = _connectInstance(r, appFd, address, addressLen)) < 0)
388     return 500;
389   
390   toApp = NGBufferedDescriptor_newWithOwnedDescriptorAndSize(appFd, 512);
391   if (toApp == NULL) {
392     close(appFd);
393     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
394                  "could not alloc socket buffer for "
395                  "application server connection");
396     return 500;
397   }
398   
399   /* write request to application server */
400   
401   if (HEAVY_LOG)
402     ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, "transfer reqline");
403
404   {
405     char *reqLine;
406     unsigned toGo;
407
408     reqLine = r->the_request;
409     toGo = reqLine ? strlen(reqLine) : 0;
410     
411     ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
412                  "req is %s(len=%i)", reqLine, toGo);
413
414     if (!NGBufferedDescriptor_safeWrite(toApp, reqLine,
415                                         reqLine ? strlen(reqLine) : 0)) {
416       writeError = 1;
417       goto writeErrorHandler;
418     }
419     if (!NGBufferedDescriptor_safeWrite(toApp, "\r\n", 2)) {
420       writeError = 1;
421       goto writeErrorHandler;
422     }
423   }
424
425   /* transfer headers */
426   
427   if (writeError == 0) {
428     if (HEAVY_LOG)
429       ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, "transfer hdrs");
430     
431     /* extended adaptor headers */
432     {
433       char tmp[256];
434       const char *value;
435       
436       value = r->protocol;
437       value = value ? value : "http";
438       if (value) {
439         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
440                                                   "x-webobjects-server-protocol",
441                                                   value)) {
442           writeError = 1;
443           goto writeErrorHandler;
444         }
445       }
446
447       if ((value = r->connection->remote_ip)) {
448         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
449                                                   "x-webobjects-remote-addr",
450                                                   value)) {
451           writeError = 1;
452           goto writeErrorHandler;
453         }
454       }
455       
456       value = r->connection->remote_host;
457       if (value == NULL) value = r->connection->remote_ip;
458       if (value) {
459         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
460                                                   "x-webobjects-remote-host",
461                                                   value)) {
462           writeError = 1;
463           goto writeErrorHandler;
464         }
465       }
466
467 #ifdef AP_VERSION_1
468       if ((value = r->connection->ap_auth_type)) {
469 #else
470       if ((value = r->ap_auth_type)) {
471 #endif
472         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
473                                                   "x-webobjects-auth-type",
474                                                   value)) {
475           writeError = 1;
476           goto writeErrorHandler;
477         }
478       }
479       
480 #ifdef AP_VERSION_1
481       if ((value = r->connection->user)) {
482 #else
483       if ((value = r->user)) {
484 #endif
485         if (!NGBufferedDescriptor_writeHttpHeader(toApp,
486                                                   "x-webobjects-remote-user",
487                                                   value)) {
488           writeError = 1;
489           goto writeErrorHandler;
490         }
491       }
492
493       if (cfg) {
494         if (cfg->appPrefix) {
495           if (!NGBufferedDescriptor_writeHttpHeader(toApp,
496                 "x-webobjects-adaptor-prefix", cfg->appPrefix)) {
497             writeError = 1;
498             goto writeErrorHandler;
499           }
500         }
501       }
502
503       if (!NGBufferedDescriptor_writeHttpHeader(toApp,
504                                                 "x-webobjects-server-name",
505                                                 r->server->server_hostname)) {
506         writeError = 1;
507         goto writeErrorHandler;
508       }
509
510       apr_snprintf(tmp, sizeof(tmp), "%i", r->server->port);
511       if (!NGBufferedDescriptor_writeHttpHeader(toApp,
512                                                 "x-webobjects-server-port",
513                                                 tmp)) {
514         writeError = 1;
515         goto writeErrorHandler;
516       }
517
518       // TODO: this seems to be broken with some Apache's!
519       // see: http://www.mail-archive.com/modssl-users@modssl.org/msg16396.html
520       apr_snprintf(tmp, sizeof(tmp), "%s://%s:%i",
521               ap_http_method(r),
522               r->server->server_hostname,
523               r->server->port);
524       if (!NGBufferedDescriptor_writeHttpHeader(toApp,
525                                                 "x-webobjects-server-url",
526                                                 tmp)) {
527         writeError = 1;
528         goto writeErrorHandler;
529       }
530
531       /*
532         SSL stuff:
533         x-webobjects-clients-cert
534         x-webobjects-https-enabled
535         x-webobjects-https-keysize
536         x-webobjects-https-secret-keysize
537       */
538     }
539     
540     /* http headers */
541     if (!_writeInHeaders(toApp, r)) {
542       writeError = 1;
543       goto writeErrorHandler;
544     }
545     
546     if (!NGBufferedDescriptor_safeWrite(toApp, "\r\n", 2)) {
547       writeError = 1;
548       goto writeErrorHandler;
549     }
550     if (!NGBufferedDescriptor_flush(toApp))
551       writeError = 1;
552   }
553
554  writeErrorHandler:
555   if (writeError == 1) {
556     if (toApp) NGBufferedDescriptor_free(toApp);
557     
558     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
559                  "socket write error during transfer of HTTP header section");
560     return 500;
561   }
562   
563   /* transfer request body */
564   
565   if (requestContentLength > 0) {
566     if (!NGBufferedDescriptor_safeWrite(toApp,
567                                         requestBody,
568                                         requestContentLength)) {
569       if (toApp) NGBufferedDescriptor_free(toApp);
570       ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
571                    "couldn't transfer HTTP req body to app server (%i bytes)",
572                    contentLength);
573       return 500;
574     }
575     NGBufferedDescriptor_flush(toApp);
576   }
577   else {
578     if (HEAVY_LOG) {
579       ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
580                    "no content in request to transfer");
581     }
582   }
583   
584   /* read response line */
585   
586   if (!NGScanResponseLine(toApp, NULL, &statusCode, NULL)) {
587     if (toApp) NGBufferedDescriptor_free(toApp);
588     ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
589                  "error during reading of response line ..");
590     return 500;
591   }
592   r->status      = statusCode;
593   r->status_line = NULL;
594
595   /* process response headers */
596   {
597     apr_table_t *headers = NULL;
598     
599     if (HEAVY_LOG)
600       ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, "scan headers");
601
602     if ((headers = NGScanHeaders(r->pool, toApp)) == NULL) {
603       ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
604                    "error during parsing of response headers ..");
605     }
606     
607     _copyHeadersToRequest(r, headers, &contentLength);
608 #ifdef AP_VERSION_1
609     ap_send_http_header(r);
610 #endif
611   }
612   
613   /* send response content */
614   
615   if (!r->header_only) {
616     if (contentLength > 0) {
617       void *buffer = NULL;
618       
619       if ((buffer = apr_pcalloc(r->pool, contentLength + 1)) == NULL) {
620         ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
621                      "could not allocate response buffer (size=%i)",
622                      contentLength);
623       }
624
625       // read whole response
626       if (!NGBufferedDescriptor_safeRead(toApp, buffer, contentLength)) {
627         if (toApp) NGBufferedDescriptor_free(toApp);
628       }
629
630       // close connection to app
631       if (toApp) {
632         NGBufferedDescriptor_free(toApp);
633         toApp = NULL;
634       }
635
636       ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
637                    "send response (size=%i)",
638                    contentLength);
639       // send response to client
640       ap_rwrite(buffer, contentLength, r);
641       ap_rflush(r);
642     }
643     else if (contentLength == 0) {
644       // no content length header, read until EOF
645       unsigned char buffer[4096];
646       int result = 0;
647       int writeCount = 0;
648
649       do {
650         result = NGBufferedDescriptor_read(toApp, buffer, sizeof(buffer));
651         if (result > 0) {
652           ap_rwrite(buffer, result, r);
653           ap_rflush(r);
654           writeCount += result;
655         }
656       }
657       while (result > 0);
658
659       if (HEAVY_LOG && (writeCount > 0)) {
660         ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
661                      "write %i bytes (without content-length header)",
662                      writeCount);
663       }
664     }
665   }
666   
667   return OK;
668 }
669
670 #if WITH_LOGGING
671 static void test(void) {
672   fprintf(stderr,
673           "%s: called:\n"
674           "  app:      %s\n"
675           "  uri:      %s\n"
676           "  pathinfo: %s\n"
677           "  method:   %s\n"
678           "  protocol: %s\n"
679           "  1st:      %s\n"
680           "  host:     %s\n"
681           "  type:     %s\n"
682           "  handler:  %s\n",
683           __PRETTY_FUNCTION__,
684           appName,
685           r->uri,
686           r->path_info,
687           r->method,
688           r->protocol,
689           r->the_request,
690           apr_table_get(r->headers_in, "content-length"),
691           r->content_type,
692           r->handler
693           );
694
695   _logTable("  out", r->headers_out);
696   _logTable("  err", r->err_headers_out);
697   _logTable("  env", r->subprocess_env);
698   _logTable("  in",  r->headers_in);
699 }
700
701 static void _logTable(const char *text, apr_table_t *table) {
702   apr_array_header_t *array;
703   apr_table_entry_t  *entries;
704   int          i;
705
706   if (table == NULL) {
707     fprintf(stderr, "%s: log NULL table.\n", text);
708     return;
709   }
710
711   array   = apr_table_elts(table);
712   entries = (apr_table_entry_t *)array->elts;
713
714   if (array->nelts == 0) {
715     fprintf(stderr, "%s: empty\n", text);
716     return;
717   }
718
719   for (i = 0; i < array->nelts; i++) {
720     apr_table_entry_t *entry = &(entries[i]);
721     
722     fprintf(stderr, "%s: %s: %s\n", text, entry->key, entry->val);
723   }
724 }
725 #endif