Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

http.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: http.c,v 1.42 2006/05/27 16:34:01 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <sys/types.h>
00013 #include <stdlib.h>
00014 #include <unistd.h>
00015 #ifdef HAVE_LIBOPENSSL
00016 #include <openssl/ssl.h>
00017 #include <openssl/err.h>
00018 #endif  /* HAVE_LIBOPENSSL */
00019 #include <u/libu.h>
00020 #include <klone/utils.h>
00021 #include <klone/klone.h>
00022 #include <klone/server.h>
00023 #include <klone/broker.h>
00024 #include <klone/request.h>
00025 #include <klone/ses_prv.h>
00026 #include <klone/response.h>
00027 #include <klone/backend.h>
00028 #include <klone/io.h>
00029 #include <klone/timer.h>
00030 #include <klone/tls.h>
00031 #include <klone/ses_prv.h>
00032 #include "http_s.h"
00033 
00034 struct http_status_map_s
00035 {
00036     int status;
00037     const char *desc;
00038 } http_status_map[] = {
00039     { HTTP_STATUS_OK                    , "OK"                      },
00040     { HTTP_STATUS_NOT_MODIFIED          , "Not Modified"            },
00041     { HTTP_STATUS_NOT_FOUND             , "Not Found"               },
00042     { HTTP_STATUS_INTERNAL_SERVER_ERROR , "Internal Server Error"   },
00043     { HTTP_STATUS_MOVED_PERMANENTLY     , "Moved Permanently"       },
00044     { HTTP_STATUS_MOVED_TEMPORARILY     , "Moved Temporarily"       },
00045     { HTTP_STATUS_CREATED               , "Created"                 },
00046     { HTTP_STATUS_ACCEPTED              , "Accepted"                },
00047     { HTTP_STATUS_NO_CONTENT            , "No Content"              },
00048     { HTTP_STATUS_BAD_REQUEST           , "Bad Request"             },
00049     { HTTP_STATUS_UNAUTHORIZED          , "Unauthorized"            },
00050     { HTTP_STATUS_FORBIDDEN             , "Forbidden"               },
00051     { HTTP_STATUS_LENGTH_REQUIRED       , "Content-Length required" },
00052     { HTTP_STATUS_REQUEST_TOO_LARGE     , "Request data too big"    },
00053     { HTTP_STATUS_NOT_IMPLEMENTED       , "Not Implemented"         },
00054     { HTTP_STATUS_BAD_GATEWAY           , "Bad Gateway"             },
00055     { HTTP_STATUS_SERVICE_UNAVAILABLE   , "Service Unavailable"     },
00056     { 0                                 , NULL                      }
00057 };
00058 
00059 /* in cgi.c */
00060 int cgi_set_request(request_t *rq);
00061 
00062 session_opt_t *http_get_session_opt(http_t *http)
00063 {
00064     dbg_return_if (http == NULL, NULL);
00065 
00066     return http->sess_opt;
00067 }
00068 
00069 u_config_t *http_get_config(http_t* http)
00070 {
00071     dbg_return_if (http == NULL, NULL);
00072 
00073     return http->config;
00074 }
00075 
00076 const char *http_get_status_desc(int status)
00077 {
00078     struct http_status_map_s *map = http_status_map;
00079     const char *msg = "Unknown Status Code";
00080 
00081     for( ; map->status; ++map)
00082         if(map->status == status)
00083         {
00084             msg = map->desc;
00085             break;
00086         }
00087 
00088     return msg;
00089 }
00090 
00091 int http_alias_resolv(http_t *h, char *dst, const char *filename, size_t sz)
00092 {
00093     static const char *WP = " \t";
00094     u_config_t *config;
00095     int i;
00096     const char *value;
00097     char *src, *res, *v = NULL,*pp = NULL;
00098 
00099     dbg_err_if (h == NULL);
00100     dbg_err_if (dst == NULL);
00101     dbg_err_if (filename == NULL);
00102 
00103     /* for each dir_alias config item */
00104     for(i = 0; !u_config_get_subkey_nth(h->config, "dir_alias", i, &config); 
00105         ++i)
00106     {
00107         if((value = u_config_get_value(config)) == NULL)
00108             continue; /* empty key */
00109 
00110         /* otherwise strtok_r will modify 'value' */
00111         v = u_strdup(value);
00112         dbg_err_if(v == NULL);
00113 
00114         src = strtok_r(v, WP, &pp); 
00115         dbg_err_if(src == NULL);
00116 
00117         if(strncmp(src, filename, strlen(src)) == 0)
00118         {
00119             /* alias found, get resolved prefix */
00120             res = strtok_r(NULL, WP, &pp);
00121             dbg_err_if(res == NULL);
00122 
00123             dbg_err_if(u_path_snprintf(dst, sz, '/', "%s/%s", res, 
00124                         filename + strlen(src)));
00125 
00126             U_FREE(v); 
00127             return 0;
00128         }
00129 
00130         U_FREE(v);
00131     }
00132 
00133     /* prepend dir_root */
00134     dbg_err_if(u_path_snprintf(dst, sz, '/', "%s/%s", h->dir_root, filename));
00135 
00136     return 0;
00137 err:
00138     U_FREE(v);
00139     return ~0;
00140 }
00141 
00142 static int http_is_valid_uri(void *arg, const char *buf, size_t len)
00143 {
00144     enum { URI_MAX = 2048 };
00145     char resolved[U_FILENAME_MAX], uri[URI_MAX];
00146     http_t *h = (http_t*)arg;
00147 
00148     dbg_err_if (arg == NULL);
00149     dbg_err_if (buf == NULL);
00150     
00151     strncpy(uri, buf, len);
00152     uri[len] = 0;
00153 
00154     dbg_err_if(http_alias_resolv(h, resolved, uri, URI_MAX));
00155 
00156     return broker_is_valid_uri(h->broker, resolved, strlen(resolved));
00157 err:
00158     return ~0;
00159 }
00160 
00161 static void http_resolv_request(http_t *h, request_t *rq)
00162 {
00163     const char *cstr;
00164     char resolved[U_FILENAME_MAX];
00165 
00166     dbg_ifb(h == NULL) return;
00167     dbg_ifb(rq == NULL) return;
00168     
00169     /* unalias rq->filename */
00170     cstr = request_get_filename(rq);
00171     if(cstr && !http_alias_resolv(h, resolved, cstr, U_FILENAME_MAX))
00172         request_set_resolved_filename(rq, resolved);
00173 
00174     /* unalias rq->path_info */
00175     cstr = request_get_path_info(rq);
00176     if(cstr && !http_alias_resolv(h, resolved, cstr, U_FILENAME_MAX))
00177         request_set_resolved_path_info(rq, resolved);
00178 }
00179 
00180 static int http_set_index_request(http_t *h, request_t *rq)
00181 {
00182     static const char *indexes[] = { "/index.klone", "/index.kl1",
00183         "/index.html", "/index.htm", NULL };
00184     const char **pg;
00185     char resolved[U_FILENAME_MAX];
00186 
00187     dbg_err_if (h == NULL);
00188     dbg_err_if (rq == NULL);
00189 
00190     /* user provided index page list (FIXME add list support) */
00191     if(h->index == NULL)
00192     {   
00193         /* try to find an index page between default index uris */
00194         for(pg = indexes; *pg; ++pg)
00195         {
00196             resolved[0] = 0;  /* for valgrind's happyness */
00197             dbg_err_if(u_path_snprintf(resolved, U_FILENAME_MAX, '/', "%s/%s", 
00198                     request_get_resolved_filename(rq), *pg));
00199 
00200             if(broker_is_valid_uri(h->broker, resolved, strlen(resolved)))
00201             {
00202                 /* a valid index uri has been found; rewrite request */
00203                 request_set_filename(rq, *pg);
00204                 break;
00205             }
00206         }
00207         if(*pg == NULL) /* no index found, set index.html (will return 404 ) */
00208             dbg_if(request_set_filename(rq, "/index.html"));
00209     } else
00210         dbg_if(request_set_filename(rq, h->index));
00211 
00212     http_resolv_request(h, rq);
00213 
00214     return 0;
00215 err:
00216     return ~0;
00217 }
00218 
00219 static int http_add_default_header(http_t *h, response_t *rs)
00220 {
00221     time_t now;
00222 
00223     dbg_err_if (h == NULL);
00224     dbg_err_if (rs == NULL);
00225     
00226     /* set server signature */
00227     dbg_err_if(response_set_field(rs, "Server", h->server_sig));
00228 
00229     now = time(NULL);
00230     dbg_err_if(response_set_date(rs, now));
00231 
00232     return 0;
00233 err:
00234     return ~0;
00235 }
00236 
00237 static int http_print_error_page(http_t *h, request_t *rq, response_t *rs, 
00238     int http_status)
00239 {
00240     enum { BUFSZ = 64 };
00241     const char *err_page;
00242     char buf[BUFSZ];
00243     int rc;
00244 
00245     dbg_err_if (h == NULL);
00246     dbg_err_if (rq == NULL);
00247     dbg_err_if (rs == NULL);
00248     dbg_err_if (http_status == 0);
00249     
00250     /* clean dirty header fields (not for redirects) */
00251     if(http_status != 302)
00252         dbg_err_if(header_clear(response_get_header(rs)));
00253 
00254     /* add default header fields */
00255     dbg_err_if(http_add_default_header(h, rs));
00256 
00257     /* disable page caching */
00258     dbg_err_if(response_disable_caching(rs));
00259 
00260     /* looking for user provided error page */
00261     dbg_err_if(u_snprintf(buf, BUFSZ, "error.%d", http_status));
00262     err_page = u_config_get_subkey_value(h->config, buf);
00263 
00264     if(err_page && !request_set_uri(rq, err_page, NULL, NULL))
00265     {
00266         http_resolv_request(h, rq);
00267         if((rc = broker_serve(h->broker, rq, rs)) == 0)
00268             return 0; 
00269         else {
00270             /* configured error page not found */
00271             http_status = rc;
00272         }
00273     }
00274 
00275     /* be sure that the status code is properly set */
00276     response_set_status(rs, http_status);
00277 
00278     response_print_header(rs);
00279 
00280     if(request_get_method(rq) == HM_HEAD)
00281         return 0; /* just the header is requested */
00282 
00283     /* print default error page */
00284     dbg_err_if(io_printf(response_io(rs), 
00285         "<html><head><title>%d %s</title></head>\n"
00286         "<body><h1>%s</h1><p>URL: %s</body></html>", 
00287         http_status, http_get_status_desc(http_status), 
00288         http_get_status_desc(http_status), 
00289         (request_get_uri(rq) ? request_get_uri(rq) : "") ) < 0);
00290 
00291     return 0;
00292 err:
00293     return ~0;
00294 }
00295 
00296 static int http_serve(http_t *h, int fd)
00297 {
00298     request_t *rq = NULL;
00299     response_t *rs = NULL;
00300     io_t *in = NULL, *out = NULL;
00301     int cgi = 0, port;
00302     const char *gwi = NULL;
00303     talarm_t *al = NULL;
00304     addr_t *addr;
00305     struct sockaddr sa;
00306     int sasz, rc = HTTP_STATUS_INTERNAL_SERVER_ERROR;
00307 
00308     dbg_err_if (h == NULL);
00309     dbg_err_if (fd < 0);
00310     
00311     if(fd == 0 && (gwi = getenv("GATEWAY_INTERFACE")) != NULL)
00312         cgi++;
00313 
00314     /* create a request object */
00315     dbg_err_if(request_create(h, &rq));
00316     request_set_cgi(rq, cgi);
00317 
00318     /* save local and peer address into the request object */
00319     dbg_err_if(addr_create(&addr));
00320 
00321     if(cgi)
00322     {
00323         if(getenv("REMOTE_ADDR") && getenv("REMOTE_PORT"))
00324         {
00325             port = atoi(getenv("REMOTE_PORT"));
00326             dbg_err_if(addr_set(addr, getenv("REMOTE_ADDR"), port));
00327             dbg_err_if(request_set_addr(rq, addr));
00328         }
00329 
00330         if(getenv("SERVER_ADDR"))
00331         {
00332             if(getenv("SERVER_PORT"))
00333                 port = atoi(getenv("SERVER_PORT"));
00334             else
00335                 port = 80;
00336             dbg_err_if(addr_set(addr, getenv("SERVER_ADDR"), port));
00337             dbg_err_if(request_set_peer_addr(rq, addr));
00338         }
00339     } else {
00340         /* set local addr */
00341         sasz = sizeof(struct sockaddr);
00342         dbg_err_if(getsockname(fd, &sa, &sasz));
00343         dbg_err_if(addr_set_from_sa(addr, &sa, sasz));
00344         dbg_err_if(request_set_addr(rq, addr));
00345 
00346         /* set peer addr */
00347         sasz = sizeof(struct sockaddr);
00348         dbg_err_if(getpeername(fd, &sa, &sasz));
00349         dbg_err_if(addr_set_from_sa(addr, &sa, sasz));
00350         dbg_err_if(request_set_peer_addr(rq, addr));
00351     }
00352 
00353     addr_free(addr);
00354     addr = NULL;
00355 
00356 #ifdef HAVE_LIBOPENSSL
00357     /* create input io buffer (no IO_FD_CLOSE used because 'out' 
00358        will close it */
00359     if(h->ssl && !cgi)
00360         dbg_err_if(io_ssl_create(fd, IO_FD_CLOSE, h->ssl_ctx, &in));
00361     else
00362         dbg_err_if(io_fd_create(fd, IO_FD_CLOSE, &in));
00363 #else
00364     /* create input io buffer */
00365     dbg_err_if(io_fd_create(fd, IO_FD_CLOSE, &in));
00366 #endif
00367 
00368     /* bind the request object to the 'in' io_t */
00369     dbg_err_if(request_bind(rq, in));
00370     in = NULL; 
00371 
00372     /* create a response object */
00373     dbg_err_if(response_create(h, &rs));
00374 
00375     response_set_cgi(rs, cgi);
00376 
00377     if(cgi)
00378         dbg_err_if(cgi_set_request(rq));
00379 
00380     /* create the output io_t */
00381     if(cgi)
00382         dbg_err_if(io_fd_create((cgi ? 1 : fd), IO_FD_CLOSE, &out));
00383     else
00384         /* create the response io_t dup'ping the request io_t object */
00385         dbg_err_if(io_dup(request_io(rq), &out));
00386 
00387     /* default method used if we cannot parse the request (bad request) */
00388     response_set_method(rs, HM_GET);
00389 
00390     /* bind the response to the connection c */
00391     dbg_err_if(response_bind(rs, out));
00392     out = NULL;
00393 
00394     dbg_err_if(rc = request_parse_header(rq, http_is_valid_uri, h));
00395 
00396     response_set_method(rs, request_get_method(rq));
00397 
00398     /* if we're running in server mode then resolv aliases and dir_root */
00399     http_resolv_request(h, rq);
00400 
00401     /* if / is requested then return one of index.{klone,kl1,html,htm} */
00402     if(strcmp(request_get_filename(rq), "/") == 0)
00403         dbg_err_if(http_set_index_request(h, rq)); /* set the index page */
00404 
00405     /* add default header fields */
00406     dbg_err_if(http_add_default_header(h, rs));
00407 
00408     /* set default successfull status code */
00409     dbg_err_if(response_set_status(rs, HTTP_STATUS_OK));
00410 
00411     /* serve the page; on error write out a simple error page */
00412     dbg_err_if(rc = broker_serve(h->broker, rq, rs));
00413 
00414     /* page successfully served */
00415 
00416     request_free(rq);
00417     response_free(rs); /* must be free'd after the request object because
00418                           the rsfilter references the response object during
00419                           the flush of the codec (so the response object must
00420                           not be free'd) that happens during the io_free call */
00421     return 0;
00422 err:
00423     if(rc && rq && rs && response_io(rs))
00424         http_print_error_page(h, rq, rs, rc); /* print the error page */
00425     if(in)
00426         io_free(in);
00427     if(out)
00428         io_free(out);
00429     if(rq)
00430         request_free(rq);
00431     if(rs)
00432         response_free(rs);
00433     return ~0;
00434 }
00435 
00436 static int http_free(http_t *h)
00437 {
00438     dbg_return_if (h == NULL, 0);   /* it's ok */
00439 
00440     if(h->broker)
00441         broker_free(h->broker);
00442 
00443     U_FREE(h);
00444 
00445     return 0;
00446 }
00447 
00448 static int http_set_config_opt(http_t *http)
00449 {
00450     u_config_t *c = http->config;
00451     const char *v;
00452 
00453     dbg_err_if (http == NULL);
00454     
00455     /* defaults */
00456     http->server_sig = "klone/" KLONE_VERSION;
00457     http->dir_root = "";
00458     http->index = NULL;
00459     http->send_enc_deflate = 0; 
00460 
00461     /* send_enc_deflate (disable if not configured) */
00462     dbg_err_if(u_config_get_subkey_value_b(c, "send_enc_deflate", 0, 
00463         &http->send_enc_deflate));
00464 
00465     /* server signature */
00466     if((v = u_config_get_subkey_value(c, "server_sig")) != NULL)
00467         http->server_sig = v;
00468 
00469     /* html dir root */
00470     if((v = u_config_get_subkey_value(c, "dir_root")) != NULL)
00471         http->dir_root = v;
00472 
00473     /* index page */
00474     if((v = u_config_get_subkey_value(c, "index")) != NULL)
00475         http->index = v;
00476 
00477     return 0;
00478 err:
00479     return ~0;
00480 }
00481 
00482 
00483 static int http_create(u_config_t *config, http_t **ph)
00484 {
00485     http_t *h = NULL;
00486 
00487     dbg_err_if (config == NULL);
00488     dbg_err_if (ph == NULL);
00489 
00490     h = u_zalloc(sizeof(http_t));
00491     dbg_err_if(h == NULL);
00492 
00493     h->config = config;
00494 
00495     /* init page broker (and page suppliers) */
00496     dbg_err_if(broker_create(&h->broker));
00497 
00498     /* set http struct config opt reading from http->config */
00499     dbg_err_if(http_set_config_opt(h));
00500 
00501     *ph = h;
00502 
00503     return 0;
00504 err:
00505     if(h)
00506         http_free(h);
00507     return ~0;
00508 }
00509 
00510 int http_backend_serve(struct backend_s *be, int fd)
00511 {
00512     http_t *h;
00513     int rc;
00514 
00515     dbg_err_if (be == NULL);
00516     dbg_err_if (be->arg == NULL);
00517     dbg_err_if (fd < 0);
00518     
00519     h = (http_t *) be->arg;
00520     
00521     /* new connection accepted on http listening socket, handle it */
00522     dbg_if((rc = http_serve(h, fd)) != 0);
00523 
00524     return rc;
00525 err:
00526     return ~0;
00527 }
00528 
00529 int http_backend_term(struct backend_s *be)
00530 {
00531     http_t *http;
00532 
00533     dbg_return_if (be == NULL, 0);
00534     dbg_return_if (be->arg == NULL, 0);
00535 
00536     http = (http_t *) be->arg;
00537 
00538     dbg_err_if(session_module_term(http->sess_opt));
00539 
00540     http_free(http);
00541 
00542     return 0;
00543 err:
00544     return ~0;
00545 }
00546 
00547 int http_backend_init(struct backend_s *be)
00548 {
00549     http_t *http = NULL;
00550     broker_t *broker = NULL;
00551 
00552     dbg_err_if (be == NULL);
00553  
00554     dbg_err_if(http_create(be->config, &http));
00555 
00556     be->arg = http;
00557 
00558     dbg_err_if(session_module_init(http->config, &http->sess_opt));
00559 
00560     return 0;
00561 err:
00562     if(http)
00563         http_free(http);
00564     if(broker)
00565         broker_free(broker);
00566     return ~0;
00567 }
00568 
00569 #ifdef HAVE_LIBOPENSSL
00570 int https_backend_init(struct backend_s *be)
00571 {
00572     http_t *https;
00573     tls_ctx_args_t *cargs;
00574 
00575     dbg_err_if (be == NULL);
00576 
00577     dbg_err_if(http_backend_init(be));
00578 
00579     https = (http_t *) be->arg;
00580 
00581     /* turn on SSL encryption */
00582     https->ssl = 1;
00583 
00584     /* load config values and set SSL_CTX accordingly */
00585     dbg_err_if (tls_load_ctx_args(http_get_config(https), &cargs));
00586     warn_err_ifm (!(https->ssl_ctx = tls_init_ctx(cargs)), 
00587         "bad or missing HTTPS credentials");
00588 
00589     dbg_err_if(session_module_init(https->config, &https->sess_opt));
00590 
00591     return 0;
00592 err:
00593     return ~0;
00594 }
00595 
00596 int https_backend_term(struct backend_s *be)
00597 {
00598     http_t *https;
00599 
00600     dbg_err_if (be == NULL);
00601 
00602     https = (http_t *) be->arg;
00603     if (https == NULL)
00604         return 0;
00605 
00606     SSL_CTX_free(https->ssl_ctx);
00607 
00608     return http_backend_term(be); 
00609 err:
00610     return ~0;
00611 }
00612 
00613 /* same http functions but different '_init' */
00614 backend_t be_https =
00615     BACKEND_STATIC_INITIALIZER( "https", 
00616         https_backend_init, 
00617         http_backend_serve, 
00618         https_backend_term );
00619 #endif /* HAVE_LIBOPENSSL */
00620 
00621 backend_t be_http =
00622     BACKEND_STATIC_INITIALIZER( "http", 
00623         http_backend_init, 
00624         http_backend_serve, 
00625         http_backend_term );
00626 

←Products
© 2005-2006 - KoanLogic S.r.l. - All rights reserved