KLone APIs | Modules | Data Structures | 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.68 2009/07/25 12:45:17 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/os.h>
00022 #include <klone/server.h>
00023 #include <klone/context.h>
00024 #include <klone/broker.h>
00025 #include <klone/request.h>
00026 #include <klone/ses_prv.h>
00027 #include <klone/response.h>
00028 #include <klone/backend.h>
00029 #include <klone/io.h>
00030 #include <klone/timer.h>
00031 #include <klone/tls.h>
00032 #include <klone/ses_prv.h>
00033 #include <klone/hook.h>
00034 #include <klone/hookprv.h>
00035 #include <klone/access.h>
00036 #include <klone/vhost.h>
00037 #include <klone/supplier.h>
00038 #include "http_s.h"
00039 
00040 struct http_status_map_s
00041 {
00042     int status;
00043     const char *desc;
00044 } http_status_map[] = {
00045     { HTTP_STATUS_OK                    , "OK"                      },
00046     { HTTP_STATUS_NOT_MODIFIED          , "Not Modified"            },
00047     { HTTP_STATUS_NOT_FOUND             , "Not Found"               },
00048     { HTTP_STATUS_INTERNAL_SERVER_ERROR , "Internal Server Error"   },
00049     { HTTP_STATUS_MOVED_PERMANENTLY     , "Moved Permanently"       },
00050     { HTTP_STATUS_MOVED_TEMPORARILY     , "Moved Temporarily"       },
00051     { HTTP_STATUS_CREATED               , "Created"                 },
00052     { HTTP_STATUS_ACCEPTED              , "Accepted"                },
00053     { HTTP_STATUS_NO_CONTENT            , "No Content"              },
00054     { HTTP_STATUS_BAD_REQUEST           , "Bad Request"             },
00055     { HTTP_STATUS_UNAUTHORIZED          , "Unauthorized"            },
00056     { HTTP_STATUS_FORBIDDEN             , "Forbidden"               },
00057     { HTTP_STATUS_LENGTH_REQUIRED       , "Content-Length required" },
00058     { HTTP_STATUS_REQUEST_TOO_LARGE     , "Request data too big"    },
00059     { HTTP_STATUS_NOT_IMPLEMENTED       , "Not Implemented"         },
00060     { HTTP_STATUS_BAD_GATEWAY           , "Bad Gateway"             },
00061     { HTTP_STATUS_SERVICE_UNAVAILABLE   , "Service Unavailable"     },
00062     { 0                                 , NULL                      }
00063 };
00064 
00065 enum { URI_MAX = 2048 };
00066 
00067 /* in cgi.c */
00068 int cgi_set_request(request_t *rq);
00069 
00070 session_opt_t *http_get_session_opt(http_t *http)
00071 {
00072     dbg_return_if (http == NULL, NULL);
00073 
00074     return http->sess_opt;
00075 }
00076 
00077 u_config_t *http_get_config(http_t* http)
00078 {
00079     dbg_return_if (http == NULL, NULL);
00080 
00081     return http->config;
00082 }
00083 
00084 const char *http_get_status_desc(int status)
00085 {
00086     struct http_status_map_s *map = http_status_map;
00087     const char *msg = "Unknown Status Code";
00088 
00089     for( ; map->status; ++map)
00090         if(map->status == status)
00091         {
00092             msg = map->desc;
00093             break;
00094         }
00095 
00096     return msg;
00097 }
00098 
00099 static int http_try_resolv(const char *alias, char *dst, const char *uri, 
00100         size_t sz)
00101 {
00102     static const char *WP = " \t";
00103     char *src, *res, *pp = NULL;
00104     char v[1024];
00105 
00106     dbg_err_if(dst == NULL);
00107     dbg_err_if(uri == NULL);
00108     dbg_err_if(alias == NULL);
00109 
00110     /* copy the alias in a buffer, strtok_r modifies it */
00111     dbg_err_if(strlcpy(v, alias, sizeof(v)) >= sizeof(v));
00112 
00113     /* src is the source directory */
00114     src = strtok_r(v, WP, &pp); 
00115     dbg_err_if(src == NULL);
00116 
00117     /* exit if the URI doesn't match this alias */
00118     nop_err_if(strncmp(src, uri, strlen(src)));
00119 
00120     /* if src doesn't end with a slash check that the next char in uri is a / */
00121     if(src[strlen(src)-1] != '/')
00122         nop_err_if(uri[strlen(src)] != '/');
00123 
00124     /* alias found, get the resolved prefix */
00125     res = strtok_r(NULL, WP, &pp);
00126     dbg_err_if(res == NULL);
00127 
00128     /* copy-out the resolved uri to dst */
00129     dbg_err_if(u_path_snprintf(dst, sz, '/', "%s/%s", res, uri + strlen(src)));
00130 
00131     return 0;
00132 err:
00133     return ~0;
00134 }
00135 
00136 vhost_list_t* http_get_vhost_list(http_t *http)
00137 {
00138     dbg_err_if(http == NULL);
00139 
00140     return http->vhosts;
00141 err:
00142     return NULL;
00143 }
00144 
00145 vhost_t* http_get_vhost(http_t *h, request_t *rq)
00146 {
00147     const char *host;
00148     char *p, hostcp[128];
00149     vhost_t *vh = NULL;
00150 
00151     dbg_err_if (h == NULL);
00152     dbg_err_if (rq == NULL);
00153 
00154     if((vh = request_get_vhost(rq)) != NULL)
00155         return vh; /* cached */
00156 
00157     if((host = request_get_field_value(rq, "Host")) != NULL)
00158     {
00159         dbg_err_if(strlcpy(hostcp, host, sizeof(hostcp)) >= sizeof(hostcp));
00160 
00161         /* remove :port part */   
00162         if((p = strrchr(hostcp, ':')) != NULL)
00163             *p = 0;
00164 
00165         vh = vhost_list_get(h->vhosts, hostcp);
00166     }
00167 
00168     if(vh == NULL)
00169     {
00170         /* get the default vhost */
00171         vh = vhost_list_get_n(h->vhosts, 0);
00172         dbg_err_if(vh == NULL);
00173     }
00174 
00175     return vh;
00176 err:
00177     return NULL;
00178 }
00179 
00180 int http_alias_resolv(http_t *h, request_t *rq, char *dst, const char *uri, 
00181         size_t sz)
00182 {
00183     u_config_t *config, *cgi;
00184     vhost_t *vhost;
00185     int i;
00186 
00187     dbg_err_if (h == NULL);
00188     dbg_err_if (dst == NULL);
00189     dbg_err_if (uri == NULL);
00190 
00191     dbg_err_if((vhost = http_get_vhost(h, rq)) == NULL);
00192 
00193     /* for each dir_alias config item */
00194     for(i = 0; !u_config_get_subkey_nth(vhost->config,"dir_alias", i, &config); 
00195         ++i)
00196     {
00197         if(!http_try_resolv(u_config_get_value(config), dst, uri, sz))
00198             return 0;   /* alias found, uri resolved */
00199     }
00200 
00201     /* if there's a cgi tree also try to resolv script_alias rules */
00202     if(!u_config_get_subkey(vhost->config, "cgi", &cgi))
00203     {
00204         for(i = 0; !u_config_get_subkey_nth(cgi, "script_alias", i, &config);
00205             ++i)
00206         {
00207             if(!http_try_resolv(u_config_get_value(config), dst, uri, sz))
00208                 return 0;   /* alias found, uri resolved */
00209         }
00210     }
00211 
00212     /* alias not found, prepend dir_root to the uri */
00213     dbg_err_if(u_path_snprintf(dst, sz, '/', "%s/%s", vhost->dir_root, uri));
00214 
00215     return 0;
00216 err:
00217     return ~0;
00218 }
00219 
00220 static int http_is_valid_uri(request_t *rq, const char *buf, size_t len)
00221 {
00222     char resolved[U_FILENAME_MAX], uri[URI_MAX];
00223     http_t *h = NULL;
00224 
00225     dbg_err_if (rq == NULL);
00226     dbg_err_if (buf == NULL);
00227 
00228     dbg_err_if (len >= URI_MAX);
00229 
00230     h = request_get_http(rq);
00231     dbg_err_if (h == NULL);
00232     
00233     strncpy(uri, buf, len);
00234     uri[len] = 0;
00235 
00236     /* try the url itself */
00237     if(broker_is_valid_uri(h->broker, h, rq, uri, strlen(uri)))
00238         return 1;
00239 
00240     /* try the path-resolved url */
00241     dbg_err_if(http_alias_resolv(h, rq, resolved, uri, U_FILENAME_MAX));
00242 
00243     return broker_is_valid_uri(h->broker, h, rq, resolved, strlen(resolved));
00244 err:
00245     return 0; /* error, not a valid uri */
00246 }
00247 
00248 static int http_resolv_request(http_t *h, request_t *rq)
00249 {
00250     const char *cstr;
00251     char resolved[U_FILENAME_MAX];
00252 
00253     dbg_err_if(h == NULL);
00254     dbg_err_if(rq == NULL);
00255     
00256     /* unalias rq->filename */
00257     if((cstr = request_get_filename(rq)) != NULL)
00258     {
00259         dbg_err_if(http_alias_resolv(h, rq, resolved, cstr, U_FILENAME_MAX));
00260 
00261         dbg_err_if(request_set_resolved_filename(rq, resolved));
00262     }
00263 
00264     /* unalias rq->path_info */
00265     if((cstr = request_get_path_info(rq)) != NULL)
00266     {
00267         dbg_err_if(http_alias_resolv(h, rq, resolved, cstr, U_FILENAME_MAX));
00268 
00269         dbg_err_if(request_set_resolved_path_info(rq, resolved));
00270     }
00271 
00272     return 0;
00273 err:
00274     return ~0;
00275 }
00276 
00277 static int http_is_valid_index(http_t *h, request_t *rq, const char *uri)
00278 {
00279     char resolved[U_FILENAME_MAX] = { 0 };
00280 
00281     dbg_err_if(u_path_snprintf(resolved, U_FILENAME_MAX, '/', "%s/%s", 
00282             request_get_resolved_filename(rq), uri));
00283 
00284     if(broker_is_valid_uri(h->broker, h, rq, resolved, strlen(resolved)))
00285         return 1; /* index found */
00286 
00287 err:
00288     return 0; /* index not found */
00289 }
00290 
00291 static int http_get_config_index(http_t *h, request_t *rq, char *idx, size_t sz)
00292 {
00293     vhost_t *vhost;
00294     char buf[256], *tok, *src, *pp = NULL;
00295     const char *cindex = NULL;
00296 
00297     dbg_err_if (h == NULL);
00298     dbg_err_if (rq == NULL);
00299 
00300     dbg_err_if((vhost = http_get_vhost(h, rq)) == NULL);
00301 
00302     if((cindex = u_config_get_subkey_value(vhost->config, "index")) == NULL)
00303         return ~0; /* index config key missing */
00304 
00305     /* copy the string (u_tokenize will modify it) */
00306     dbg_err_if(strlcpy(buf, cindex, sizeof(buf)) >= sizeof(buf));
00307 
00308     for(src = buf; (tok = strtok_r(src, " \t", &pp)) != NULL; src = NULL)
00309     {
00310         if(!strcmp(tok, ""))
00311             continue; 
00312 
00313         if(http_is_valid_index(h, rq, tok))
00314         {
00315             dbg_err_if(strlcpy(idx, tok, sz) >= sz);
00316             return 0; /* index page found */
00317         }
00318     }
00319 
00320     /* fall through */
00321 err:
00322     return ~0;
00323 }
00324 
00325 static int http_get_default_index(http_t *h, request_t *rq, char *cindex, 
00326         size_t sz)
00327 {
00328     static const char *indexes[] = { "/index.klone", "/index.kl1",
00329         "/index.klc", "/index.klx", "/index.html", "/index.htm", NULL };
00330     const char **pg;
00331 
00332     dbg_err_if (h == NULL);
00333     dbg_err_if (rq == NULL);
00334 
00335     /* try to find an index page between default index uris */
00336     for(pg = indexes; *pg; ++pg)
00337     {
00338         if(http_is_valid_index(h, rq, *pg))
00339         {
00340             dbg_err_if(strlcpy(cindex, *pg, sz) >= sz);
00341             return 0; /* index page found */
00342         }
00343     }
00344 
00345     /* fall through */
00346 err:
00347     return ~0;
00348 }
00349 
00350 static int http_set_index_request(http_t *h, request_t *rq)
00351 {
00352     char idx[128], uri[1024];
00353 
00354     dbg_err_if (h == NULL);
00355     dbg_err_if (rq == NULL);
00356 
00357     /* find an index page; try first config options then static index names */
00358     nop_err_if(http_get_config_index(h, rq, idx, sizeof(idx)) &&
00359             http_get_default_index(h, rq, idx, sizeof(idx)));
00360 
00361     dbg_err_if(u_snprintf(uri, sizeof(uri), "%s%s", 
00362                 request_get_filename(rq), idx));
00363 
00364     dbg_if(request_set_filename(rq, uri));
00365 
00366     dbg_err_if(http_resolv_request(h, rq));
00367 
00368     return 0;
00369 err:
00370     return ~0;
00371 }
00372 
00373 static int http_add_default_header(http_t *h, request_t *rq, response_t *rs)
00374 {
00375     vhost_t *vhost;
00376     time_t now;
00377 
00378     dbg_err_if (h == NULL);
00379     dbg_err_if (rs == NULL);
00380     
00381     dbg_err_if((vhost = http_get_vhost(h, rq)) == NULL);
00382 
00383     /* set server signature */
00384     dbg_err_if(response_set_field(rs, "Server", vhost->server_sig));
00385 
00386     now = time(NULL);
00387     dbg_err_if(response_set_date(rs, now));
00388 
00389     return 0;
00390 err:
00391     return ~0;
00392 }
00393 
00394 static int http_print_error_page(http_t *h, request_t *rq, response_t *rs, 
00395     int http_status)
00396 {
00397     enum { BUFSZ = 64 };
00398     const char *err_page;
00399     char buf[BUFSZ];
00400     vhost_t *vhost;
00401 
00402     dbg_err_if (h == NULL);
00403     dbg_err_if (rq == NULL);
00404     dbg_err_if (rs == NULL);
00405     dbg_err_if (http_status == 0);
00406     
00407     /* clean dirty header fields (not for redirects) */
00408     if(http_status != 302)
00409         dbg_err_if(header_clear(response_get_header(rs)));
00410 
00411     /* add default header fields */
00412     dbg_err_if(http_add_default_header(h, rq, rs));
00413 
00414     /* disable page caching */
00415     dbg_err_if(response_disable_caching(rs));
00416 
00417     /* looking for user provided error page */
00418     dbg_err_if(u_snprintf(buf, BUFSZ, "error.%d", http_status));
00419     if((vhost = http_get_vhost(h, rq)) == NULL)
00420         err_page = u_config_get_subkey_value(h->config, buf);
00421     else
00422         err_page = u_config_get_subkey_value(vhost->config, buf);
00423 
00424     if(err_page && !request_set_uri(rq, err_page, NULL, NULL))
00425     {
00426         dbg_err_if(http_resolv_request(h, rq));
00427         if(http_is_valid_uri(rq, err_page, strlen(err_page)))
00428         {
00429             /* user provided error page found */
00430             broker_serve(h->broker, h, rq, rs);
00431             return 0;
00432         }
00433 
00434         /* page not found */
00435         warn("%d handler page (%s) not found", http_status, err_page);
00436     }
00437 
00438     /* be sure that the status code is properly set */
00439     response_set_status(rs, http_status);
00440 
00441     response_print_header(rs);
00442 
00443     if(request_get_method(rq) == HM_HEAD)
00444         return 0; /* just the header is requested */
00445 
00446     /* print default error page */
00447     dbg_err_if(io_printf(response_io(rs), 
00448         "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
00449         "<html><head><title>%d %s</title></head>\n"
00450         "<body><h1>%s</h1><p>URL: %s</p><hr>"
00451         "<address>KLone/%s web server - www.koanlogic.com</address>"
00452         "</body></html>", 
00453         http_status, http_get_status_desc(http_status), 
00454         http_get_status_desc(http_status), 
00455         (request_get_uri(rq) ? request_get_uri(rq) : ""),
00456         KLONE_VERSION
00457         ) < 0);
00458 
00459     return 0;
00460 err:
00461     return ~0;
00462 }
00463 
00464 static int http_serve(http_t *h, int fd)
00465 {
00466     request_t *rq = NULL;
00467     response_t *rs = NULL;
00468     io_t *in = NULL, *out = NULL;
00469     int cgi = 0, port, rc = HTTP_STATUS_INTERNAL_SERVER_ERROR;
00470     const char *gwi = NULL, *cstr;
00471     talarm_t *al = NULL;
00472     kaddr_t *addr;
00473     vhost_t *vhost;
00474     struct sockaddr sa;
00475     socklen_t sasz;
00476     char *uri, nuri[URI_MAX];
00477     supplier_t *sup;
00478 
00479     u_unused_args(al);
00480 
00481     dbg_err_if (h == NULL);
00482     dbg_err_if (fd < 0);
00483     
00484     if(fd == 0 && (gwi = getenv("GATEWAY_INTERFACE")) != NULL)
00485         cgi++; /* klone is being used as a CGI */
00486 
00487     /* create a request object */
00488     dbg_err_if(request_create(h, &rq));
00489     request_set_cgi(rq, cgi);
00490 
00491     /* save local and peer address into the request object */
00492     dbg_err_if(addr_create(&addr));
00493 
00494     if(cgi)
00495     {
00496         if(getenv("REMOTE_ADDR") && getenv("REMOTE_PORT"))
00497         {
00498             port = atoi(getenv("REMOTE_PORT"));
00499             dbg_err_if(addr_set(addr, getenv("REMOTE_ADDR"), port));
00500             dbg_err_if(request_set_addr(rq, addr));
00501         }
00502 
00503         if(getenv("SERVER_ADDR"))
00504         {
00505             if(getenv("SERVER_PORT"))
00506                 port = atoi(getenv("SERVER_PORT"));
00507             else
00508                 port = 80;
00509             dbg_err_if(addr_set(addr, getenv("SERVER_ADDR"), port));
00510             dbg_err_if(request_set_peer_addr(rq, addr));
00511         }
00512     } else {
00513         /* set local addr */
00514         sasz = sizeof(struct sockaddr);
00515         dbg_err_if(getsockname(fd, &sa, &sasz));
00516         dbg_err_if(addr_set_from_sa(addr, &sa, sasz));
00517         dbg_err_if(request_set_addr(rq, addr));
00518 
00519         /* set peer addr */
00520         sasz = sizeof(struct sockaddr);
00521         dbg_err_if(getpeername(fd, &sa, &sasz));
00522         dbg_err_if(addr_set_from_sa(addr, &sa, sasz));
00523         dbg_err_if(request_set_peer_addr(rq, addr));
00524     }
00525 
00526     addr_free(addr);
00527     addr = NULL;
00528 
00529 #ifdef HAVE_LIBOPENSSL
00530     /* create input io buffer */
00531     if(h->ssl && !cgi)
00532         dbg_err_if(io_ssl_create(fd, IO_FD_CLOSE, 0, h->ssl_ctx, &in));
00533     else
00534         dbg_err_if(io_fd_create(fd, IO_FD_CLOSE, &in));
00535 #else
00536     /* create input io buffer */
00537     dbg_err_if(io_fd_create(fd, IO_FD_CLOSE, &in));
00538 #endif
00539 
00540     /* bind the request object to the 'in' io_t */
00541     dbg_err_if(request_bind(rq, in));
00542     in = NULL; 
00543 
00544     /* create a response object */
00545     dbg_err_if(response_create(h, &rs));
00546 
00547     response_set_cgi(rs, cgi);
00548 
00549     if(cgi)
00550         dbg_err_if(cgi_set_request(rq));
00551 
00552     /* create the output io_t */
00553     if(cgi)
00554         dbg_err_if(io_fd_create((cgi ? 1 : dup(fd)), IO_FD_CLOSE, &out));
00555     else {
00556         /* create the response io_t dup'ping the request io_t object */
00557         dbg_err_if(io_dup(request_io(rq), &out));
00558     }
00559 
00560     /* default method used if we cannot parse the request (bad request) */
00561     response_set_method(rs, HM_GET);
00562 
00563     /* bind the response to the connection c */
00564     dbg_err_if(response_bind(rs, out));
00565     out = NULL;
00566 
00567     /* server ready, parse the request */
00568     dbg_err_if(response_set_status(rs, HTTP_STATUS_BAD_REQUEST));
00569     rc = HTTP_STATUS_BAD_REQUEST;
00570 
00571     /* parse request. may fail on timeout */
00572     dbg_err_if(request_parse_header(rq, http_is_valid_uri, rq));
00573 
00574     response_set_method(rs, request_get_method(rq));
00575 
00576     /* get and cache the vhost ptr to speed up next lookups */
00577     dbg_err_if((vhost = http_get_vhost(h, rq)) == NULL);
00578     request_set_vhost(rq, vhost);
00579 
00580     /* if we're running in server mode then resolv aliases and dir_root */
00581     dbg_err_if(http_resolv_request(h, rq));
00582 
00583     /* if the uri end with a slash then return an index page */
00584     request_get_sup_info(rq, &sup, NULL, NULL);
00585     if(sup == NULL && (cstr = request_get_filename(rq)) != NULL && 
00586             cstr[strlen(cstr)-1] == '/')
00587         dbg_if(http_set_index_request(h, rq)); /* set the index page */
00588 
00589     /* add default header fields */
00590     dbg_err_if(http_add_default_header(h, rq, rs));
00591 
00592     /* set default successfull status code */
00593     dbg_err_if(response_set_status(rs, HTTP_STATUS_OK));
00594 
00595     /* serve the page; on error write out a simple error page */
00596     rc = broker_serve(h->broker, h, rq, rs);
00597 
00598     /* on 404 (file not found) try to find out if this is a directory request 
00599        i.e. http://site:port/dir redirects to /dir/ */
00600     if(response_get_status(rs) == 404 && (uri = request_get_uri(rq)) != NULL &&
00601             uri[strlen(uri)-1] != '/')
00602     {
00603         if(!http_set_index_request(h, rq))
00604         {
00605             strlcpy(nuri, request_get_uri(rq), sizeof(nuri));
00606             strlcat(nuri, "/", sizeof(nuri));
00607 
00608             if(request_get_path_info(rq))
00609                 strlcat(nuri, request_get_path_info(rq), sizeof(nuri));
00610 
00611             if(request_get_query_string(rq))
00612             {
00613                 strlcat(nuri, "?", sizeof(nuri));
00614                 strlcat(nuri, request_get_query_string(rq), sizeof(nuri));
00615             }
00616 
00617             response_redirect(rs, nuri);
00618             rc = HTTP_STATUS_MOVED_TEMPORARILY;
00619         }
00620     }
00621 
00622     /* log the request */
00623     if(vhost->klog)
00624         dbg_if(access_log(h, vhost->al_config, rq, rs));
00625 
00626     /* call the hook that fires on each request */
00627     hook_call(request, rq, rs);
00628 
00629     /* on broker_serve error jump to err */
00630     nop_err_if(rc != 0);
00631 
00632     /* page successfully served */
00633 
00634     request_free(rq);
00635     response_free(rs); /* must be free'd after the request object because
00636                           the rsfilter references the response object during
00637                           the flush of the codec (so the response object must
00638                           not be free'd) that happens during the io_free call */
00639     return 0;
00640 err:
00641     /* hook get fired also on error */
00642     if(rq && rs)
00643         hook_call(request, rq, rs);
00644 
00645     if(rc && rq && rs && response_io(rs))
00646         http_print_error_page(h, rq, rs, rc); /* print the error page */
00647     if(in)
00648         io_free(in);
00649     if(out)
00650         io_free(out);
00651     if(rq)
00652         request_free(rq);
00653     if(rs)
00654         response_free(rs);
00655     return ~0;
00656 }
00657 
00658 static int http_free(http_t *h)
00659 {
00660     dbg_return_if (h == NULL, 0);   /* it's ok */
00661 
00662     if(h->broker)
00663         broker_free(h->broker);
00664 
00665     if(h->vhosts)
00666         vhost_list_free(h->vhosts);
00667 
00668     U_FREE(h);
00669 
00670     return 0;
00671 }
00672 
00673 static int http_add_vhost(http_t *http, const char *host, u_config_t *c)
00674 {
00675     vhost_t *top, *vhost = NULL;
00676     u_config_t *child;
00677     const char *v;
00678 
00679     dbg_err_if (http == NULL);
00680     dbg_err_if (host == NULL);
00681     dbg_err_if (c == NULL);
00682 
00683     dbg_err_if(vhost_create(&vhost));
00684     
00685     vhost->host = host;
00686     vhost->config = c;
00687     vhost->http = http;
00688 
00689     /* set defaults */
00690     vhost->server_sig = "klone/" KLONE_VERSION;
00691     vhost->dir_root = "";
00692     vhost->index = NULL;
00693     vhost->send_enc_deflate = 0; 
00694 
00695     /* if there's a per-vhost access_log open it, otherwise inherit from the
00696        main server configuration */
00697     if((child = u_config_get_child(c, "access_log")) != NULL)
00698     {
00699         v = u_config_get_value(child);
00700 
00701         /* if the access_log key is not "no" then load the log configuration */
00702         if(v == NULL || strcasecmp(v, "no"))
00703             dbg_err_if(klog_open_from_config(child, &vhost->klog));
00704 
00705         vhost->al_config = child;
00706     } else {
00707         /* if there's a global access log use it */
00708         if((top = vhost_list_get_n(http->vhosts, 0)) != NULL)
00709         {
00710             /* inherit from the main config (may be NULL) */
00711             vhost->klog = top->klog;
00712             vhost->al_config = top->al_config;
00713         }
00714     }
00715 
00716     /* send_enc_deflate (disable if not configured) */
00717     dbg_err_if(u_config_get_subkey_value_b(c, "send_enc_deflate", 0, 
00718         &vhost->send_enc_deflate));
00719 
00720     /* server signature */
00721     if((v = u_config_get_subkey_value(c, "server_sig")) != NULL)
00722         vhost->server_sig = v;
00723 
00724     /* html dir root */
00725     if((v = u_config_get_subkey_value(c, "dir_root")) != NULL)
00726         vhost->dir_root = v;
00727     else
00728         crit_err("dir_root must be set (vhost: %s)", vhost->host);
00729 
00730     /* index page */
00731     if((v = u_config_get_subkey_value(c, "index")) != NULL)
00732         vhost->index = v;
00733 
00734     dbg_err_if(vhost_list_add(http->vhosts, vhost));
00735 
00736     return 0;
00737 err:
00738     if(vhost)
00739         vhost_free(vhost);
00740     return ~0;
00741 }
00742 
00743 static int config_inherit(u_config_t *dst, u_config_t *from)
00744 {
00745     static const char *dont_inherit[] = {
00746         "addr", "model", "type", "dir_root", "dir_alias", "script_alias", 
00747         "access_log", NULL
00748     };
00749     u_config_t *config, *child = NULL;
00750     const char **di, *key, *value;
00751     int n;
00752 
00753     dbg_err_if (dst == NULL);
00754     dbg_err_if (from == NULL);
00755 
00756     for(n = 0; (config = u_config_get_child_n(from, NULL, n)); ++n)
00757     {
00758         if(u_config_get_child(config, "dir_root"))
00759             continue; /* skip vhost config subtree */
00760         
00761         key = u_config_get_key(config);
00762         value = u_config_get_value(config);
00763 
00764         /* don't inherit keys listed in dont_inherit array */
00765         for(di = dont_inherit; *di; ++di)
00766             if(strcasecmp(*di, key) == 0)
00767                 goto next;
00768 
00769         dbg_err_if(u_config_add_child(dst, key, &child));
00770         dbg_err_if(u_config_set_value(child, value));
00771 
00772         dbg_err_if(config_inherit(child, config));
00773 
00774     next:;
00775     }
00776 
00777     return 0;
00778 err:
00779     return ~0;
00780 }
00781 
00782 static int http_set_vhost_list(http_t *http)
00783 {
00784     u_config_t *config;
00785     int n;
00786 
00787     dbg_err_if (http == NULL);
00788 
00789     /* virtual vhost that stores the main server config */
00790     dbg_err_if(http_add_vhost(http, "", http->config));
00791 
00792     /* look for vhosts (any key that contain a dir_root subkey is a vhost) */
00793     for(n = 0; (config = u_config_get_child_n(http->config, NULL, n)); ++n)
00794     {
00795         if(u_config_get_child(config, "dir_root") == NULL)
00796             continue; /* it's not a vhost config branch */
00797 
00798         dbg_err_if(u_config_get_key(config) == NULL);
00799 
00800         info("configuring virtual host [%s]", u_config_get_key(config));
00801 
00802         /* inherit top-level values */
00803         dbg_err_if(config_inherit(config, http->config));
00804 
00805         dbg_err_if(http_add_vhost(http, u_config_get_key(config), config));
00806     }
00807 
00808     return 0;
00809 err:
00810     return ~0;
00811 }
00812 
00813 static int http_create(u_config_t *config, http_t **ph)
00814 {
00815     http_t *h = NULL;
00816 
00817     dbg_err_if (config == NULL);
00818     dbg_err_if (ph == NULL);
00819 
00820     h = u_zalloc(sizeof(http_t));
00821     dbg_err_if(h == NULL);
00822 
00823     h->config = config;
00824 
00825     dbg_err_if(vhost_list_create(&h->vhosts));
00826 
00827     /* init page broker (and page suppliers) */
00828     dbg_err_if(broker_create(&h->broker));
00829 
00830     /* load main server and vhosts config */
00831     dbg_err_if(http_set_vhost_list(h));
00832 
00833     /* print-out config with inherited values */
00834     if(ctx->debug > 1)
00835         u_config_print(ctx->config, 0);
00836 
00837     *ph = h;
00838 
00839     return 0;
00840 err:
00841     if(h)
00842         http_free(h);
00843     return ~0;
00844 }
00845 
00846 static int http_backend_serve(struct backend_s *be, int fd)
00847 {
00848     http_t *h;
00849     int rc;
00850 
00851     dbg_err_if (be == NULL);
00852     dbg_err_if (be->arg == NULL);
00853     dbg_err_if (fd < 0);
00854     
00855     h = (http_t *) be->arg;
00856     
00857     /* new connection accepted on http listening socket, handle it */
00858     rc = http_serve(h, fd);
00859 
00860     return rc;
00861 err:
00862     return ~0;
00863 }
00864 
00865 static int http_backend_term(struct backend_s *be)
00866 {
00867     http_t *http;
00868 
00869     dbg_return_if (be == NULL, 0);
00870     dbg_return_if (be->arg == NULL, 0);
00871 
00872     http = (http_t *) be->arg;
00873 
00874     dbg_err_if(session_module_term(http->sess_opt));
00875 
00876     http_free(http);
00877 
00878     return 0;
00879 err:
00880     return ~0;
00881 }
00882 
00883 static int http_backend_init(struct backend_s *be)
00884 {
00885     http_t *http = NULL;
00886     broker_t *broker = NULL;
00887 
00888     dbg_err_if (be == NULL);
00889  
00890     dbg_err_if(http_create(be->config, &http));
00891 
00892     be->arg = http;
00893 
00894     dbg_err_if(session_module_init(http->config, &http->sess_opt));
00895 
00896     return 0;
00897 err:
00898     if(http)
00899         http_free(http);
00900     if(broker)
00901         broker_free(broker);
00902     return ~0;
00903 }
00904 
00905 #ifdef HAVE_LIBOPENSSL
00906 static int https_backend_init(struct backend_s *be)
00907 {
00908     http_t *https;
00909 
00910     dbg_err_if (be == NULL);
00911 
00912     dbg_err_if(http_backend_init(be));
00913 
00914     https = (http_t *) be->arg;
00915 
00916     /* turn on SSL encryption */
00917     https->ssl = 1;
00918 
00919     /* load config values and set SSL_CTX accordingly */
00920     https->ssl_ctx = tls_load_init_ctx(http_get_config(https));
00921     warn_err_ifm (https->ssl_ctx == NULL, "bad or missing HTTPS credentials");
00922 
00923     dbg_err_if(session_module_init(https->config, &https->sess_opt));
00924 
00925     return 0;
00926 err:
00927     return ~0;
00928 }
00929 
00930 static int https_backend_term(struct backend_s *be)
00931 {
00932     http_t *https;
00933 
00934     dbg_err_if (be == NULL);
00935 
00936     https = (http_t *) be->arg;
00937     if (https == NULL)
00938         return 0;
00939 
00940     SSL_CTX_free(https->ssl_ctx);
00941 
00942     return http_backend_term(be); 
00943 err:
00944     return ~0;
00945 }
00946 
00947 /* same http functions but different '_init' */
00948 backend_t be_https =
00949     BACKEND_STATIC_INITIALIZER( "https", 
00950         https_backend_init, 
00951         http_backend_serve, 
00952         https_backend_term );
00953 #endif /* HAVE_LIBOPENSSL */
00954 
00955 backend_t be_http =
00956     BACKEND_STATIC_INITIALIZER( "http", 
00957         http_backend_init, 
00958         http_backend_serve, 
00959         http_backend_term );
00960