http.c

Go to the documentation of this file.
00001 
00011 /* supress warning for strndup */
00012 #include "http.h"
00013 #include <string.h>
00014 
00015 #if defined(__APPLE__)
00016 #include <sys/types.h>
00017 #endif
00018 
00019 #include <errno.h>
00020 #include <stdlib.h>
00021 
00022 #ifdef CP_HAS_UNISTD_H
00023 #include <unistd.h>
00024 #endif
00025 
00026 #include <signal.h>
00027 
00028 #if defined(unix) || defined(__unix__) || defined(__MACH__)
00029 #include <sys/socket.h>
00030 #include <netinet/in.h>
00031 #include <arpa/inet.h>
00032 #endif
00033 
00034 #ifdef CP_USE_SSL
00035 #include <openssl/ssl.h>
00036 #include <openssl/err.h>
00037 #endif
00038 
00039 #ifdef CP_HAS_STRINGS_H
00040 #include <strings.h>
00041 #endif /* CP_HAS_STRINGS_H */
00042 
00043 #ifdef CP_HAS_REGEX_H
00044 #include <regex.h>
00045 #else
00046 #ifdef CP_HAS_PCRE
00047 #include <pcreposix.h>
00048 #endif /* CP_HAS_PCRE */
00049 #endif /* CP_HAS_REGEX_H */
00050 
00051 #include "hashtable.h"
00052 #include "hashlist.h"
00053 #include "trie.h"
00054 #include "vector.h"
00055 #include "log.h"
00056 #include "util.h"
00057 #include "socket.h"
00058 
00059 /****************************************************************************
00060  *                                                                          *
00061  *                    constants and initialization code                     *
00062  *                                                                          *
00063  ****************************************************************************/
00064 
00065 #ifdef __OpenBSD__
00066 #define HTTP_PARSE_BUFSIZE 0x1000
00067 #else
00068 #define HTTP_PARSE_BUFSIZE 0xFFFF
00069 #endif
00070 
00071 #define MATCHMAX 10 
00072 
00073 static char *re_request_line_str = 
00074     "^(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT)[[:space:]]+([^ ]+)[[:space:]]+HTTP/([^[:space:]]*)[[:space:]]*$";
00075 static char *re_request_header_str = 
00076     "^([[:alnum:][:punct:]]+): (.*[^[:space:]])([[:space:]]?)$";
00077 //  "^([[:alnum:][:punct:]]+): (.*[^[:space:]])([[:space:]]*)$";
00078 static char *re_request_vars_str = "([^&]+)=([^&]*[^&[:space:]])&?";
00079 
00080 static regex_t re_request_line;
00081 static regex_t re_request_header;
00082 static regex_t re_request_vars;
00083 
00084 static char *cp_http_version_lit[] = { "1.0", "1.1" };
00085 
00086 static char *connection_policy_lit[] = { "Close", "Close", "Keep-Alive" };
00087 
00088 static char *content_type_lit[] = { "text/plain", "text/html", "image/jpeg" };
00089 
00090 static char *server_error_fmt = 
00091     "<html>\n"
00092     "<head>\n"
00093     "<title> 400 BAD REQUEST </title>\n"
00094     "</head>\n"
00095     "<body>\n"
00096     "<h1> 400 BAD REQUEST: %s </h1>\n"
00097     "</body>\n"
00098     "</html>";
00099 
00100 static int cp_http_server_error_code[] =
00101     {
00102         CP_HTTP_EMPTY_REQUEST,
00103         CP_HTTP_INVALID_REQUEST_LINE,
00104         CP_HTTP_UNKNOWN_REQUEST_TYPE,
00105         CP_HTTP_INVALID_URI,
00106         CP_HTTP_VERSION_NOT_SPECIFIED,
00107         CP_HTTP_1_1_HOST_NOT_SPECIFIED,
00108         CP_HTTP_INCORRECT_REQUEST_BODY_LENGTH,
00109         -1
00110     };
00111     
00112 static char *cp_http_server_error_lit[] = 
00113     {
00114         "EMPTY REQUEST",
00115         "INVALID REQUEST LINE",
00116         "UNKNOWN REQUEST TYPE",
00117         "INVALID URI",
00118         "VERSION NOT SPECIFIED",
00119         "HTTP/1.1 HOST NOT SPECIFIED",
00120         "INCORRECT REQUEST BODY LENGTH"
00121     };
00122 
00123 static cp_hashtable *cp_http_server_error;
00124 
00125 struct cp_http_status_code_entry 
00126 {
00127     cp_http_status_code code;
00128     char *lit;
00129 } cp_http_status_code_list[] = 
00130     {
00131         { HTTP_100_CONTINUE, "100 CONTINUE" },
00132         { HTTP_101_SWITCHING_PROTOCOLS, "101 SWITCHING PROTOCOLS" },
00133         { HTTP_200_OK, "200 OK" },
00134         { HTTP_201_CREATED, "201 CREATED" },
00135         { HTTP_202_ACCEPTED, "202 ACCEPTED" },
00136         { HTTP_203_NON_AUTHORITATIVE_INFORMATION, "203 NON AUTHORITATIVE INFORMATION" },
00137         { HTTP_204_NO_CONTENT, "204 NO CONTENT" },
00138         { HTTP_205_RESET_CONTENT, "205 RESET CONTENT" },
00139         { HTTP_206_PARTIAL_CONTENT, "206 PARTIAL CONTENT" },
00140         { HTTP_300_MULTIPLE_CHOICES, "300 MULTIPLE CHOICES" },
00141         { HTTP_301_MOVED_PERMANENTLY, "301 MOVED PERMANENTLY" },
00142         { HTTP_302_FOUND, "302 FOUND" },
00143         { HTTP_303_SEE_OTHER, "303 SEE OTHER" },
00144         { HTTP_304_NOT_MODIFIED, "304 NOT MODIFIED" },
00145         { HTTP_305_USE_PROXY, "305 USE PROXY" },
00146         { HTTP_307_TEMPORARY_REDIRECT, "307 TEMPORARY REDIRECT" },
00147         { HTTP_400_BAD_REQUEST, "400 BAD REQUEST" },
00148         { HTTP_401_UNAUTHORIZED, "401 UNAUTHORIZED" },
00149         { HTTP_402_PAYMENT_REQUIRED, "402 PAYMENT REQUIRED" },
00150         { HTTP_403_FORBIDDEN, "403 FORBIDDEN" },
00151         { HTTP_404_NOT_FOUND, "404 NOT FOUND" },
00152         { HTTP_405_METHOD_NOT_ALLOWED, "405 METHOD NOT ALLOWED" },
00153         { HTTP_406_NOT_ACCEPTABLE, "406 NOT ACCEPTABLE" },
00154         { HTTP_407_PROXY_AUTHENTICATION_REQUIRED, "407 PROXY AUTHENTICATION REQUIRED" },
00155         { HTTP_408_REQUEST_TIME_OUT, "408 REQUEST TIME OUT" },
00156         { HTTP_409_CONFLICT, "409 CONFLICT" },
00157         { HTTP_410_GONE, "410 GONE" },
00158         { HTTP_411_LENGTH_REQUIRED, "411 LENGTH REQUIRED" },
00159         { HTTP_412_PRECONDITION_FAILED, "412 PRECONDITION FAILED" },
00160         { HTTP_413_REQUEST_ENTITY_TOO_LARGE, "413 REQUEST ENTITY TOO LARGE" },
00161         { HTTP_414_REQUEST_URI_TOO_LARGE, "414 REQUEST URI TOO LARGE" },
00162         { HTTP_415_UNSUPPORTED_MEDIA_TYPE, "415 UNSUPPORTED MEDIA TYPE" },
00163         { HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE, "416 REQUESTED RANGE NOT SATISFIABLE" },
00164         { HTTP_417_EXPECTATION_FAILED, "417 EXPECTATION FAILED" },
00165         { HTTP_500_INTERNAL_SERVER_ERROR, "500 INTERNAL SERVER ERROR" },
00166         { HTTP_501_NOT_IMPLEMENTED, "501 NOT IMPLEMENTED" },
00167         { HTTP_502_BAD_GATEWAY, "502 BAD GATEWAY" },
00168         { HTTP_503_SERVICE_UNAVAILABLE, "503 SERVICE UNAVAILABLE" },
00169         { HTTP_504_GATEWAY_TIME_OUT, "504 GATEWAY TIME OUT" },
00170         { HTTP_505_HTTP_VERSION_NOT_SUPPORTED, "505 HTTP VERSION NOT SUPPORTED" },
00171         { HTTP_NULL_STATUS, NULL}
00172     };
00173 
00174 static cp_hashtable *cp_http_status_lit;
00175 
00176 static char *cp_http_request_type_lit[] = 
00177     {
00178         "OPTIONS", 
00179         "GET", 
00180         "HEAD", 
00181         "POST", 
00182         "PUT", 
00183         "DELETE", 
00184         "TRACE", 
00185         "CONNECT" 
00186     };
00187 
00188 static volatile int initialized = 0;
00189 
00190 static volatile int socket_reg_id = 0;
00191 static cp_hashlist *socket_registry = NULL;
00192 static cp_vector *shutdown_hook = NULL;
00193 
00194 void cp_http_signal_handler(int sig);
00195             
00196 void *session_cleanup_thread(void *);
00197 
00198 int cp_http_init()
00199 {
00200     int i;
00201     int rc;
00202 #if defined(unix) || defined(__unix__) || defined(__MACH__)
00203     struct sigaction act;
00204 #endif
00205 
00206     if (initialized) return initialized++;
00207     initialized = 1;
00208 
00209     cp_socket_init();
00210 
00211 #if defined(unix) || defined(__unix__) || defined(__MACH__)
00212     act.sa_handler = cp_http_signal_handler;
00213     sigemptyset(&act.sa_mask);
00214     act.sa_flags = 0;
00215     sigaction(SIGINT, &act, NULL);
00216     sigaction(SIGPIPE, &act, NULL);
00217     sigaction(SIGTERM, &act, NULL);
00218 #endif
00219 
00220     if ((rc = regcomp(&re_request_line, re_request_line_str, 
00221             REG_EXTENDED | REG_NEWLINE)))
00222         return log_regex_compilation_error(rc, "cp_http_init - request line");
00223     
00224     if ((rc = regcomp(&re_request_header, re_request_header_str, 
00225             REG_EXTENDED | REG_NEWLINE)))
00226         return log_regex_compilation_error(rc, "cp_http_init - request header");
00227 
00228     if ((rc = regcomp(&re_request_vars, re_request_vars_str, 
00229             REG_EXTENDED | REG_NEWLINE)))
00230         return log_regex_compilation_error(rc, "cp_http_init - request variables");
00231 
00232     cp_http_server_error = cp_hashtable_create(10, cp_hash_int, cp_hash_compare_int);
00233     for (i = 0; cp_http_server_error_code[i] != -1; i++)
00234         cp_hashtable_put(cp_http_server_error, 
00235                       &cp_http_server_error_code[i], 
00236                       cp_http_server_error_lit[i]);
00237 
00238     cp_http_status_lit = cp_hashtable_create(50, cp_hash_int, cp_hash_compare_int);
00239     for (i = 0; cp_http_status_code_list[i].lit != NULL; i++)
00240         cp_hashtable_put(cp_http_status_lit,
00241                       &cp_http_status_code_list[i].code, 
00242                       cp_http_status_code_list[i].lit);
00243     
00244     socket_registry = 
00245         cp_hashlist_create_by_mode(1, COLLECTION_MODE_DEEP, 
00246                                    cp_hash_int, cp_hash_compare_int);
00247 
00248 #ifdef CP_USE_HTTP_SESSIONS
00249     {
00250         cp_thread _session_cleanup_thread;
00251         cp_thread_create(_session_cleanup_thread, NULL, 
00252                          session_cleanup_thread, NULL);
00253 #ifdef CP_HAS_PTHREAD_H
00254         cp_thread_detach(_session_cleanup_thread);
00255 #endif
00256     }
00257 #endif
00258     
00259     return 0;
00260 }
00261 
00262 volatile int cp_http_stopping = 0;
00263 
00264 /* signal all sockets in select() to stop */
00265 void cp_httpsocket_stop_all()
00266 {
00267     cp_http_stopping = 1;
00268     cp_socket_stop_all();
00269 }
00270 
00271 static volatile int cp_http_shutting_down = 0;
00272 
00273 void cp_httpsocket_stop(cp_httpsocket *sock)
00274 {
00275     if (sock) sock->sock->closing = 1;
00276 }
00277 
00278 void *cp_http_add_shutdown_callback(void (*cb)(void *), void *prm)
00279 {
00280     cp_wrap *wrap;
00281     
00282     if (shutdown_hook == NULL)
00283         shutdown_hook = cp_vector_create(1);
00284     
00285     wrap = cp_wrap_new(prm, cb);
00286 
00287     cp_vector_add_element(shutdown_hook, wrap);
00288 
00289     return wrap;
00290 }
00291 
00292 void cp_http_shutdown()
00293 {
00294     if (cp_http_shutting_down) return; /* one shutdown should be enough */
00295 
00296     cp_http_shutting_down = 1;
00297 
00298 #ifdef __TRACE__
00299     DEBUGMSG("shutting down http services");
00300 #endif
00301 
00302     if (shutdown_hook)
00303     {
00304         cp_vector_destroy_custom(shutdown_hook, 
00305                                  (cp_destructor_fn) cp_wrap_delete);
00306         shutdown_hook = NULL;
00307     }
00308 
00309     if (initialized)
00310     {
00311         regfree(&re_request_line);
00312         regfree(&re_request_header);
00313         regfree(&re_request_vars);
00314     }
00315 
00316     cp_hashtable_destroy(cp_http_server_error);
00317     cp_hashtable_destroy(cp_http_status_lit);
00318     cp_hashlist_destroy_custom(socket_registry, NULL, 
00319                                (cp_destructor_fn) cp_httpsocket_stop);
00320     socket_registry = NULL;
00321 
00322     cp_socket_shutdown();
00323 
00324     initialized = 0;
00325 }
00326 
00327 void cp_http_signal_handler(int sig)
00328 {
00329     switch (sig)
00330     {
00331         case SIGINT: 
00332 #ifdef __TRACE__
00333             DEBUGMSG("SIGINT - stopping\n");
00334 #endif
00335             cp_http_shutdown();
00336             break;
00337 
00338 #ifdef SIGPIPE
00339         case SIGPIPE: 
00340 #ifdef __TRACE__
00341             DEBUGMSG("SIGPIPE - ignoring\n");
00342 #endif
00343             break;
00344 #endif /* SIGPIPE */
00345 
00346         case SIGTERM:
00347 #ifdef __TRACE__
00348             DEBUGMSG("SIGTERM - stopping\n");
00349 #endif
00350             cp_http_shutdown();
00351             break;
00352     }
00353 }
00354 
00355 #define HTTP_RE_MATCHMAX 10
00356 
00357 #ifdef CP_USE_HTTP_SESSIONS
00358 
00359 /****************************************************************************
00360  *                                                                          *
00361  *  cp_http_session_* functions:                                            *
00362  *                                                                          *
00363  *  o cp_httpsocket_create_session - creates a session [static]             *
00364  *  o cp_httpsocket_checkout_session - retrieves/creates session [static]   *
00365  *  o cp_httpsocket_remove_session - drops a session [static]               *
00366  *  o cp_http_session_delete - deletes a session [static]                   *
00367  *  o cp_http_session_inc_refcount - increase reference count               *
00368  *  o cp_http_session_dec_refcount - decrease reference count               *
00369  *  o cp_http_session_get - retrieves a value by key                        *
00370  *  o cp_http_session_set - sets a value that doesn't need to be deleted    *
00371  *  o cp_http_session_set_dtr - sets a value with a destructor callback     *
00372  *  o cp_http_session_set_validity - sets session validity time             *
00373  *  o cp_http_session_is_fresh - session is newly created                   *
00374  *                                                                          *
00375  *  client code should use cp_http_request_get_session to get a reference   *
00376  *  to the session. use cp_http_session_increase_refcount and               *
00377  *  cp_http_session_decrease_refcount only if performing special tricks.    *
00378  *                                                                          *
00379  ****************************************************************************/
00380 
00381 static cp_http_session *cp_httpsocket_create_session(cp_httpsocket *sock)
00382 {
00383     cp_http_session *session = calloc(1, sizeof(cp_http_session));
00384     if (session)
00385     {
00386         struct timeval tm;
00387         gettimeofday(&tm, NULL);
00388         session->sid = malloc(17 * sizeof(char));
00389         if (session->sid == NULL)
00390         {
00391             free(session);
00392             return NULL;
00393         }
00394         session->key = 
00395             cp_hashtable_create_by_option(COLLECTION_MODE_COPY |
00396                                           COLLECTION_MODE_DEEP,
00397                                           1,
00398                                           cp_hash_string, 
00399                                           cp_hash_compare_string,
00400                                           (cp_copy_fn) strdup, 
00401                                           (cp_destructor_fn) free,
00402                                           NULL, 
00403                                           (cp_destructor_fn) cp_wrap_delete);
00404         if (session->key == NULL)
00405         {
00406             free(session->sid);
00407             free(session);
00408             return NULL;
00409         }
00410 
00411         session->created = session->access = tm.tv_sec;
00412         session->validity = DEFAULT_SESSION_VALIDITY;
00413         session->renew_on_access = 1;
00414         session->fresh = 1;
00415         session->refcount = 1;
00416         session->valid = 1;
00417 
00418         if (sock) /* may be null if called by separate instance */
00419         {
00420             if (sock->session == NULL)
00421             {
00422                 sock->session = 
00423                     cp_hashlist_create_by_option(COLLECTION_MODE_COPY |
00424                                                  COLLECTION_MODE_DEEP,
00425                                                  1,
00426                                                  cp_hash_string,
00427                                                  cp_hash_compare_string, 
00428                                                  (cp_copy_fn) strdup,
00429                                                  (cp_destructor_fn) free,
00430                                                  NULL,
00431                                                  (cp_destructor_fn) 
00432                                                      cp_http_session_delete);
00433                 if (sock->session == NULL)
00434                 {
00435                     cp_http_session_delete(session);
00436                     return NULL;
00437                 }
00438             }
00439         //~~ although improbable (~ 1:100,000), gen_tm_id_str may return 
00440         // doubles and should be replaced with a more robust function. until
00441         // then, make sure the id isn't already in there.
00442             do 
00443             { 
00444                 gen_tm_id_str(session->sid, &tm);
00445             } while (cp_hashlist_get(sock->session, session->sid));
00446 
00447             cp_hashlist_append(sock->session, session->sid, session);
00448         }
00449         else
00450             gen_tm_id_str(session->sid, &tm); //~~ incorporate pid info
00451     }
00452 
00453     return session;
00454 }
00455 
00456 
00457 static cp_http_session *
00458     cp_httpsocket_checkout_session(cp_httpsocket *sock, char *sid)
00459 {
00460     cp_http_session *session = 
00461         sock->session ? cp_hashlist_get(sock->session, sid) : NULL;
00462 
00463     if (session)
00464         session->refcount++;
00465     //~~ else create invalid session with specified id?
00466 
00467     return session;
00468 }
00469 
00474 void cp_http_session_delete(cp_http_session *session)
00475 {
00476     if (session)
00477     {
00478         if (session->sid) free(session->sid);
00479         if (session->key) 
00480             cp_hashtable_destroy(session->key); /* calls ~cp_wrap() */
00481         free(session);
00482     }
00483 }
00484 
00485 void *cp_http_session_get(cp_http_session *session, char *key)
00486 {
00487     cp_wrap *wrap = cp_hashtable_get(session->key, key);
00488     if (wrap)
00489         return wrap->item;
00490     return NULL;
00491 }
00492 
00493 void *cp_http_session_set_dtr(cp_http_session *session, 
00494                               char *key, void *value, 
00495                               cp_destructor_fn dtr)
00496 {
00497     cp_wrap *wrap = cp_wrap_new(value, dtr);
00498     if (wrap)
00499         return cp_hashtable_put(session->key, key, wrap);
00500     return NULL;
00501 }
00502 
00503 void *cp_http_session_set(cp_http_session *session, 
00504                                  char *key, void *value)
00505 {
00506     return cp_http_session_set_dtr(session, key, value, NULL);
00507 }
00508 
00509 void cp_http_session_set_validity(cp_http_session *session, long sec)
00510 {
00511     session->validity = sec;
00512 }
00513 
00514 short cp_http_session_is_fresh(cp_http_session *session)
00515 {
00516     return session->fresh;
00517 }
00518 
00519 static char *redirect_fmt = "<html><head><title>302 FOUND</title></head><body><h1>302 FOUND</h1>The document has moved <a href=\"%s\"> here </a>.</body></html>";
00520 
00525 static cp_http_response *
00526     cp_http_new_session_redirect(cp_http_request *req, int step)
00527 {
00528     cp_http_response *res = cp_http_response_create(req);
00529     
00530     if (res)
00531     {
00532         char redirect_url[MAX_URL_LENGTH]; 
00533 #ifdef CP_USE_COOKIES
00534         char cookie_path[MAX_URL_LENGTH]; 
00535         char *cp;
00536 #endif /* CP_USE_COOKIES */
00537         char *host; 
00538         char buf[HTTP_PARSE_BUFSIZE];
00539         cp_string *content;
00540         char *protocol;
00541 
00542 #ifdef CP_USE_SSL
00543         if (req->owner->sock->use_ssl)
00544             protocol = "https";
00545         else
00546 #endif
00547         protocol = "http";
00548 
00549         if ((host = cp_http_request_get_header(req, "Host")) == NULL)
00550             host = "localhost"; //~~ figure out hostname
00551         
00552 #ifdef CP_USE_COOKIES
00553         if (step == 2)
00554 #ifdef CP_HAS_SNPRINTF
00555             snprintf(redirect_url, MAX_URL_LENGTH, 
00556                      "%s://%s%s", protocol, host, req->uri);
00557 #else
00558             sprintf(redirect_url, "%s://%s%s", protocol, host, req->uri);
00559 #endif /* CP_HAS_SNPRINTF */
00560         else
00561 #endif /* CP_USE_COOKIES */
00562 #ifdef CP_HAS_SNPRINTF
00563         snprintf(redirect_url, MAX_URL_LENGTH, "%s://%s%s?%s=%s", protocol, 
00564                  host, req->uri, CP_HTTP_SESSION_PRM, req->session->sid);
00565 #else
00566         sprintf(redirect_url, "%s://%s%s?%s=%s", protocol, host, req->uri,
00567                 CP_HTTP_SESSION_PRM, req->session->sid);
00568 #endif /* CP_HAS_SNPRINTF */
00569 
00570         cp_http_response_set_status(res, HTTP_302_FOUND);
00571         cp_http_response_set_header(res, "Location", redirect_url);
00572         cp_http_response_set_connection_policy(res, 
00573                 HTTP_CONNECTION_POLICY_KEEP_ALIVE);
00574 
00575 #ifdef CP_HAS_SNPRINTF
00576         snprintf(buf, HTTP_PARSE_BUFSIZE, redirect_fmt, redirect_url);
00577 #else
00578         sprintf(buf, redirect_fmt, redirect_url);
00579 #endif /* CP_HAS_SNPRINTF */
00580         content = cp_string_cstrdup(buf);
00581         cp_http_response_set_content(res, content);
00582 
00583 #ifdef CP_USE_COOKIES
00584         if (step == 1)
00585         {
00586             strlcpy(cookie_path, req->uri, MAX_URL_LENGTH);
00587             if ((cp = strrchr(cookie_path, '/')))
00588             {
00589                 cp++;
00590                 *cp = '\0';
00591             }
00592         
00593             cp_http_response_set_cookie(res, CP_HTTP_SESSION_PRM, 
00594                     req->session->sid, host, cookie_path, 
00595 //                  req->session->validity, 
00596                     31536000, // 1 yr == 60 * 60 * 24 * 365
00597 #ifdef CP_USE_SSL
00598                 req->owner->sock->use_ssl
00599 #else
00600                 0
00601 #endif /* CP_USE_SSL */
00602                 );
00603         }
00604 #endif /* CP_USE_COOKIES */
00605     }
00606 
00607     return res;
00608 }
00609 
00611 void *session_cleanup_thread(void *prm)
00612 {
00613     cp_hashlist_iterator i, j;
00614     cp_httpsocket *curr;
00615     cp_http_session *session;
00616     time_t now;
00617     int rc;
00618 
00619     while (!(cp_http_shutting_down || cp_http_stopping))
00620     {
00621         rc = cp_sleep(900);
00622         if (cp_http_shutting_down || cp_http_stopping) break;
00623 #ifdef __TRACE__
00624         DEBUGMSG("session_cleanup_thread running");
00625 #endif
00626         now = time(NULL);
00627         cp_hashlist_iterator_init(&i, socket_registry, COLLECTION_LOCK_READ);
00628         while ((curr = cp_hashlist_iterator_next_value(&i)))
00629         {
00630             if (curr->session == NULL) continue;
00631             cp_hashlist_iterator_init(&j, curr->session, COLLECTION_LOCK_WRITE);
00632             session = cp_hashlist_iterator_curr_value(&j);
00633             while (session != NULL)
00634             {
00635                 time_t expiration = (session->renew_on_access ? 
00636                         session->access : session->created) + session->validity;
00637                 if (now > expiration) /* expired */
00638                 {
00639                     if (session->refcount <= 0)
00640                     {
00641                         if (curr->pending)
00642                         {
00643                             cp_http_response *res = 
00644                                 cp_hashlist_remove(curr->pending, session->sid);
00645                             if (res) 
00646                                 cp_http_response_delete(res);
00647                         }
00648                         cp_hashlist_iterator_remove(&j);
00649                     }
00650                 }
00651                 session = cp_hashlist_iterator_next_value(&j);
00652             }
00653             cp_hashlist_iterator_release(&j); /* release locks */
00654         }
00655         cp_hashlist_iterator_release(&i); /* release locks here too */
00656 #ifdef __TRACE__
00657         DEBUGMSG("session_cleanup_thread - cleanup done");
00658 #endif
00659     }
00660 #ifdef __TRACE__
00661     DEBUGMSG("session_cleanup_thread exits");
00662 #endif
00663         
00664     return NULL;
00665 }
00666 #endif /* CP_USE_HTTP_SESSIONS */
00667 
00668 /****************************************************************************
00669  *                                                                          *
00670  *  cp_http_request_* functions:                                            *
00671  *                                                                          *
00672  *  o cp_http_request_delete - performs cleanup                             *
00673  *  o cp_http_request_parse - parses a request from a char buffer           *
00674  *  o cp_http_request_get_header - returns headers by name                  *
00675  *  o cp_http_request_dump - dumps request to stdout and log                *
00676  *                                                                          *
00677  ****************************************************************************/
00678 
00679 void cp_http_request_delete(cp_http_request *request)
00680 {
00681     if (request)
00682     {
00683         free(request->uri);
00684 #ifdef CP_USE_COOKIES
00685         if (request->cookie)
00686             cp_vector_destroy_custom(request->cookie, free);
00687 #endif /* CP_USE_COOKIES */
00688         if (request->header)
00689             cp_hashtable_destroy(request->header);
00690         if (request->query_string)
00691             free(request->query_string);
00692         if (request->prm)
00693             cp_hashtable_destroy(request->prm);
00694 #ifdef CP_USE_HTTP_SESSIONS
00695         if (request->session) request->session->refcount--;
00696 #endif /* CP_USE_HTTP_SESSIONS */
00697         free(request);
00698     }
00699 }
00700 
00701 char *get_http_request_type_lit(cp_http_request_type type)
00702 {
00703     return  cp_http_request_type_lit[type];
00704 }
00705 
00706 static int parse_request_line(cp_http_request *req, char *request, int *plen)
00707 {
00708     regmatch_t rm[HTTP_RE_MATCHMAX];
00709     int len;
00710     int valid;
00711 
00712     if (regexec(&re_request_line, request, HTTP_RE_MATCHMAX, rm, 0))
00713         return CP_HTTP_INVALID_REQUEST_LINE;
00714 
00715     valid = 0;
00716     len = rm[1].rm_eo - rm[1].rm_so;
00717     if (len)
00718         for (req->type = OPTIONS; req->type <= CONNECT; req->type++)
00719             if (strncasecmp(&request[rm[1].rm_so], 
00720                 cp_http_request_type_lit[req->type], len) == 0)
00721             {
00722                 valid = 1;
00723                 break;
00724             }
00725 
00726     if (!valid) /* unknown or missing method */
00727         return CP_HTTP_UNKNOWN_REQUEST_TYPE;
00728 
00729     valid = 0;
00730     len = rm[2].rm_eo - rm[2].rm_so;
00731     if (len)
00732     {
00733         char *q = strnchr(&request[rm[2].rm_so], '?', rm[0].rm_eo);
00734         if (q) 
00735         {
00736             //~~ check if this is a GET - if not, reject
00737             len = q - request;
00738             req->query_string = strndup(q + 1, rm[2].rm_eo - len - 1);
00739             len -= rm[2].rm_so;
00740         }
00741 
00742         req->uri = strndup(&request[rm[2].rm_so], len);
00743         valid = 1;
00744     }
00745     
00746     if (!valid) /* invalid uri */
00747         return CP_HTTP_INVALID_URI;
00748 
00749     valid = 0;
00750     len = rm[3].rm_eo - rm[3].rm_so;
00751 
00752     if (len)
00753     {
00754         if (strncmp(&request[rm[3].rm_so], "1.0", len) == 0)
00755         {
00756             req->version = HTTP_1_0;
00757             valid = 1;
00758         }
00759         else if (strncmp(&request[rm[3].rm_so], "1.1", len) == 0)
00760         {
00761             req->version = HTTP_1_1;
00762             valid = 1;
00763         }
00764     }
00765 
00766     if (!valid) /* HTTP version not specified */
00767         return CP_HTTP_VERSION_NOT_SPECIFIED;
00768 
00769     *plen = rm[0].rm_eo;
00770 
00771     return 0;
00772 }
00773 
00774 #define REQUEST_STAGE_START        0
00775 #define REQUEST_STAGE_STATUS_LINE  1
00776 #define REQUEST_STAGE_HEADERS      2
00777 #define REQUEST_STAGE_BODY         3
00778 #define REQUEST_STAGE_DONE         4
00779 
00780 static int is_empty_line(char *c)
00781 {
00782     return strncmp(c, "\r\n\r\n", 4) == 0 || strncmp(c, "\n\n", 2) == 0;
00783 }
00784 
00785 static void skip_eol(char **c)
00786 {
00787     if (**c == '\r') (*c)++;
00788     if (**c == '\n') (*c)++;
00789     if (**c == '\r') (*c)++;
00790     if (**c == '\n') (*c)++;
00791 }
00792 
00793 static int 
00794     parse_headers(cp_http_request *req, char *request, int *plen, int *stage)
00795 {
00796     char *curr = request;
00797     regmatch_t rm[MATCHMAX];
00798     int len;
00799     char *name, *value;
00800     char *eol;
00801             
00802     if (req->header == NULL)
00803         req->header = 
00804             cp_hashtable_create_by_option(COLLECTION_MODE_NOSYNC | 
00805                                           COLLECTION_MODE_DEEP, 10,
00806                                           cp_hash_string, 
00807                                           cp_hash_compare_string, 
00808                                           NULL, free, NULL, free);
00809             
00810     while (regexec(&re_request_header, curr, MATCHMAX, rm, 0) == 0)
00811     {
00812         len = rm[1].rm_eo - rm[1].rm_so;
00813         if ((name = (char *) malloc((len + 1) * sizeof(char))) == NULL) 
00814             return CP_MEMORY_ALLOCATION_FAILURE;
00815         strncpy(name, curr + rm[1].rm_so, len);
00816         name[len] = '\0';
00817 
00818         len = rm[2].rm_eo - rm[2].rm_so;
00819         if ((value = (char *) malloc((len + 1) * sizeof(char))) == NULL) 
00820         {
00821             free(name);
00822             return CP_MEMORY_ALLOCATION_FAILURE;
00823         }
00824         strncpy(value, curr + rm[2].rm_so, len);
00825         value[len] = '\0';
00826 #ifdef CP_USE_COOKIES
00827         if (strcmp(name, "Cookie") == 0)
00828         {
00829             if (req->cookie == NULL)
00830             {
00831                 req->cookie = cp_vector_create(1);
00832                 if (req->cookie == NULL)
00833                     return CP_MEMORY_ALLOCATION_FAILURE;
00834             }
00835             cp_vector_add_element(req->cookie, value);
00836             free(name);
00837         }
00838         else
00839 #endif /* CP_USE_COOKIES */
00840         cp_hashtable_put(req->header, name, value);
00841 
00842         eol = &curr[rm[2].rm_eo];
00843         curr = &curr[rm[0].rm_eo];
00844         if (is_empty_line(eol)) 
00845         {
00846             *stage = REQUEST_STAGE_DONE;
00847             break;
00848         }
00849     }
00850     
00851     skip_eol(&curr);
00852     *plen = curr - request;
00853 
00854     if ((req->version == HTTP_1_1) && 
00855         (cp_http_request_get_header(req, "Host") == NULL))
00856         return CP_HTTP_1_1_HOST_NOT_SPECIFIED;
00857             
00858     return 0;
00859 }
00860 
00861 static int parse_cp_http_request_body(cp_http_request *req, 
00862                                       char *request, 
00863                                       int *used)
00864 {
00865     int len;
00866     int dlen;
00867     char *clen;
00868     char *p = &request[*used];
00869 
00870     clen = cp_http_request_get_header(req, "Content-Length");
00871     if (clen == NULL) 
00872     { 
00873         cp_warn("no length header"); 
00874         return CP_HTTP_INCORRECT_REQUEST_BODY_LENGTH; 
00875     }
00876     dlen = atoi(clen);
00877     
00878     if (req->query_string)
00879     {
00880         int before = strlen(req->query_string);
00881         len = dlen - before;
00882         strncat(req->query_string, request, len);
00883         len = strlen(req->query_string) - before;
00884     }
00885     else
00886     {
00887         req->query_string = malloc(dlen + 1);
00888         if (*p == '\r') { p++; (*used)++; }
00889         if (*p == '\n') { p++; (*used)++; }
00890         strncpy(req->query_string, p, dlen);
00891         req->query_string[dlen] = '\0';
00892         len = strlen(req->query_string);
00893     }
00894 
00895     *used += len;
00896     p = &request[*used];
00897     if (*p == '\r') { p++; (*used)++; }
00898     if (*p == '\n') { p++; (*used)++; }
00899 
00900     return 0;
00901 }
00902 
00903 #define HEXDIGIT(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \
00904                      ((c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 0xA : \
00905                      ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 0xA : -1)))
00906 
00907 void urldecode(char *str, char **decoded)
00908 {
00909     char dbuf[HTTP_PARSE_BUFSIZE];
00910     char *src, *dst;
00911     int len;
00912 
00913     for (src = str, dst = dbuf, len = 0; *src && len < HTTP_PARSE_BUFSIZE; src++, dst++, len++)
00914     {
00915         switch (*src)
00916         {
00917             case '+': 
00918                 *dst = ' '; 
00919                 break; 
00920                 
00921             case '%': 
00922                 src++;
00923                 if (*src == '%') 
00924                     *dst = '%';
00925                 else
00926                     *dst = (HEXDIGIT(src[0])) << 4 | (HEXDIGIT(src[1]));
00927                     src++;
00928                 break;
00929 
00930             default:
00931                 *dst = *src;
00932                 break;
00933         }
00934     }
00935 
00936     *dst = '\0';
00937     
00938     *decoded = strndup(dbuf, len);
00939 }
00940 
00941 static int parse_cgi_vars(cp_http_request *req)
00942 {
00943     regmatch_t rm[MATCHMAX];
00944     int len;
00945     char *pos;
00946     char *raw_name, *raw_value;
00947     char *name, *value;
00948 
00949     req->prm = 
00950         cp_hashtable_create_by_option(COLLECTION_MODE_NOSYNC | 
00951                                       COLLECTION_MODE_DEEP, 10,
00952                                       cp_hash_string, cp_hash_compare_string,
00953                                       NULL, (cp_destructor_fn) free,
00954                                       NULL, (cp_destructor_fn) free);
00955 
00956     for (pos = req->query_string; 
00957          regexec(&re_request_vars, pos, MATCHMAX, rm, 0) == 0; 
00958          pos = &pos[rm[0].rm_eo])
00959     {
00960         len = rm[1].rm_eo - rm[1].rm_so;
00961         raw_name = strndup(&pos[rm[1].rm_so], len);
00962 
00963         len = rm[2].rm_eo - rm[2].rm_so;
00964         raw_value = strndup(&pos[rm[2].rm_so], len);
00965 
00966         urldecode(raw_name, &name);
00967         urldecode(raw_value, &value);
00968         free(raw_name);
00969         free(raw_value);
00970         
00971         cp_hashtable_put(req->prm, name, value);
00972     }
00973 
00974     return 0;
00975 }
00976 
00977 char *cp_http_request_get_header(cp_http_request *req, char *header)
00978 {
00979     char *res = NULL;
00980     
00981     if (req && header)
00982         res = cp_hashtable_get(req->header, header);
00983 
00984     return res;
00985 }
00986 
00987 char **cp_http_request_get_headers(cp_http_request *request)
00988 {
00989     char **res = NULL;
00990 
00991     if (request) res = (char **) cp_hashtable_get_keys(request->header);
00992 
00993     return res;
00994 }
00995 
00996 char *cp_http_request_get_parameter(cp_http_request *request, char *name)
00997 {
00998     char *res = NULL;
00999 
01000     if (request && name && request->prm)
01001         res = cp_hashtable_get(request->prm, name);
01002 
01003     return res;
01004 }
01005 
01006 #ifdef CP_USE_HTTP_SESSIONS
01007 cp_http_session *
01008     cp_http_request_get_session(cp_http_request *request, int create)
01009 {
01010     if (request->session == NULL && create)
01011         request->session = cp_httpsocket_create_session(request->owner);
01012 
01013     return request->session;
01014 }
01015 #endif /* CP_USE_HTTP_SESSIONS */
01016 
01017 void cp_http_request_dump(cp_http_request *req)
01018 {
01019     int i;
01020     char **keys;
01021     
01022     cp_info("dumping http request (%lX)", (long) req);
01023     cp_info("--------------------------------------------------------------");
01024     cp_info("method: %s", cp_http_request_type_lit[req->type]);
01025     cp_info("version: HTTP/%s", cp_http_version_lit[req->version]);
01026     cp_info("uri: [%s]", req->uri);
01027     
01028     cp_info("headers:\n--------");
01029     keys = (char **) cp_hashtable_get_keys(req->header);
01030     for (i = 0; i < cp_hashtable_count(req->header); i++)
01031         cp_info("%s: %s", keys[i], (char *) cp_hashtable_get(req->header, keys[i]));
01032     free(keys);
01033 
01034 #ifdef CP_USE_COOKIES
01035     if (req->cookie)
01036     {
01037         int n;
01038         cp_info("cookies:\n--------");
01039         n = cp_vector_size(req->cookie);
01040         for (i = 0; i < n; i++)
01041             cp_info("%s", cp_vector_element_at(req->cookie, i));
01042     }
01043 #endif /* CP_USE_COOKIES */
01044 
01045     if (req->prm)
01046     {
01047         cp_info("cgi parameters:\n---------------");
01048         keys = (char **) cp_hashtable_get_keys(req->prm);
01049         for(i = 0; i < cp_hashtable_count(req->prm); i++)
01050             cp_info("%s: %s", keys[i], (char *) cp_hashtable_get(req->prm, keys[i]));
01051         free(keys);
01052     }
01053 
01054     cp_info("--------------------------------------------------------------");
01055 }
01056 
01057 
01058 /****************************************************************************
01059  *                                                                          *
01060  *  cp_http_response_* functions:                                           *
01061  *                                                                          *
01062  *  o cp_http_response_create - allocates & initialized response struct     *
01063  *  o cp_http_response_delete - releases a response struct                  *
01064  *  o cp_http_response_write - writes response to client                    *
01065  *                                                                          *
01066  *  o cp_http_response_set_status - set the status code for a response      *
01067  *  o cp_http_response_get_status - get the status code of a response       *
01068  *  o cp_http_response_set_content_type - set content type code [deprecated]*
01069  *  o cp_http_response_set_content_type_string - set content type           *
01070  *  o cp_http_response_get_content_type - get content type                  *
01071  *  o cp_http_response_set_header - set a header                            *
01072  *  o cp_http_response_get_header - get a header value                      *
01073  *  o cp_http_response_get_header_names - get a vector of header names      *
01074  *  o cp_http_response_set_body - set the response content [deprecated]     *
01075  *  o cp_http_response_set_content - set the response content               *
01076  *  o cp_http_response_get_content - get the response content               *
01077  *  o cp_http_response_set_connection_policy - Close / Keep-Alive           *
01078  *  o cp_http_response_skip - framework doesn't write response object       *
01079  *                                                                          *
01080  ****************************************************************************/
01081 
01082 cp_http_response *cp_http_response_create(cp_http_request *req)
01083 {
01084     cp_http_response *res = NULL;
01085     
01086     if ((res = (cp_http_response *) calloc(1, sizeof(cp_http_response))))
01087     {
01088         res->request = req;
01089         res->header = 
01090             cp_hashtable_create_by_option(COLLECTION_MODE_NOSYNC |
01091                                           COLLECTION_MODE_COPY |
01092                                           COLLECTION_MODE_DEEP,
01093                                           10, 
01094                                           cp_hash_string, 
01095                                           cp_hash_compare_string,
01096                                           (cp_copy_fn) strdup, free, 
01097                                           (cp_copy_fn) strdup, free);
01098         if (res->header == NULL)
01099         {
01100             free(res);
01101             return NULL;
01102         }
01103         res->status = HTTP_200_OK;
01104         res->version = req ? req->version : HTTP_1_1;
01105         res->servername = req && req->owner ? req->owner->server_name : DEFAULT_SERVER_NAME;
01106     }
01107 
01108     return res;
01109 }
01110 
01111 void cp_http_response_delete(cp_http_response *response)
01112 {
01113     if (response)
01114     {
01115         if (response->header)
01116             cp_hashtable_destroy(response->header);
01117 #ifdef CP_USE_COOKIES
01118         if (response->cookie)
01119             cp_vector_destroy_custom(response->cookie, free);
01120 #endif /* CP_USE_COOKIES */
01121         if (response->content_type_lit)
01122             free(response->content_type_lit);
01123         if (response->content)
01124             cp_string_delete(response->content);
01125 //~~ check this free
01126 //      if (response->body)
01127 //          free(response->body);
01128         if (response->status_lit) 
01129             free(response->status_lit);
01130         free(response);
01131     }
01132 }
01133 
01134 void cp_http_response_destroy(cp_http_response *res)
01135 {
01136     cp_http_response_delete(res);
01137 }
01138 
01139 int cp_http_response_write(cp_connection_descriptor *cdesc, 
01140                            cp_http_response *res)
01141 {
01142     char rbuf[HTTP_PARSE_BUFSIZE];
01143     char *content = NULL;
01144     int content_len = 0;
01145     int len;
01146     int rc, total;
01147     int fd = cp_connection_descriptor_get_fd(cdesc);
01148 #ifdef CP_USE_SSL
01149     cp_socket *sock = cp_connection_descriptor_get_socket(cdesc);
01150 #endif
01151 
01152     if (res->len == 0 && res->body) res->len = strlen(res->body);
01153 
01154 #ifdef CP_HAS_SNPRINTF
01155     snprintf(rbuf, HTTP_PARSE_BUFSIZE, 
01156         "HTTP/%s %s\r\n"
01157         "Server: %s\r\n"
01158         "Connection: %s\r\n", 
01159             cp_http_version_lit[res->version], 
01160             (char *) cp_hashtable_get(cp_http_status_lit, &res->status), 
01161             res->servername ? res->servername : DEFAULT_SERVER_NAME, 
01162             connection_policy_lit[res->connection]);
01163 #else
01164     sprintf(rbuf, 
01165         "HTTP/%s %s\r\n"
01166         "Server: %s\r\n"
01167         "Connection: %s\r\n", 
01168             cp_http_version_lit[res->version], 
01169             (char *) cp_hashtable_get(cp_http_status_lit, &res->status), 
01170             res->servername ? res->servername : DEFAULT_SERVER_NAME, 
01171             connection_policy_lit[res->connection]);
01172 #endif /* CP_HAS_SNPRINTF */
01173 
01174     if (res->header)
01175     {
01176         char hbuf[HTTP_PARSE_BUFSIZE];
01177         int i;
01178         char **key = (char **) cp_hashtable_get_keys(res->header);
01179         for (i = 0; i < cp_hashtable_count(res->header); i++)
01180         {
01181 #ifdef CP_HAS_SNPRINTF
01182             snprintf(hbuf, HTTP_PARSE_BUFSIZE, "%s: %s\r\n", 
01183                     key[i], (char *) cp_hashtable_get(res->header, key[i]));
01184 #else
01185             sprintf(hbuf, "%s: %s\r\n", 
01186                     key[i], (char *) cp_hashtable_get(res->header, key[i]));
01187 #endif /* CP_HAS_SNPRINTF */
01188             strlcat(rbuf, hbuf, HTTP_PARSE_BUFSIZE);
01189         }
01190         free(key);
01191     }
01192                 
01193 #ifdef CP_USE_COOKIES
01194     if (res->cookie)
01195     {
01196         char cbuf[HTTP_PARSE_BUFSIZE];
01197         int i;
01198         for (i = 0; i < cp_vector_size(res->cookie); i++)
01199         {
01200 #ifdef CP_HAS_SNPRINTF
01201             snprintf(cbuf, HTTP_PARSE_BUFSIZE, "Set-Cookie: %s\r\n", 
01202                     (char *) cp_vector_element_at(res->cookie, i));
01203 #else
01204             sprintf(cbuf, "Set-Cookie: %s\r\n", 
01205                     (char *) cp_vector_element_at(res->cookie, i));
01206 #endif /* CP_HAS_SNPRINTF */
01207             strlcat(rbuf, cbuf, HTTP_PARSE_BUFSIZE);
01208         }
01209     }
01210 #endif /* CP_USE_COOKIES */
01211 
01212     if (res->content)
01213     {
01214         char bbuf[HTTP_PARSE_BUFSIZE];
01215 #ifdef CP_HAS_SNPRINTF
01216         snprintf(bbuf, HTTP_PARSE_BUFSIZE, 
01217             "Content-Type: %s\r\nContent-Length: %d\r\n\r\n",
01218             res->content_type_lit ? 
01219                 res->content_type_lit : content_type_lit[res->content_type],
01220             res->content->len); //~~
01221 #else
01222         sprintf(bbuf, "Content-Type: %s\r\nContent-Length: %d\r\n\r\n",
01223             res->content_type_lit ? 
01224                 res->content_type_lit : content_type_lit[res->content_type],
01225             res->content->len); //~~
01226 #endif /* CP_HAS_SNPRINTF */
01227         strlcat(rbuf, bbuf, HTTP_PARSE_BUFSIZE);
01228         content = res->content->data;
01229         content_len = res->content->len;
01230     }
01231     else if (res->body)
01232     {
01233         char bbuf[HTTP_PARSE_BUFSIZE];
01234         content_len = strlen(res->body);
01235 #ifdef CP_HAS_SNPRINTF
01236         snprintf(bbuf, HTTP_PARSE_BUFSIZE, 
01237             "Content-Type: %s\r\nContent-Length: %d\r\n\r\n",
01238             res->content_type_lit ? 
01239                 res->content_type_lit : content_type_lit[res->content_type],
01240             content_len);
01241 #else
01242         sprintf(bbuf, "Content-Type: %s\r\nContent-Length: %d\r\n\r\n",
01243             res->content_type_lit ? 
01244                 res->content_type_lit : content_type_lit[res->content_type],
01245             content_len);
01246 #endif /* CP_HAS_SNPRINTF */
01247         strlcat(rbuf, bbuf, HTTP_PARSE_BUFSIZE);
01248         content = res->body;
01249     }
01250 
01251     len = strlen(rbuf);
01252 
01253 #ifdef CP_USE_SSL
01254     if (sock->use_ssl)
01255     {
01256         rc = SSL_write(cdesc->ssl, rbuf, len);
01257         total = rc;
01258         if (content_len)
01259         {
01260             rc = SSL_write(cdesc->ssl, content, content_len);
01261             total += rc;
01262         }
01263     }
01264     else
01265 #endif /* CP_USE_SSL */
01266     {
01267         rc = send(fd, rbuf, len, 0); //~~ handle errors etc.
01268         total = rc;
01269         if (content_len)
01270         {
01271             rc = send(fd, content, content_len, 0);
01272             total += rc;
01273         }
01274     }
01275     
01276 #ifdef __TRACE__
01277     cp_info("----- http response headers -----\n%s", rbuf);
01278     if (res->content) 
01279         cp_ndump(LOG_LEVEL_INFO, res->content, 0x400);
01280     else if (res->body) 
01281         cp_nlog(0x400, content);
01282     if (content_len > 0x400) 
01283         cp_log("\n ... truncated (%d bytes)\n", content_len);
01284     cp_info("---------------------------------\n");
01285 #endif
01286 
01287     if (total > 0) cdesc->bytes_sent += total;
01288 
01289 #ifdef DEBUG
01290     DEBUGMSG("%d bytes written", total);
01291 #endif
01292     
01293     return rc;
01294 }
01295 
01296 void cp_http_response_set_status(cp_http_response *response, 
01297                                         cp_http_status_code status)
01298 {
01299     response->status = status;
01300 }
01301 
01302 cp_http_status_code cp_http_response_get_status(cp_http_response *response)
01303 {
01304     return response->status;
01305 }
01306 
01307 void cp_http_response_set_content_type(cp_http_response *response, 
01308                                               cp_http_content_type type)
01309 {
01310     response->content_type = type;
01311 }
01312 
01313 void cp_http_response_set_content_type_string(cp_http_response *response,
01314                                                      char *content_type)
01315 {
01316     response->content_type_lit = content_type ? strdup(content_type) : NULL;
01317 }
01318 
01319 char *cp_http_response_get_content_type(cp_http_response *response)
01320 {
01321     return response->header ? cp_hashtable_get(response->header, "Content-Type"): NULL;
01322 }
01323 
01324 void cp_http_response_set_header(cp_http_response *response, 
01325                                         char *name, char *value)
01326 {
01327     cp_hashtable_put(response->header, name, value);
01328 }
01329 
01330 char *cp_http_response_get_header(cp_http_response *response, char *name)
01331 {
01332     return cp_hashtable_get(response->header, name);
01333 }
01334 
01335 cp_vector *cp_http_response_get_header_names(cp_http_response *response)
01336 {
01337     cp_vector *names = NULL;
01338     if (response->header)
01339     {
01340         int len = cp_hashtable_count(response->header);
01341         names = cp_vector_wrap(cp_hashtable_get_keys(response->header), len, 0);
01342         if (names == NULL) return NULL;
01343     }
01344 
01345     return names;
01346 }
01347 
01348 void cp_http_response_set_body(cp_http_response *response, 
01349                                       char *body)
01350 {
01351     response->body = strdup(body);
01352 }
01353 
01354 void cp_http_response_set_content(cp_http_response *response, 
01355                                          cp_string *content)
01356 {
01357     response->content = content;
01358 }
01359 
01360 cp_string *cp_http_response_get_content(cp_http_response *response)
01361 {
01362     return response->content;
01363 }
01364 
01365 void cp_http_response_set_connection_policy(cp_http_response *response, 
01366                                             connection_policy policy)
01367 {
01368     response->connection = policy;
01369 }
01370 
01371 #ifdef CP_USE_COOKIES
01372 int cp_http_response_set_cookie(cp_http_response *response, char *name, 
01373         char *content, char *host, char *path, long validity, int secure)
01374 {
01375     char cookie[MAX_COOKIE_LENGTH];
01376     char expiration[0x50];
01377     time_t now;
01378     struct tm gmt;
01379 #ifndef CP_HAS_GMTIME_R
01380     struct tm *pgmt;
01381 #endif /* CP_HAS_GMTIME_R */
01382     char *domain;
01383 
01384     now = time(NULL);
01385 
01386     now += validity;
01387 #ifdef CP_HAS_GMTIME_R
01388     gmtime_r(&now, &gmt);
01389 #else
01390     pgmt = gmtime(&now);
01391     gmt = *pgmt;
01392 #endif /* CP_HAS_GMTIME_R */
01393     strftime(expiration, 0x50, COOKIE_TIME_FMT, &gmt);
01394 
01395     if (host) 
01396     {
01397         if (strncmp(host, "www.", 4) == 0) 
01398             domain = &host[3];
01399         else
01400             domain = NULL; //~~
01401     }
01402     else
01403         domain = NULL;
01404 
01405 #ifdef CP_HAS_SNPRINTF
01406     snprintf(cookie, MAX_COOKIE_LENGTH, "%s=%s; expires=%s", name, content, expiration);
01407 #else
01408     sprintf(cookie, "%s=%s; expires=%s", name, content, expiration);
01409 #endif /* CP_HAS_SNPRINTF */
01410     if (path)
01411     {
01412         strlcat(cookie, "; path=", MAX_COOKIE_LENGTH);
01413         strlcat(cookie, path, MAX_COOKIE_LENGTH);
01414     }
01415     if (domain)
01416     {
01417         strlcat(cookie, "; domain=", MAX_COOKIE_LENGTH);
01418         strlcat(cookie, domain, MAX_COOKIE_LENGTH);
01419     }
01420     if (secure) strlcat(cookie, "; secure", MAX_COOKIE_LENGTH);
01421 
01422 #ifdef __TRACE__
01423     DEBUGMSG("setting cookie (%d chars): %s", strlen(cookie), cookie);
01424 #endif
01425     if (response->cookie == NULL)
01426     {
01427         response->cookie = cp_vector_create(1);
01428         if (response->cookie == NULL) return -1;
01429     }
01430 
01431     cp_vector_add_element(response->cookie, strdup(cookie)); //~~ vector mode
01432     
01433     return 0;
01434 }
01435 #endif
01436 
01437 void cp_http_response_skip(cp_http_response *response)
01438 {
01439     response->skip = 1;
01440 }
01441 
01442 
01443 static int cp_http_write_server_error(cp_connection_descriptor *cdesc, 
01444                                       int status_code)
01445 {
01446     char errbuf[HTTP_PARSE_BUFSIZE];
01447     char *lit;
01448     int rc;
01449     
01450     cp_http_response *res = cp_http_response_create(NULL);
01451 
01452     if (res == NULL) return CP_MEMORY_ALLOCATION_FAILURE;
01453 
01454     cp_http_response_set_status(res, HTTP_400_BAD_REQUEST);
01455 
01456     lit = cp_hashtable_get(cp_http_server_error, &status_code);
01457 
01458     if (lit)
01459 #ifdef CP_HAS_SNPRINTF
01460         snprintf(errbuf, HTTP_PARSE_BUFSIZE, server_error_fmt, lit);
01461 #else
01462         sprintf(errbuf, server_error_fmt, lit);
01463 #endif /* CP_HAS_SNPRINTF */
01464     else
01465         errbuf[0] = '\0';
01466     
01467     cp_http_response_set_content_type(res, HTML);
01468 
01469     res->body = errbuf;
01470 
01471     rc = cp_http_response_write(cdesc, res);
01472 
01473     res->body = NULL;
01474     cp_http_response_delete(res);
01475         
01476     return rc;
01477 }
01478 
01479 
01480 /****************************************************************************
01481  *                                                                          *
01482  * cp_httpsocket_* functions                                                *
01483  *                                                                          *
01484  ****************************************************************************/
01485 
01486 cp_httpsocket *cp_httpsocket_create(int port, cp_http_service_callback service)
01487 {
01488     cp_socket *sock;
01489     cp_httpsocket *psock;
01490 
01491     cp_http_init(); /* idempotent */
01492 
01493     sock = cp_socket_create(port, CPSOCKET_STRATEGY_THREADPOOL, cp_http_thread_fn);
01494     if (sock == NULL) return NULL; //~~ how about an error code?
01495 
01496     psock = (cp_httpsocket *) calloc(1, sizeof(cp_httpsocket));
01497     if (psock == NULL)
01498     {
01499         cp_socket_delete(sock);
01500         cp_error(CP_MEMORY_ALLOCATION_FAILURE, "can\'t allocate cp_httpsocket");
01501     }
01502 
01503     psock->id = socket_reg_id++;
01504     cp_hashlist_append(socket_registry, &psock->id, psock); /* register */
01505     
01506     psock->sock = sock;
01507     cp_socket_set_owner(sock, psock);
01508 
01509     psock->default_service = service;
01510     cp_httpsocket_set_keepalive(psock, HTTP_KEEPALIVE);
01511 
01512     return psock;
01513 }
01514 
01515 #ifdef CP_USE_SSL
01516 cp_httpsocket *cp_httpsocket_create_ssl(int port, 
01517                                         cp_http_service_callback service, 
01518                                         char *certificate_file,
01519                                         char *key_file,
01520                                         int verification_mode)
01521 {
01522     cp_socket *sock;
01523     cp_httpsocket *psock;
01524 
01525     cp_http_init(); /* idempotent */
01526 
01527     sock = cp_socket_create_ssl(port, CPSOCKET_STRATEGY_THREADPOOL, 
01528                                 cp_http_thread_fn, certificate_file, 
01529                                 key_file, verification_mode);
01530 
01531     if (sock == NULL) return NULL; //~~ how about an error code?
01532 
01533     psock = (cp_httpsocket *) calloc(1, sizeof(cp_httpsocket));
01534     if (psock == NULL)
01535     {
01536         cp_socket_delete(sock);
01537         cp_error(CP_MEMORY_ALLOCATION_FAILURE, "can\'t allocate cp_httpsocket");
01538     }
01539     
01540     psock->sock = sock;
01541     cp_socket_set_owner(sock, psock);
01542 
01543     psock->default_service = service;
01544     cp_httpsocket_set_keepalive(psock, HTTP_KEEPALIVE);
01545 
01546     return psock;
01547 }
01548 #endif
01549     
01550 void cp_httpsocket_set_keepalive(cp_httpsocket *socket, int sec)
01551 {
01552     socket->keepalive = sec;
01553 }
01554 
01555 void cp_httpsocket_set_server_name(cp_httpsocket *socket, char *name)
01556 {
01557     socket->server_name = strdup(name);
01558 }
01559 
01560 void cp_httpsocket_set_backlog(cp_httpsocket *socket, int backlog)
01561 {
01562     cp_socket_set_backlog(socket->sock, backlog);
01563 }
01564 
01565 void cp_httpsocket_set_delay(cp_httpsocket *socket, struct timeval delay)
01566 {
01567     cp_socket_set_delay(socket->sock, delay);
01568 }
01569 
01570 void cp_httpsocket_set_delay_sec(cp_httpsocket *socket, long sec)
01571 {
01572     cp_socket_set_delay_sec(socket->sock, sec);
01573 }
01574 
01575 void cp_httpsocket_set_delay_usec(cp_httpsocket *socket, long usec)
01576 {
01577     cp_socket_set_delay_usec(socket->sock, usec);
01578 }
01579 
01580 void cp_httpsocket_set_poolsize_min(cp_httpsocket *socket, int min)
01581 {
01582     cp_socket_set_poolsize_min(socket->sock, min);
01583 }
01584 
01585 void cp_httpsocket_set_poolsize_max(cp_httpsocket *socket, int max)
01586 {
01587     cp_socket_set_poolsize_max(socket->sock, max);
01588 }
01589 
01590 void cp_httpsocket_delete(cp_httpsocket *sock)
01591 {
01592     if (sock)
01593     {
01594         if (socket_registry)
01595             cp_hashlist_remove(socket_registry, &sock->id);
01596 
01597         cp_socket_delete(sock->sock);
01598 #ifdef CP_USE_HTTP_SESSIONS
01599         if (sock->session)
01600             cp_hashlist_destroy_deep(sock->session);
01601         if (sock->pending)
01602             cp_hashlist_destroy_custom(sock->pending, NULL, 
01603                     (cp_destructor_fn) cp_http_response_delete);
01604 #endif /* CP_USE_HTTP_SESSIONS */
01605         if (sock->service)
01606             cp_trie_destroy(sock->service);
01607         if (sock->server_name) 
01608             free(sock->server_name);
01609         free(sock);
01610     }
01611 }
01612 
01613 void *cp_httpsocket_add_shutdown_callback(cp_httpsocket *socket, 
01614                                           void (*cb)(void *),
01615                                           void *prm)
01616 {
01617     return cp_socket_add_shutdown_callback(socket->sock, cb, prm);
01618 }
01619 
01620 int cp_httpsocket_listen(cp_httpsocket *sock)
01621 {
01622     int rc;
01623 
01624     rc = cp_socket_listen(sock->sock);
01625     if (rc == 0) rc = cp_socket_select(sock->sock);
01626 
01627     return rc;
01628 }
01629 
01630 int cp_httpsocket_register_service(cp_httpsocket *server, 
01631                                    cp_http_service *service)
01632 {
01633     int rc = 0;
01634 
01635     if (server->service == NULL)
01636     {
01637         server->service = 
01638             cp_trie_create_trie(COLLECTION_MODE_DEEP, NULL, 
01639                                 (cp_destructor_fn) cp_http_service_delete);
01640         if (server->service == NULL)
01641             return -1; //~~ error code
01642     }
01643 
01644     rc = cp_trie_add(server->service, service->path, service);
01645 
01646     return rc;
01647 }
01648 
01649 void *cp_httpsocket_unregister_service(cp_httpsocket *server, 
01650                                        cp_http_service *service)
01651 {
01652     if (server->service)
01653     {
01654         void *rm = NULL;
01655         cp_trie_remove(server->service, service->path, &rm);
01656         return rm;
01657     }
01658 
01659     return NULL;
01660 }
01661 
01662 
01663 /****************************************************************************
01664  *                                                                          *
01665  * cp_http_service_* functions                                              *
01666  *                                                                          *
01667  ****************************************************************************/
01668 
01669 cp_http_service *cp_http_service_create(char *name, 
01670                                         char *path, 
01671                                         cp_http_service_callback callback)
01672 {
01673     cp_http_service *svc;
01674     
01675     svc = (cp_http_service *) calloc(1, sizeof(cp_http_service));
01676     if (svc == NULL)
01677     {
01678         cp_error(CP_MEMORY_ALLOCATION_FAILURE, "can\'t allocate cp_http_service");
01679         return NULL;
01680     }
01681 
01682     svc->name = strdup(name);
01683     svc->path = strdup(path);
01684     svc->service = callback;
01685     
01686     return svc;
01687 }
01688 
01689 void cp_http_service_delete(cp_http_service *service)
01690 {
01691     if (service)
01692     {
01693         free(service->name);
01694         free(service->path);
01695         free(service);
01696     }
01697     else
01698         cp_warn("cp_http_service_delete: attempting to delete NULL service");
01699 }
01700 
01701 
01702 /****************************************************************************
01703  *                                                                          *
01704  * cp_http implementation helper functions                                  *
01705  *                                                                          *
01706  * o cp_http_default_response - creates a 501 NOT IMPLEMENTED response for  *
01707  *   the case that an cp_httpsocket instance is created with a NULL         *
01708  *   default_service                                                        *
01709  * o get_keepalive returns the keep-alive time for a request                *
01710  *                                                                          *
01711  ****************************************************************************/
01712 
01713 static int cp_http_default_response(cp_http_request *request, cp_http_response *response)
01714 {
01715     char buf[0x400];
01716 
01717     cp_warn("cp_http: no service defined for uri [%s]", request->uri);
01718 
01719     cp_http_response_set_content_type(response, HTML);
01720     cp_http_response_set_status(response, HTTP_501_NOT_IMPLEMENTED);
01721 
01722 #ifdef CP_HAS_SNPRINTF
01723     snprintf(buf, 0x400, "<html><head><title>%s: 501 NOT IMPLEMENTED</title></head><body><h1>Welcome to %s</h1>Your request [%s] could not be served: this server is not configured.<h1></body></html>", response->servername ? response->servername : DEFAULT_SERVER_NAME, response->servername ? response->servername : DEFAULT_SERVER_NAME, request->uri);
01724 #else
01725     sprintf(buf, "<html><head><title>%s: 501 NOT IMPLEMENTED</title></head><body><h1>Welcome to %s</h1>Your request [%s] could not be served: this server is not configured.<h1></body></html>", response->servername ? response->servername : DEFAULT_SERVER_NAME, response->servername ? response->servername : DEFAULT_SERVER_NAME, request->uri);
01726 #endif /* CP_HAS_SNPRINTF */
01727 
01728     response->body = strdup(buf);
01729 
01730     return HTTP_CONNECTION_POLICY_CLOSE;
01731 }
01732 
01733 void cp_http_response_report_error(cp_http_response *response, 
01734                                    cp_http_status_code code, 
01735                                    char *message)
01736 {
01737     char buf[0x400];
01738     char *err = cp_hashtable_get(cp_http_status_lit, &code);
01739 
01740     cp_http_response_set_content_type(response, HTML);
01741     cp_http_response_set_status(response, code);
01742     
01743 #ifdef CP_HAS_SNPRINTF
01744     snprintf(buf, 0x400, "<html><head><title>%s: %s</title></head>"
01745              "<body><h1>%s</h1>"
01746              "%s</body></html>", 
01747              response->servername ? response->servername : DEFAULT_SERVER_NAME, 
01748              err, err, message);
01749 #else
01750     sprintf(buf, "<html><head><title>%s: %s</title></head>"
01751             "<body><h1>%s</h1>"
01752             "%s</body></html>", 
01753             response->servername ? response->servername : DEFAULT_SERVER_NAME, 
01754             err, err, message);
01755 #endif /* CP_HAS_SNPRINTF */
01756 
01757     cp_http_response_set_body(response, buf);
01758 }
01759 
01760 static int get_keepalive(cp_httpsocket *sock, cp_http_request *request)
01761 {
01762     int keepalive = 0;
01763     char *ka_lit;
01764     char *con_lit;
01765     
01766     con_lit = cp_hashtable_get(request->header, "Connection");
01767     if (con_lit && strcmp(con_lit, "close") == 0) return 0;
01768     
01769     ka_lit = cp_hashtable_get(request->header, "Keep-Alive");
01770     if (ka_lit)
01771         keepalive = atoi(ka_lit);
01772     
01773     if (keepalive > sock->keepalive || 
01774         (keepalive == 0 && request->version == HTTP_1_1)) 
01775         keepalive = sock->keepalive;
01776 
01777     return keepalive;
01778 }
01779 
01780 static int cp_httpselect(cp_httpsocket *hsock, int sec, int fd)
01781 {
01782     int rc = -1;
01783 
01784     fd_set rd;
01785     struct timeval tv;
01786 
01787 #ifdef __TRACE__
01788     DEBUGMSG("keepalive: %d", sec);
01789 #endif
01790                 
01791     FD_ZERO(&rd);
01792     FD_SET((unsigned int) fd, &rd);
01793 
01794     tv.tv_sec = sec;
01795     tv.tv_usec = 0;
01796     rc = select(fd + 1, &rd, NULL, NULL, &tv);
01797 #ifdef __TRACE__        
01798     DEBUGMSG("read fd: %d", FD_ISSET(fd, &rd));  
01799 #endif
01800 
01801     if (rc > -1) return FD_ISSET(fd, &rd) - 1;
01802 #ifdef __TRACE__
01803     else DEBUGMSG("select: %s", strerror(errno));
01804 #endif
01805     
01806     return rc;
01807 }
01808 
01809 static int find_newline(char *buf)
01810 {
01811     char *p = strstr(buf, "\r\n");
01812     if (p == NULL) p = strchr(buf, '\n');
01813     if (p == NULL) return -1;
01814     return p - buf;
01815 }
01816 
01817 static int check_newline(char *buf, int *index)
01818 {
01819     return (strncmp(buf, "\r\n\r\n", 4) == 0) ||
01820            (strncmp(buf, "\n\n", 2) == 0);
01821 }
01822     
01823 static int 
01824     incremental_request_parse(cp_httpsocket *sock, cp_http_request **request, 
01825                               int *stage, cp_string *state, char *curr, 
01826                               int len, int *used)
01827 {
01828     int rc;
01829     char *buf;
01830 
01831     /* if carrying over unprocessed bytes from a previous call, concatenate 
01832      * the new data to the old data
01833      */
01834     if (state->len)
01835     {
01836         cp_string_cat_bin(state, curr, len);
01837         len = state->len;
01838         cp_string_tocstr(state); /* place '\0' */
01839         buf = state->data;
01840     }
01841     else
01842         buf = curr;
01843     
01844     switch (*stage)
01845     {
01846         case REQUEST_STAGE_START:
01847             *used = 0;
01848             *request = calloc(1, sizeof(cp_http_request));
01849             if (*request == NULL) return CP_MEMORY_ALLOCATION_FAILURE;
01850             (*request)->owner = sock;
01851             *stage = REQUEST_STAGE_STATUS_LINE;
01852 
01853         case REQUEST_STAGE_STATUS_LINE:
01854             if (find_newline(buf) == -1) /* not done reading status line */
01855             {
01856                 if (buf != state->data)  /* store content for next attempt */
01857                     cp_string_cstrcpy(state, buf);
01858                 break;
01859             }
01860             if ((rc = parse_request_line(*request, buf, used)))
01861                 return rc;
01862             if (check_newline(buf, used))
01863             {
01864                 *stage = REQUEST_STAGE_DONE;
01865                 break;
01866             }
01867             *stage = REQUEST_STAGE_HEADERS;
01868             if (*used == len) break;
01869 
01870         case REQUEST_STAGE_HEADERS:
01871             if ((rc = parse_headers(*request, buf, used, stage)))
01872                 return rc;
01873             if (*stage == REQUEST_STAGE_DONE && 
01874                 cp_http_request_get_header(*request, "Content-Length"))
01875                 *stage = REQUEST_STAGE_BODY;
01876             if (*used == len) break;
01877 
01878         case REQUEST_STAGE_BODY:
01879             if ((rc = parse_cp_http_request_body(*request, buf, used)))
01880                 return rc;
01881             if (*used == len) *stage = REQUEST_STAGE_DONE;
01882     }
01883 
01884     return 0;
01885 }
01886 
01887 /* handle cgi parameters, sessions and cookies */
01888 static int post_process(cp_httpsocket *socket, cp_http_request *req)
01889 {
01890     int rc;
01891 
01892     /* parse cgi parameters */
01893     if (req->query_string)
01894     {
01895         if ((rc = parse_cgi_vars(req)))
01896             return rc;
01897     }
01898 
01899     /* session handling if enabled */
01900 #ifdef CP_USE_HTTP_SESSIONS
01901     {
01902         char *sid;
01903 #ifdef CP_USE_COOKIES
01904         if (req->cookie) //~~ move this to cookie parsing to avoid looping?
01905         {
01906             char *cookie;
01907             int i;
01908             int n = cp_vector_size(req->cookie);
01909             for (i = 0; i < n; i++)
01910             {
01911                 cookie = cp_vector_element_at(req->cookie, i);
01912                 if (strncmp(cookie, 
01913                     CP_HTTP_SESSION_MARKER, CP_HTTP_SESSION_MARKER_LEN) == 0)
01914                 {
01915                     sid = &cookie[CP_HTTP_SESSION_MARKER_LEN];
01916                     req->session = cp_httpsocket_checkout_session(socket, sid);
01917                     if (req->session == NULL)
01918                         cp_warn("can\'t find session record %s", sid);
01919                     else
01920                     {
01921                         req->session->type = SESSION_TYPE_COOKIE;
01922                         req->session->access = time(NULL);
01923 //                      req->session->fresh = 0;
01924                     }
01925                 }
01926             }
01927         }
01928         if (req->session == NULL)
01929 #endif /* CP_USE_COOKIES */
01930         {
01931             sid = cp_http_request_get_parameter(req, CP_HTTP_SESSION_PRM);
01932             if (sid)
01933             {
01934                 req->session = cp_httpsocket_checkout_session(socket, sid);
01935                 if (req->session == NULL)
01936                     cp_warn("can\'t find session record %s", sid);
01937                 else
01938                 {
01939                     req->session->type = SESSION_TYPE_URLREWRITE;
01940                     req->session->access = time(NULL);
01941                 }
01942             }
01943         }
01944     }
01945 #endif /* CP_USE_HTTP_SESSIONS */
01946 
01947     return 0;
01948 }
01949 
01950 static int read_request(cp_list *req_list, cp_connection_descriptor *cdesc)
01951 {
01952     char tmpbuf[HTTP_PARSE_BUFSIZE + 1];
01953     cp_string *buf = cp_string_create_empty(HTTP_PARSE_BUFSIZE);
01954     int rc;
01955     int len;
01956     int fd = 0;
01957     cp_socket *sock = cp_connection_descriptor_get_socket(cdesc);
01958     cp_http_request *request = NULL;
01959     int done_reading = 0;
01960     int stage = REQUEST_STAGE_START;
01961     int used;
01962 
01963 #ifdef CP_USE_SSL   
01964     if (!sock->use_ssl)
01965 #endif
01966     fd = cp_connection_descriptor_get_fd(cdesc);
01967 
01968     while (!done_reading)
01969     {
01970 #ifdef CP_USE_SSL
01971         if (sock->use_ssl)
01972         {
01973             if ((len = SSL_read(cdesc->ssl, tmpbuf, HTTP_PARSE_BUFSIZE)) <= 0)
01974                 break;
01975         }
01976         else
01977 #endif
01978         if ((len = recv(fd, tmpbuf, HTTP_PARSE_BUFSIZE, 0)) <= 0) break;
01979         if (len <= 0) break;
01980         tmpbuf[len] = '\0';
01981         cdesc->bytes_read += len;
01982         used = 0;
01983 
01984 #ifdef _HTTP_DUMP
01985         DEBUGMSG("\n---------------------------------------------------------------------------" "\n%s\n" "---------------------------------------------------------------------------", tmpbuf); 
01986 #endif /* _HTTP_DUMP */
01987 
01988         /* parse request by stage - initial, status line, headers, body */
01989         rc = incremental_request_parse((cp_httpsocket *) sock->owner, &request,
01990                                        &stage, buf, tmpbuf, len, &used);
01991         if (rc)
01992         {
01993             if (request)
01994                 cp_http_request_delete(request);
01995             return rc;
01996         }
01997 
01998         if (stage == REQUEST_STAGE_DONE)
01999         {
02000             if (request) 
02001             {
02002                 /* handle cgi parameters, cookies and sessions */
02003                 if ((rc = post_process(sock->owner, request)))
02004                 {
02005                     cp_list_append(req_list, (void *) (long) -rc);
02006                     cp_http_request_delete(request);
02007                     break;
02008                 }
02009                 else
02010                     cp_list_append(req_list, request);
02011             }
02012             else
02013                 cp_list_append(req_list, (void *) (long) -rc);
02014 
02015             if (used == len) /* done reading */
02016                 done_reading = 1;
02017             else            /* piplining - get next request */
02018             {
02019                 request = NULL;
02020                 stage = REQUEST_STAGE_START;
02021                 buf->len = 0;
02022             }
02023         }
02024     }
02025 
02026     if (buf) cp_string_destroy(buf);
02027     return cp_list_item_count(req_list);
02028 }
02029 
02030 /****************************************************************************
02031  *                                                                          *
02032  * cp_http_thread_fn - a cp_socket based partial http implementation        *
02033  *                                                                          *
02034  * the void *prm is a cp_socket cp_connection_descriptor. The               *
02035  * cp_socket->owner is a cp_httpsocket                                      *
02036  *                                                                          *
02037  ****************************************************************************/
02038 
02039 void *cp_http_thread_fn(void *prm)
02040 {
02041     cp_connection_descriptor *cdesc;
02042     cp_socket *sock;
02043     struct sockaddr_in *addr;
02044     int fd;
02045     int rc = 0;
02046     int done;
02047     int action;
02048     int keepalive = HTTP_CONNECTION_POLICY_DEFAULT;
02049     cp_http_request *request = NULL; // prevents gcc warning
02050     cp_httpsocket *hsock;
02051     cp_http_service_callback svc;
02052     cp_list *req_list = 
02053         cp_list_create_list(COLLECTION_MODE_NOSYNC | 
02054                             COLLECTION_MODE_MULTIPLE_VALUES, NULL, NULL, NULL);
02055 
02056     cdesc = (cp_connection_descriptor *) prm;
02057     sock = cp_connection_descriptor_get_socket(cdesc);
02058     fd = cp_connection_descriptor_get_fd(cdesc);
02059     hsock = (cp_httpsocket *) sock->owner;
02060 
02061     addr = cp_connection_descriptor_get_addr(cdesc);
02062 
02063 #ifdef __TRACE__
02064     cp_info("\n\n *** connection from %s:%d\n", 
02065             inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
02066 #endif
02067     
02068     done = 0;
02069 
02070     while (!done && !sock->closing && !cp_http_stopping)
02071     {
02072         svc = hsock->default_service;
02073         action = HTTP_CONNECTION_POLICY_DEFAULT;
02074 
02075         /* read client request */
02076         if ((rc = read_request(req_list, cdesc)) == -1) /* io error */
02077         {
02078             rc = errno;
02079             cp_perror(CP_IO_ERROR, rc, "serving request from %s:%d on port %d",
02080                       inet_ntoa(addr->sin_addr), addr->sin_port,
02081                       sock->port);
02082             continue; //~~ check errno to decide whether to continue or break
02083         }
02084 
02085         /* handle empty requests or EOF */
02086         if (rc == 0) break; 
02087 
02088         /* parse request and write response */
02089         while ((request = cp_list_remove_head(req_list)))
02090         {
02091             cp_http_response *res;
02092             long err = (long) request;
02093             if (err < 0)
02094             {
02095                 cp_http_write_server_error(cdesc, -err);
02096                 continue;
02097             }
02098             
02099             request->connection = cdesc;
02100 #ifdef CP_USE_HTTP_SESSIONS
02101             /* catch new session redirect and send pending responses */
02102             res = NULL;
02103             if (request->session && request->session->fresh)
02104             {
02105 #ifdef CP_USE_COOKIES
02106                 if (request->session->type == SESSION_TYPE_COOKIE &&
02107                     cp_http_request_get_parameter(request, CP_HTTP_SESSION_PRM))
02108                     res = cp_http_new_session_redirect(request, 2);
02109                 else
02110 #endif /* CP_USE_COOKIES */
02111                 {
02112                     res = cp_hashlist_get(hsock->pending, request->session->sid);
02113                     if (res)
02114                     {
02115                         res->request = request;
02116                         request->session->fresh = 0;
02117                         cp_hashlist_remove(hsock->pending, request->session->sid);
02118                     }
02119                 }
02120             }
02121 
02122             if (res == NULL)
02123 #endif /* CP_USE_HTTP_SESSIONS */
02124             {
02125                 res = cp_http_response_create(request);
02126                 if (res == NULL)
02127                 {
02128                     cp_error(CP_MEMORY_ALLOCATION_FAILURE, "couldn\'t create response wrapper");
02129                     cp_http_request_dump(request);
02130                     cp_http_request_delete(request);
02131                     continue;
02132                 }
02133 
02134                 /* determine service to invoke */
02135                 if (hsock->service)
02136                 {
02137                     cp_http_service *desc;
02138                     void *ptr;
02139                     cp_trie_prefix_match(hsock->service, request->uri, &ptr);
02140                     desc = ptr; /* prevents 'dereferencing type-punned pointer 
02141                                    will break strict-aliasing rules' warning */
02142                     if (desc) svc = desc->service;
02143 #ifdef __TRACE__
02144                     if (desc) DEBUGMSG("invoking service [%s]", desc->name);
02145 #endif /* __TRACE__ */
02146                 }
02147                 if (svc == NULL)
02148                     svc = cp_http_default_response;
02149 
02150                 action = (*svc)(request, res);
02151 
02152                 if (action != res->connection) //~~
02153                     if (res->connection == HTTP_CONNECTION_POLICY_DEFAULT)
02154                         res->connection = (connection_policy) action;
02155                 
02156 #ifdef CP_USE_HTTP_SESSIONS
02157                 /* if service created a new session, start it */
02158                 if (res->request->session)
02159                 { /* fresh session - rewrite url, set cookie, see what works */
02160                     if (res->request->session->fresh)
02161                     {
02162                         if (hsock->pending == NULL)
02163                             hsock->pending = 
02164                                 cp_hashlist_create(1, cp_hash_string, 
02165                                                    cp_hash_compare_string);
02166                         cp_hashlist_append(hsock->pending, 
02167                                            res->request->session->sid, res);
02168                         /* redirect to uri?cpsid=... */
02169                         res = cp_http_new_session_redirect(request, 1);
02170                     }
02171                 }
02172 #endif /* CP_USE_HTTP_SESSIONS */
02173             }
02174 
02175             if (!res->skip) /* skip flag suppresses response writing */
02176                 cp_http_response_write(cdesc, res);
02177             cp_http_response_delete(res);
02178 
02179             keepalive = get_keepalive(hsock, request); //~~ check all requests
02180             if (action == HTTP_CONNECTION_POLICY_DEFAULT)
02181                 action = request->version == HTTP_1_1 ? 
02182                     HTTP_CONNECTION_POLICY_KEEP_ALIVE : 
02183                     HTTP_CONNECTION_POLICY_CLOSE;
02184         
02185             cp_http_request_delete(request);
02186         }
02187 //      else /* parse failed, rc should be set */
02188 //          cp_http_write_server_error(cdesc, rc);
02189 
02190         /* handle connection (keepalive or close as required) */
02191         switch (action)
02192         {
02193             case HTTP_CONNECTION_POLICY_KEEP_ALIVE: // select here
02194                 if ((rc = cp_httpselect(hsock, keepalive, fd)) < 0)
02195                     done = 1;
02196 #ifdef __TRACE__
02197                 DEBUGMSG("rc: %d", rc);
02198 #endif 
02199                 break;
02200 
02201             case HTTP_CONNECTION_POLICY_CLOSE:
02202                 done = 1;
02203                 break;
02204         }
02205     }
02206 
02207     cp_list_destroy_custom(req_list, 
02208             (cp_destructor_fn) cp_http_request_delete);
02209 
02210 #ifdef __TRACE__
02211     if (rc == 0)
02212         cp_info("done serving connection from %s:%d\n", 
02213                 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
02214 #endif
02215 
02216     cp_connection_descriptor_destroy(cdesc);
02217 
02218     return NULL;
02219 }
02220 

Generated on Mon Dec 5 23:00:22 2011 for cprops by  doxygen 1.4.7