2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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
11 SOPE 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.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
29 static void _logTable(const char *text, apr_table_t *table);
32 static ngobjweb_dir_config *_getConfig(request_rec *r) {
33 ngobjweb_dir_config *cfg;
36 fprintf(stderr, "%s: missing request !\n", __PRETTY_FUNCTION__);
39 if (r->per_dir_config == NULL) {
40 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
41 "missing directory config in request ...");
45 cfg = (ngobjweb_dir_config *)
46 ap_get_module_config(r->per_dir_config, &ngobjweb_module);
51 static void _extractAppName(const char *uri, char *appName, int maxLen) {
54 /* extract name of application */
55 if ((tmp = index(uri + 1, '/'))) {
57 len = (tmp - (uri + 1));
58 strncpy(appName, (uri + 1), len);
62 strncpy(appName, (uri + 1), maxLen - 1);
63 appName[maxLen - 1] = '\0';
66 /* cut off .woa extension from application name */
67 if ((tmp = strstr(appName, ".woa")))
70 /* cut off .sky extension from application name */
71 if ((tmp = strstr(appName, ".sky")))
75 static void *_readRequestBody(request_rec *r, int *requestContentLength) {
79 int readBytes, toBeRead;
82 clen = apr_table_get(r->headers_in, "content-length");
83 contentLength = clen ? atoi(clen) : 0;
84 *requestContentLength = contentLength;
86 /* no content to read ... */
87 if (contentLength == 0) return NULL;
92 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
93 "going to read %i bytes from browser ...", contentLength);
96 requestBody = apr_palloc(r->pool, contentLength + 2);
99 for (toBeRead = contentLength; toBeRead > 0;) {
101 readBytes = ap_bread(r->connection->client, ptr, toBeRead);
103 ap_setup_client_block(r,REQUEST_CHUNKED_DECHUNK);
104 readBytes = ap_get_client_block(r, ptr, toBeRead);
106 toBeRead -= readBytes;
108 if (readBytes == 0) break;
113 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
114 "couldn't read complete HTTP req body from browser "
115 "(read %i of %i bytes)",
116 (contentLength - toBeRead), contentLength);
124 _copyHeadersToRequest(request_rec *r, apr_table_t *headers, int *contentLength)
126 const apr_array_header_t *array;
127 apr_table_entry_t *entries;
131 if (headers == NULL) return;
133 value = apr_table_get(headers, "content-type");
134 if (value) r->content_type = value;
135 value = apr_table_get(headers, "content-encoding");
136 if (value) r->content_encoding = value;
137 value = apr_table_get(headers, "content-length");
138 *contentLength = value ? atoi(value) : 0;
140 array = apr_table_elts(headers);
141 entries = (apr_table_entry_t *)array->elts;
143 for (i = 0; i < array->nelts; i++) {
144 apr_table_entry_t *entry = &(entries[i]);
146 apr_table_set(r->headers_out, entry->key, entry->val);
148 // _logTable("out", r->headers_out);
151 static void _logInstanceAddress(request_rec *r, struct sockaddr *address,
152 size_t addressLen, int domain)
156 if (!HEAVY_LOG) return;
158 apr_snprintf(buf, sizeof(buf), " => address len=%li domain=%i<", (long int) addressLen, domain);
160 case AF_INET: strcat(buf, "inet"); break;
161 case AF_UNIX: strcat(buf, "unix"); break;
162 default: strcat(buf, "unknown"); break;
166 if (domain == AF_UNIX) {
167 strcat(buf, " path=\"");
168 strcat(buf, ((struct sockaddr_un *)address)->sun_path);
171 else if (domain == AF_INET) {
176 ptr = inet_ntoa(((struct sockaddr_in *)address)->sin_addr);
177 port = ntohs(((struct sockaddr_in *)address)->sin_port);
178 apr_snprintf(sport, sizeof(sport), "host=\"%s\" port=%i", ptr, port);
182 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, buf);
185 static int _connectInstance(request_rec *r,
186 int appFd, struct sockaddr *address,
191 char isConnected = 0;
193 result = connect(appFd, address, addressLen);
194 if (result >= 0) return result;
196 while (tryCount < 3) {
197 char *pdelay = NULL; /* pblock_findval("delay", _paras) */
198 int delay = pdelay ? atoi(pdelay) : 3; // default: 3s
200 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
201 "sleeping %is ..", delay);
203 apr_sleep(delay); /* should be in seconds for Apache 1? */
205 apr_sleep(delay * 1000 * 1000 /* in microseconds now! */);
208 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
210 result = connect(appFd, address, addressLen);
219 if (isConnected == 0) {
220 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
221 "connect to application instance failed, tried %i times.",
229 static int _writeInHeaders(NGBufferedDescriptor *toApp, request_rec *r) {
230 const apr_array_header_t *array;
231 apr_table_entry_t *entries;
234 if (r->headers_in == NULL) return 1;
236 array = apr_table_elts(r->headers_in);
237 entries = (apr_table_entry_t *)array->elts;
239 for (i = 0; i < array->nelts; i++) {
240 apr_table_entry_t *entry = &(entries[i]);
242 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
243 entry->key, entry->val)) {
250 int ngobjweb_handler(request_rec *r) {
251 struct sockaddr *address = NULL;
255 NGBufferedDescriptor *toApp = NULL;
259 int contentLength = 0;
260 int statusCode = 500;
261 ngobjweb_dir_config *cfg;
263 unsigned requestContentLength;
267 requestContentLength = 0;
271 if (r->handler == NULL)
273 if (strcmp(r->handler, "ngobjweb-adaptor") != 0)
277 if (uri == NULL) return DECLINED;
278 if (uri[0] != '/') return DECLINED;
279 if (strstr(uri, "WebServerResources")) return DECLINED;
281 /* get directory configuration */
283 if ((cfg = _getConfig(r))) {
284 if (cfg->appPrefix) {
286 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
287 "using prefix '%s'\n", cfg->appPrefix);
289 uri += strlen(cfg->appPrefix);
296 /* find app name in url */
297 _extractAppName(uri, appName, sizeof(appName));
299 /* before continuing, read request body */
301 requestBody = _readRequestBody(r, &contentLength);
302 requestContentLength = contentLength;
304 if ((requestBody == NULL) && (contentLength > 0))
305 /* read failed, error is logged in function */
308 /* ask SNS for server address */
311 address = _sendSNSQuery(r,
313 apr_table_get(r->headers_in, "cookie"),
314 &domain, &addressLen,
317 if (address == NULL) {
318 /* did not find an appropriate application server */
319 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
320 "did not find SOPE instance using SNS.");
324 else if (cfg->appPort) {
325 domain = cfg->appPortDomain;
327 if (cfg->appPortDomain == AF_UNIX) {
328 addressLen = sizeof(struct sockaddr_un);
329 address = apr_palloc(r->pool, sizeof(struct sockaddr_un));
330 memset(address, 0, sizeof(struct sockaddr_un));
332 ((struct sockaddr_un *)address)->sun_family = AF_UNIX;
333 strncpy(((struct sockaddr_un *)address)->sun_path,
335 sizeof(((struct sockaddr_un *)address)->sun_path) - 1);
338 struct sockaddr_in *snsi;
343 port = atoi(cfg->appPort);
345 //ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
346 // "appPort: '%s', cfg 0x%08X", cfg->appPort, cfg);
348 addressLen = sizeof(struct sockaddr_in);
349 address = apr_palloc(r->pool, sizeof(struct sockaddr_in));
350 memset(address, 0, sizeof(struct sockaddr_in));
351 snsi = (struct sockaddr_in *)address;
353 snsi->sin_addr.s_addr = apr_inet_addr(host);
355 snsi->sin_family = AF_INET;
356 snsi->sin_port = htons((short)(port & 0xFFFF));
358 if (snsi->sin_addr.s_addr == -1) {
359 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
360 "couldn't convert snsd IP address: %s", host);
362 if (HEAVY_LOG && 0) {
363 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
364 "connect IP address: %s", host);
369 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
370 "neither SNS port nor app port are set for request ...");
374 if (addressLen > 10000) {
375 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
376 "suspect instance port length (%li) ...",
377 (long int) addressLen);
381 _logInstanceAddress(r, address, addressLen, domain);
383 /* setup connection to application server */
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);
391 if ((result = _connectInstance(r, appFd, address, addressLen)) < 0)
394 toApp = NGBufferedDescriptor_newWithOwnedDescriptorAndSize(appFd, 512);
397 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
398 "could not alloc socket buffer for "
399 "application server connection");
403 /* write request to application server */
406 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
414 reqLine = r->the_request;
415 toGo = reqLine ? strlen(reqLine) : 0;
417 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
418 "req is %s(len=%i)", reqLine, toGo);
420 if (!NGBufferedDescriptor_safeWrite(toApp, reqLine,
421 reqLine ? strlen(reqLine) : 0)) {
423 goto writeErrorHandler;
425 if (!NGBufferedDescriptor_safeWrite(toApp, "\r\n", 2)) {
427 goto writeErrorHandler;
431 /* transfer headers */
433 if (writeError == 0) {
435 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
439 /* extended adaptor headers */
445 value = (value != NULL) ? value : "http";
446 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
447 "x-webobjects-server-protocol",
450 goto writeErrorHandler;
453 if ((value = r->connection->remote_ip) != NULL) {
454 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
455 "x-webobjects-remote-addr",
458 goto writeErrorHandler;
462 value = r->connection->remote_host;
463 if (value == NULL) value = r->connection->remote_ip;
465 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
466 "x-webobjects-remote-host",
469 goto writeErrorHandler;
474 if ((value = r->connection->ap_auth_type) != NULL) {
476 if ((value = r->ap_auth_type) != NULL) {
478 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
479 "x-webobjects-auth-type",
482 goto writeErrorHandler;
487 if ((value = r->connection->user) != NULL) {
489 if ((value = r->user) != NULL) {
491 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
492 "x-webobjects-remote-user",
495 goto writeErrorHandler;
500 if (cfg->appPrefix != NULL) {
501 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
502 "x-webobjects-adaptor-prefix", cfg->appPrefix)) {
504 goto writeErrorHandler;
509 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
510 "x-webobjects-server-name",
511 r->server->server_hostname)) {
513 goto writeErrorHandler;
516 if (r->server->port != 0) {
517 apr_snprintf(tmp, sizeof(tmp), "%i", r->server->port);
518 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
519 "x-webobjects-server-port",
522 goto writeErrorHandler;
526 // TODO: this seems to be broken with some Apache's!
527 // see: http://www.mail-archive.com/modssl-users@modssl.org/msg16396.html
528 if (r->server->port != 0) {
529 apr_snprintf(tmp, sizeof(tmp), "%s://%s:%i",
531 r->server->server_hostname,
535 apr_snprintf(tmp, sizeof(tmp), "%s://%s",
536 ap_http_method(r), r->server->server_hostname);
538 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
539 "x-webobjects-server-url",
542 goto writeErrorHandler;
545 /* SSL environment */
547 if (r->subprocess_env != NULL) {
548 apr_table_t *env = r->subprocess_env;
551 s = apr_table_get(env, "HTTPS");
552 if (s != NULL && strncasecmp(s, "on", 2) == 0) { // SSL is one
553 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
554 "x-webobjects-https-enabled",
557 goto writeErrorHandler;
561 s = apr_table_get(env, "SSL_CLIENT_CERT");
563 const apr_array_header_t *array;
564 apr_table_entry_t *entries;
567 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
568 "x-webobjects-clients-cert",
571 goto writeErrorHandler;
574 /* deliver all SSL_CLIENT_ env-vars as headers */
575 array = apr_table_elts(env);
576 entries = (apr_table_entry_t *)array->elts;
577 for (i = 0; i < array->nelts; i++) {
578 apr_table_entry_t *entry = &(entries[i]);
580 if (strncmp(entry->key, "SSL_CLIENT_", 11) != 0)
582 if (strncmp(entry->key, "SSL_CLIENT_CERT", 15) == 0)
583 continue; /* already avail as x-webobjects-clients-cert" */
585 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
589 goto writeErrorHandler;
594 /* keysize, don't know whether mapping is correct? */
595 if ((s = apr_table_get(env, "SSL_CIPHER_ALGKEYSIZE")) != NULL) {
596 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
597 "x-webobjects-https-secret-keysize",
600 goto writeErrorHandler;
603 if ((s = apr_table_get(env, "SSL_CIPHER_USEKEYSIZE")) != NULL) {
604 if (!NGBufferedDescriptor_writeHttpHeader(toApp,
605 "x-webobjects-https-keysize",
608 goto writeErrorHandler;
615 if (!_writeInHeaders(toApp, r)) {
617 goto writeErrorHandler;
620 if (!NGBufferedDescriptor_safeWrite(toApp, "\r\n", 2)) {
622 goto writeErrorHandler;
624 if (!NGBufferedDescriptor_flush(toApp))
629 if (writeError == 1) {
630 if (toApp) NGBufferedDescriptor_free(toApp);
632 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
633 "socket write error during transfer of HTTP header section");
637 /* transfer request body */
639 if (requestContentLength > 0) {
640 if (!NGBufferedDescriptor_safeWrite(toApp,
642 requestContentLength)) {
643 if (toApp) NGBufferedDescriptor_free(toApp);
644 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
645 "couldn't transfer HTTP req body to app server (%i bytes)",
649 NGBufferedDescriptor_flush(toApp);
653 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
654 "no content in request to transfer");
658 /* read response line */
660 if (!NGScanResponseLine(toApp, NULL, &statusCode, NULL)) {
661 if (toApp) NGBufferedDescriptor_free(toApp);
662 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
663 "error during reading of response line ..");
666 r->status = statusCode;
667 r->status_line = NULL;
669 /* process response headers */
671 apr_table_t *headers = NULL;
674 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server, "scan headers");
676 if ((headers = NGScanHeaders(r->pool, toApp)) == NULL) {
677 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
678 "error during parsing of response headers ..");
681 _copyHeadersToRequest(r, headers, &contentLength);
683 ap_send_http_header(r);
687 /* send response content */
689 if (!r->header_only) {
690 if (contentLength > 0) {
693 if ((buffer = apr_pcalloc(r->pool, contentLength + 1)) == NULL) {
694 ap_log_error(__FILE__, __LINE__, APLOG_ERR, 0, r->server,
695 "could not allocate response buffer (size=%i)",
699 // read whole response
700 if (!NGBufferedDescriptor_safeRead(toApp, buffer, contentLength)) {
701 if (toApp) NGBufferedDescriptor_free(toApp);
704 // close connection to app
706 NGBufferedDescriptor_free(toApp);
710 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
711 "send response (size=%i)",
713 // send response to client
714 ap_rwrite(buffer, contentLength, r);
717 else if (contentLength == 0) {
718 // no content length header, read until EOF
719 unsigned char buffer[4096];
724 result = NGBufferedDescriptor_read(toApp, buffer, sizeof(buffer));
726 ap_rwrite(buffer, result, r);
728 writeCount += result;
733 if (HEAVY_LOG && (writeCount > 0)) {
734 ap_log_error(__FILE__, __LINE__, APLOG_INFO, 0, r->server,
735 "write %i bytes (without content-length header)",
746 static void test(void) {
765 apr_table_get(r->headers_in, "content-length"),
770 _logTable(" out", r->headers_out);
771 _logTable(" err", r->err_headers_out);
772 _logTable(" env", r->subprocess_env);
773 _logTable(" in", r->headers_in);
777 static void _logTable(const char *text, apr_table_t *table) {
778 const apr_array_header_t *array;
779 apr_table_entry_t *entries;
783 fprintf(stderr, "%s: log NULL table.\n", text);
787 array = apr_table_elts(table);
788 entries = (apr_table_entry_t *)array->elts;
790 if (array->nelts == 0) {
791 fprintf(stderr, "%s: empty\n", text);
795 for (i = 0; i < array->nelts; i++) {
796 apr_table_entry_t *entry = &(entries[i]);
798 fprintf(stderr, "%s: %s: %s\n", text, entry->key, entry->val);