testhttpsrv.c

00001 #include <stdio.h>
00002 #include <string.h>
00003 #include <stdlib.h>
00004 #include <unistd.h>
00005 #include <signal.h>
00006 #include <sys/types.h>
00007 #include <sys/stat.h>
00008 #include <ctype.h>
00009 #include <errno.h>
00010 
00011 #include "config.h"
00012 #include "socket.h"
00013 #include "http.h"
00014 #include "util.h"
00015 #include "hashtable.h"
00016 #include "log.h"
00017 #include "str.h"
00018 
00019 #ifdef CP_USE_SSL
00020 #include <openssl/ssl.h>
00021 #endif
00022 
00023 #ifndef CP_HAS_STRLCAT
00024 #define strlcat(s1, s2, len) strcat((s1), (s2))
00025 #endif /* CP_HAS_STRLCAT */
00026 
00027 #define BUFLEN 0x800
00028 #define LINELEN 0x200
00029 
00030 static int port = 5000;
00031 
00032 #define DEFAULT_DOCUMENT_ROOT "."
00033 #define DEFAULT_MIME_TYPES_DEFINITION_FILE "svc/mime.types"
00034 
00035 static char *document_root = DEFAULT_DOCUMENT_ROOT;
00036 static char *mimetypes_filename = DEFAULT_MIME_TYPES_DEFINITION_FILE;
00037 static cp_hashtable *mimemap;
00038 
00039 int load_mime_types(char *filename)
00040 {
00041     FILE *fp;
00042     char mimebuf[LINELEN];
00043     int rc = 0;
00044     char *name;
00045     char *ext;
00046     char *curr;
00047 
00048     mimemap = 
00049         cp_hashtable_create_by_option(COLLECTION_MODE_NOSYNC | 
00050                                       COLLECTION_MODE_COPY | 
00051                                       COLLECTION_MODE_DEEP, 
00052                                       500,
00053                                       cp_hash_string,
00054                                       cp_hash_compare_string,
00055                                       (cp_copy_fn) strdup, 
00056                                       (cp_destructor_fn) free,
00057                                       (cp_copy_fn) strdup, 
00058                                       (cp_destructor_fn) free);
00059     fp = fopen(filename, "r");
00060     if (fp == NULL)
00061     {
00062         cp_error(CP_INVALID_VALUE, "can\'t open %s", filename);
00063         cp_hashtable_destroy(mimemap);
00064         return -1;
00065     }
00066 
00067     while (fgets(mimebuf, LINELEN, fp))
00068     {
00069         if (mimebuf[0] == '#') continue;
00070         name = curr = mimebuf;
00071         while (*curr && !isspace(*curr)) curr++;
00072         if (*curr == '\0') continue; /* no extension for this type */
00073         *curr++ = '\0';
00074 
00075         while (1)
00076         {
00077             while (*curr && isspace(*curr)) curr++;
00078             ext = curr;
00079             while (*curr && !isspace(*curr)) curr++;
00080             if (strlen(ext))
00081             {
00082                 *curr++ = '\0';
00083                 cp_hashtable_put(mimemap, ext, name);
00084             }
00085             else
00086                 break;
00087         }
00088     }
00089 
00090     fclose(fp);
00091     return rc;
00092 }
00093 
00094 static int init_file_service(char *mimetypes_filename, char *doc_path)
00095 {
00096     int rc = 0;
00097 
00098     if ((rc = checkdir(doc_path)))
00099         cp_fatal(rc, "can\'t open document root at [%s]", doc_path);
00100 
00101     if ((rc = load_mime_types(mimetypes_filename)))
00102         cp_fatal(rc, "can\'t load mime types from [%s], sorry", 
00103                  mimetypes_filename); 
00104 
00105     document_root = doc_path;
00106     
00107     return rc;
00108 }
00109 
00110 static int stop_file_service()
00111 {
00112     cp_hashtable_destroy(mimemap);
00113     return 0;
00114 }
00115 
00116 char *HTTP404_PAGE = 
00117     "<html><head><title>404 NOT FOUND</title></head>\n"
00118     "<body><h1>404 NOT FOUND</h1>\n"
00119     "The page you are looking for is not here.\n<p>"
00120     "It may be somewhere else."
00121     "</body></html>";
00122     
00123 char *HTTP404_PAGE_uri = 
00124     "<html><head><title>404 NOT FOUND</title></head>\n"
00125     "<body><h1>404 NOT FOUND</h1>\n"
00126     "Can\'t find %s on this server;\n<p>"
00127     "No. it is not here."
00128     "</body></html>";
00129 
00130 #define FBUFSIZE 0x4000
00131 #define PATHLEN 0x400
00132 
00133 int file_service(cp_http_request *request, cp_http_response *response)
00134 {
00135     int rc = 0;
00136     char *ext;
00137     char path[PATHLEN];
00138     int uri_len;
00139     char buf[FBUFSIZE];
00140     FILE *fp;
00141     cp_string *body = NULL;
00142 
00143 #ifdef DEBUG
00144     cp_http_request_dump(request);
00145 #endif
00146 
00147     ext = strrchr(request->uri, '.');
00148     if (ext) 
00149         cp_http_response_set_content_type_string(response, 
00150                                                  cp_hashtable_get(mimemap, ++ext));
00151 
00152     /* check len, avoid buffer overrun */
00153     uri_len = strlen(request->uri);
00154     if (uri_len + strlen(document_root) >= PATHLEN)
00155     {
00156         cp_http_response_set_content_type(response, HTML);
00157         cp_http_response_set_status(response, HTTP_404_NOT_FOUND);
00158         response->body = strdup(HTTP404_PAGE);
00159         return HTTP_CONNECTION_POLICY_CLOSE;
00160     }
00161         
00162 #ifdef CP_HAS_SNPRINTF
00163     snprintf(path, PATHLEN, "%s%s", document_root, request->uri);
00164 #else
00165     sprintf(path, "%s%s", document_root, request->uri);
00166 #endif /* CP_HAS_SNPRINTF */
00167     if (path[strlen(path) - 1] == '/') 
00168     {
00169         strlcat(path, "index.html", PATHLEN);
00170         response->content_type = HTML;
00171     }
00172 
00173     fp = fopen(path, "rb");
00174     if (fp == NULL)
00175     {
00176         cp_http_response_set_content_type(response, HTML);
00177         cp_http_response_set_status(response, HTTP_404_NOT_FOUND);
00178 #ifdef CP_HAS_SNPRINTF
00179         snprintf(buf, FBUFSIZE, HTTP404_PAGE_uri, request->uri);
00180 #else
00181         sprintf(buf, HTTP404_PAGE_uri, request->uri);
00182 #endif /* CP_HAS_SNPRINTF */
00183         response->body = strdup(buf);
00184         return HTTP_CONNECTION_POLICY_CLOSE;
00185     }
00186 
00187 #ifdef __TRACE__
00188     DEBUGMSG("retrieving [%s]", path);
00189 #endif
00190     while ((rc = fread(buf, 1, FBUFSIZE, fp)) > 0)
00191     {
00192         if (body == NULL)
00193             body = cp_string_create(buf, rc);
00194         else
00195             cp_string_cat_bin(body, buf, rc);
00196     }
00197     fclose(fp);
00198     
00199     cp_http_response_set_status(response, HTTP_200_OK);
00200 
00201     response->content = body;
00202 
00203     return HTTP_CONNECTION_POLICY_KEEP_ALIVE;
00204 }
00205 
00206 #ifdef CP_USE_SSL
00207 char *usage = "%s [-p port] [-s -c certificate_file -k key_file -v -f -o -m mimetypes -d path]\n"
00208 "  -s   use ssl\n"
00209 "  -v   verify client\n"
00210 "  -f   fail connection on verification failure\n"
00211 "  -o   only verify client once\n"
00212 #else
00213 char *usage = "%s [-p port] [-m mimetypes] [-d path]\n"
00214 #endif
00215 "  -m mimetypes  --  mime types definition file\n"
00216 "  -d path       --  document root\n";
00217 
00218 #ifdef CP_USE_SSL
00219 char *certificate_file = NULL;
00220 char *key_file = NULL;
00221 int verify = 0;
00222 int use_ssl = 0;
00223 #endif
00224 
00225 void process_cmdline(int argc, char *argv[])
00226 {
00227     int c;
00228 #ifdef CP_USE_SSL
00229     int rc;
00230     struct stat buf;
00231 
00232     while ((c = getopt(argc, argv, "sp:c:k:vfom:d:")) != -1)
00233 #else
00234     while ((c = getopt(argc, argv, "p:")) != -1)
00235 #endif
00236     {
00237         switch (c)
00238         {
00239             case 'p': 
00240                 port = atoi(optarg);
00241                 if (port == 0)
00242                 {
00243                     fprintf(stderr, "%s: bad port number [%s]\n", 
00244                             argv[0], optarg);
00245                     exit(1);
00246                 }
00247                 break;
00248                 
00249             case 'm':
00250                 mimetypes_filename = optarg;
00251                 break; 
00252 
00253             case 'd':
00254                 document_root = optarg;
00255                 break;
00256             
00257 #ifdef CP_USE_SSL                      
00258             case 's': 
00259                 use_ssl = 1;
00260                 break;
00261 
00262             case 'c': 
00263                 rc = stat(optarg, &buf);
00264                 if (rc)
00265                 {
00266                     fprintf(stderr, stat_error_fmt(errno), argv[0], optarg);
00267                     exit(1);
00268                 }
00269                 certificate_file = strdup(optarg); 
00270                 break;
00271 
00272             case 'k': 
00273                 rc = stat(optarg, &buf);
00274                 if (rc)
00275                 {
00276                     fprintf(stderr, stat_error_fmt(errno), argv[0], optarg);
00277                     exit(1);
00278                 }
00279                 key_file = strdup(optarg); 
00280                 break;
00281 
00282             case 'v': 
00283                 verify |= SSL_VERIFY_PEER;
00284                 break;
00285 
00286             case 'f': 
00287                 verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
00288                 break;
00289 
00290             case 'o': 
00291                 verify |= SSL_VERIFY_CLIENT_ONCE;
00292                 break;
00293 #endif
00294             default:  
00295                 fprintf(stderr, "%s: unknown option \'%c\'\n", argv[0], c);
00296                 fprintf(stderr, usage, argv[0]);
00297                 exit(1);
00298                 break;
00299                 
00300         }
00301     }
00302 }
00303 
00304 static int hitcount = 1;
00305 
00306 int hitcount_service(cp_http_request *request, cp_http_response *response)
00307 {
00308     char buf[BUFLEN];
00309     char sbuf[BUFLEN / 2];
00310     char **key;
00311     int i;
00312     int *pcount;
00313 
00314     cp_http_session *session = cp_http_request_get_session(request, 1);
00315     if (cp_http_session_is_fresh(session))
00316     {
00317         pcount = malloc(sizeof(int *));
00318         *pcount = 1;
00319         cp_http_session_set_dtr(session, "pcount", pcount, free);
00320     }
00321     else
00322     {
00323         pcount = cp_http_session_get(session, "pcount");
00324         (*pcount)++;
00325     }
00326 
00327     cp_http_response_set_content_type(response, HTML);
00328 #ifdef CP_HAS_SNPRINTF
00329     snprintf(buf, BUFLEN, 
00330         "<html>\n<head>\n<title>cp_httpsocket test </title>\n</head>\n"
00331         "<body><h1>hit count: %d</h1>\n"
00332         "you've visited this page %d times.<p>", hitcount++, *pcount);
00333 #else
00334     sprintf(buf, 
00335         "<html>\n<head>\n<title>cp_httpsocket test </title>\n</head>\n"
00336         "<body><h1>hit count: %d</h1>\n"
00337         "you've visited this page %d times.<p>", hitcount++, *pcount);
00338 #endif /* CP_HAS_SNPRINTF */
00339 
00340     if (cp_hashtable_count(request->header))
00341     {
00342         strlcat(buf, "<hr>\n<h3> your headers: </h3>\n<ul>\n", BUFLEN);
00343         key = (char **) cp_hashtable_get_keys(request->header);
00344         for (i = 0; i < cp_hashtable_count(request->header); i++)
00345         {
00346 #ifdef CP_HAS_SNPRINTF
00347             snprintf(sbuf, BUFLEN / 2, "<li> %s: %s\n", 
00348                     key[i], cp_http_request_get_header(request, key[i]));
00349 #else
00350             sprintf(sbuf, "<li> %s: %s\n", 
00351                     key[i], cp_http_request_get_header(request, key[i]));
00352 #endif /* CP_HAS_SNPRINTF */
00353             strlcat(buf, sbuf, BUFLEN);
00354         }
00355         if (key) free(key);
00356         strlcat(buf, "</ul>\n", BUFLEN);
00357     }
00358     
00359     if (request->cookie)
00360     {
00361         int n = cp_vector_size(request->cookie);
00362         strlcat(buf, "<hr>\n<h3> cookies: </h3>\n<ul>\n", BUFLEN);
00363         for (i = 0; i < n; i++)
00364         {
00365 #ifdef CP_HAS_SNPRINTF
00366             snprintf(sbuf, BUFLEN / 2, "<li> %s\n", (char *) cp_vector_element_at(request->cookie, i));
00367 #else
00368             sprintf(sbuf, "<li> %s\n", (char *) cp_vector_element_at(request->cookie, i));
00369 #endif /* CP_HAS_SNPRINTF */
00370             strlcat(buf, sbuf, BUFLEN);
00371         }
00372         strlcat(buf, "</ul>\n", BUFLEN);
00373     }
00374         
00375     if (request->prm)
00376     {
00377         strlcat(buf, "<hr>\n<h3> cgi variables: </h3>\n<ul>\n", BUFLEN);
00378         key = (char **) cp_hashtable_get_keys(request->prm);
00379         for (i = 0; i < cp_hashtable_count(request->prm); i++)
00380         {
00381 #ifdef CP_HAS_SNPRINTF
00382             snprintf(sbuf, BUFLEN / 2, "<li> %s: %s\n", 
00383                     key[i], cp_http_request_get_parameter(request, key[i]));
00384 #else
00385             sprintf(sbuf, "<li> %s: %s\n", 
00386                     key[i], cp_http_request_get_parameter(request, key[i]));
00387 #endif /* CP_HAS_SNPRINTF */
00388             strlcat(buf, sbuf, BUFLEN);
00389         }
00390         if (key) free(key);
00391         strlcat(buf, "</ul>\n", BUFLEN);
00392     }
00393 
00394     strlcat(buf, "<hr>\nfree image: <img src=\"/img/free_image.jpg\">\n", BUFLEN);
00395     strlcat(buf, "</body>\n</html>\n", BUFLEN);
00396     response->body = strdup(buf);
00397     
00398     DEBUGMSG("hitcount_service: dumping request");
00399     cp_http_request_dump(request);
00400 
00401     return HTTP_CONNECTION_POLICY_KEEP_ALIVE;
00402 }
00403 
00404 void cpsvc_signal_handler(int sig)
00405 {
00406     switch (sig)
00407     {
00408         case SIGINT:
00409             DEBUGMSG("testhttpsrv: SIGINT - stopping");
00410             cp_socket_stop_all();
00411             break;
00412 
00413         case SIGTERM:
00414             DEBUGMSG("testhttpsrv: SIGTERM - stopping");
00415             cp_socket_stop_all();
00416             break;
00417     }
00418 }
00419 
00420 int main(int argc, char *argv[])
00421 {
00422     int rc = 0;
00423     struct sigaction act;
00424     cp_httpsocket *sock;
00425 
00426     cp_http_service *hitcount_svc = 
00427         cp_http_service_create("hitcount", "/test", hitcount_service);
00428     
00429     process_cmdline(argc, argv);
00430 
00431     act.sa_handler = cpsvc_signal_handler;
00432     sigemptyset(&act.sa_mask);
00433     act.sa_flags = 0;
00434 
00435     cp_log_init("testhttpsrv.log", 0);
00436     cp_http_init();
00437 
00438     /* override default http signal handler for stopping */
00439     sigaction(SIGINT, &act, NULL);
00440     sigaction(SIGTERM, &act, NULL);
00441 
00442     cp_info("%s: starting", argv[0]);
00443 
00444     if ((rc = init_file_service(mimetypes_filename, document_root)))
00445     {
00446         cp_error(rc, "%s: can\'t start", argv[0]);
00447         goto DONE;
00448     }
00449         
00450 #ifdef CP_USE_SSL
00451     if (use_ssl)
00452     {
00453         sock = cp_httpsocket_create_ssl(port, 
00454                     (cp_http_service_callback) file_service,                    
00455                     certificate_file, key_file, verify);
00456     }
00457     else
00458 #endif
00459     sock = cp_httpsocket_create(port, file_service);
00460     cp_httpsocket_register_service(sock, hitcount_svc);
00461     if (sock)
00462     {
00463         cp_info("%s: cp_httpsocket server starting on port %d", argv[0], port);
00464         cp_httpsocket_listen(sock);
00465         cp_info("%s: cp_httpsocket server on port %d: stopping", argv[0], port);
00466         cp_httpsocket_delete(sock);
00467     }
00468 
00469 DONE:
00470     stop_file_service();
00471     cp_http_shutdown();
00472     cp_info("done");
00473     cp_log_close();
00474 
00475     return rc;
00476 }
00477 

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