Main Page | Modules | File List | Globals

http.c

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