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
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;
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
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
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
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
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
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
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
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
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