PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/chm_http.c

https://github.com/jjuran/CHMLib
C | 367 lines | 272 code | 52 blank | 43 comment | 41 complexity | bc11bc5d3e77ca9ef6e970b7f1ffc441 MD5 | raw file
  1. /* $Id: chm_http.c,v 1.7 2002/10/08 03:43:33 jedwin Exp $ */
  2. /***************************************************************************
  3. * chm_http.c - CHM archive test driver *
  4. * ------------------- *
  5. * *
  6. * author: Jed Wing <jedwin@ugcs.caltech.edu> *
  7. * notes: This is a slightly more complex test driver for the chm *
  8. * routines. It also serves the purpose of making .chm html *
  9. * help files viewable from a text mode browser, which was my *
  10. * original purpose for all of this. *
  11. * *
  12. * It is not included with the expectation that it will be of *
  13. * use to others; nor is it included as an example of a *
  14. * stunningly good implementation of an HTTP server. It is, *
  15. * in fact, probably badly broken for any serious usage. *
  16. * *
  17. * Nevertheless, it is another example program, and it does *
  18. * serve a purpose for me, so I've included it as well. *
  19. ***************************************************************************/
  20. /***************************************************************************
  21. * *
  22. * This program is free software; you can redistribute it and/or modify *
  23. * it under the terms of the GNU Lesser General Public License as *
  24. * published by the Free Software Foundation; either version 2.1 of the *
  25. * License, or (at your option) any later version. *
  26. * *
  27. ***************************************************************************/
  28. #include "chm_lib.h"
  29. /* standard system includes */
  30. #define _REENTRANT
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #if __sun || __sgi
  35. #include <strings.h>
  36. #define strrchr rindex
  37. #endif
  38. /* includes for networking */
  39. #include <sys/socket.h>
  40. #include <sys/types.h>
  41. #include <netinet/in.h>
  42. /* threading includes */
  43. #include <pthread.h>
  44. #include <getopt.h>
  45. int config_port = 8080;
  46. char config_bind[65536] = "0.0.0.0";
  47. static void usage(const char *argv0)
  48. {
  49. #ifdef CHM_HTTP_SIMPLE
  50. fprintf(stderr, "usage: %s <filename>\n", argv0);
  51. #else
  52. fprintf(stderr, "usage: %s [--port=PORT] [--bind=IP] <filename>\n", argv0);
  53. #endif
  54. exit(1);
  55. }
  56. static void chmhttp_server(const char *filename);
  57. int main(int c, char **v)
  58. {
  59. #ifdef CHM_HTTP_SIMPLE
  60. if (c < 2)
  61. usage(v[0]);
  62. /* run the server */
  63. chmhttp_server(v[1]);
  64. #else
  65. int optindex = 0;
  66. struct option longopts[] =
  67. {
  68. { "port", required_argument, 0, 'p' },
  69. { "bind", required_argument, 0, 'b' },
  70. { "help", no_argument, 0, 'h' },
  71. { 0, 0, 0, 0 }
  72. };
  73. while (1)
  74. {
  75. int o;
  76. o = getopt_long (c, v, "n:b:h", longopts, &optindex);
  77. if (o < 0)
  78. {
  79. break;
  80. }
  81. switch (o)
  82. {
  83. case 'p':
  84. config_port = atoi (optarg);
  85. if (config_port <= 0)
  86. {
  87. fprintf(stderr, "bad port number (%s)\n", optarg);
  88. exit(1);
  89. }
  90. break;
  91. case 'b':
  92. strncpy (config_bind, optarg, 65536);
  93. config_bind[65535] = '\0';
  94. break;
  95. case 'h':
  96. usage (v[0]);
  97. break;
  98. }
  99. }
  100. if (optind + 1 != c)
  101. {
  102. usage (v[0]);
  103. }
  104. /* run the server */
  105. chmhttp_server(v[optind]);
  106. #endif
  107. /* NOT REACHED */
  108. return 0;
  109. }
  110. struct chmHttpServer
  111. {
  112. int socket;
  113. struct chmFile *file;
  114. };
  115. struct chmHttpSlave
  116. {
  117. int fd;
  118. struct chmHttpServer *server;
  119. };
  120. static void *_slave(void *param);
  121. static void chmhttp_server(const char *filename)
  122. {
  123. struct chmHttpServer server;
  124. struct chmHttpSlave *slave;
  125. struct sockaddr_in bindAddr;
  126. int addrLen;
  127. pthread_t tid;
  128. int one = 1;
  129. /* open file */
  130. if ((server.file = chm_open(filename)) == NULL)
  131. {
  132. fprintf(stderr, "couldn't open file '%s'\n", filename);
  133. exit(2);
  134. }
  135. /* create socket */
  136. server.socket = socket(AF_INET, SOCK_STREAM, 0);
  137. memset(&bindAddr, 0, sizeof(struct sockaddr_in));
  138. bindAddr.sin_family = AF_INET;
  139. bindAddr.sin_port = htons(config_port);
  140. bindAddr.sin_addr.s_addr = inet_addr(config_bind);
  141. if (setsockopt (server.socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
  142. {
  143. perror ("setsockopt");
  144. exit(3);
  145. }
  146. if (bind(server.socket,
  147. (struct sockaddr *)&bindAddr,
  148. sizeof(struct sockaddr_in)) < 0)
  149. {
  150. close(server.socket);
  151. server.socket = -1;
  152. fprintf(stderr, "couldn't bind to ip %s port %d\n", config_bind, config_port);
  153. exit(3);
  154. }
  155. /* listen for connections */
  156. listen(server.socket, 75);
  157. addrLen = sizeof(struct sockaddr);
  158. while(1)
  159. {
  160. slave = (struct chmHttpSlave *)malloc(sizeof(struct chmHttpSlave));
  161. slave->server = &server;
  162. slave->fd = accept(server.socket, (struct sockaddr *)&bindAddr, &addrLen);
  163. if (slave->fd == -1)
  164. break;
  165. pthread_create(&tid, NULL, _slave, (void *)slave);
  166. pthread_detach(tid);
  167. }
  168. free(slave);
  169. }
  170. static void service_request(int fd, struct chmFile *file);
  171. static void *_slave(void *param)
  172. {
  173. struct chmHttpSlave *slave;
  174. struct chmFile *file;
  175. /* grab our relevant information */
  176. slave = (struct chmHttpSlave *)param;
  177. file = slave->server->file;
  178. /* handle request */
  179. service_request(slave->fd, file);
  180. /* free our resources and return */
  181. close(slave->fd);
  182. free(slave);
  183. return NULL;
  184. }
  185. static const char CONTENT_404[] = "HTTP/1.1 404 File not found\r\nConnection: close\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n<html><head><title>404 File Not Found</title></head><body><h1>404 File not found</h1></body></html>\r\n";
  186. static const char CONTENT_500[] = "HTTP/1.1 500 Unknown thing\r\nConnection: close\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n<html><head><title>500 Unknown thing</title></head><body><h1>500 Unknown thing</h1></body></html>\r\n";
  187. static const char INTERNAL_ERROR[] = "HTTP/1.1 500 Internal error\r\nConnection: close\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n<html><head><title>500 Unknown thing</title></head><body><h1>500 Server error</h1></body></html>\r\n";
  188. struct mime_mapping
  189. {
  190. const char *ext;
  191. const char *ctype;
  192. };
  193. struct mime_mapping mime_types[] =
  194. { { ".htm", "text/html" },
  195. { ".html", "text/html" },
  196. { ".css", "text/css" },
  197. { ".gif", "image/gif" },
  198. { ".jpg", "image/jpeg" },
  199. { ".jpeg", "image/jpeg" },
  200. { ".jpe", "image/jpeg" },
  201. { ".bmp", "image/bitmap" },
  202. { ".png", "image/png" }
  203. };
  204. static const char *lookup_mime(const char *ext)
  205. {
  206. int i;
  207. if (ext != NULL)
  208. {
  209. for (i=0; i<sizeof(mime_types)/sizeof(struct mime_mapping); i++)
  210. {
  211. if (strcasecmp(mime_types[i].ext, ext) == 0)
  212. return mime_types[i].ctype;
  213. }
  214. }
  215. return "application/octet-stream";
  216. }
  217. static int _print_ui_index(struct chmFile *h, struct chmUnitInfo *ui,
  218. void *context)
  219. {
  220. FILE *fout = (FILE*) context;
  221. fprintf(fout,
  222. "<tr>"
  223. "<td align=right>%8d\n</td>"
  224. "<td><a href=\"%s\">%s</a></td>"
  225. "</tr>",
  226. (int)ui->length, ui->path, ui->path);
  227. return CHM_ENUMERATOR_CONTINUE;
  228. }
  229. static void deliver_index(FILE *fout, struct chmFile *file)
  230. {
  231. fprintf(fout,
  232. "HTTP/1.1 200 OK\r\n"
  233. "Connection: close\r\n"
  234. /* "Content-Length: 1000000\r\n" */
  235. "Content-Type: text/html\r\n\r\n"
  236. "<h2><u>CHM contents:</u></h2>"
  237. "<body><table>"
  238. "<tr><td><h5>Size:</h5></td><td><h5>File:</h5></td></tr>"
  239. "<tt>");
  240. if (! chm_enumerate(file, CHM_ENUMERATE_ALL, _print_ui_index, fout))
  241. fprintf(fout,"<br> *** ERROR ***\r\n");
  242. fprintf(fout,"</tt> </table></body></html>");
  243. }
  244. static void deliver_content(FILE *fout, const char *filename, struct chmFile *file)
  245. {
  246. struct chmUnitInfo ui;
  247. const char *ext;
  248. unsigned char buffer[65536];
  249. int swath, offset;
  250. if (strcmp(filename,"/") == 0)
  251. {
  252. deliver_index(fout,file);
  253. fclose(fout);
  254. return;
  255. }
  256. /* try to find the file */
  257. if (chm_resolve_object(file, filename, &ui) != CHM_RESOLVE_SUCCESS)
  258. {
  259. fprintf(fout, CONTENT_404);
  260. fclose(fout);
  261. return;
  262. }
  263. /* send the file back */
  264. ext = strrchr(filename, '.');
  265. fprintf(fout, "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: %d\r\nContent-Type: %s\r\n\r\n",
  266. (int)ui.length,
  267. lookup_mime(ext));
  268. /* pump the data out */
  269. swath = 65536;
  270. offset = 0;
  271. while (offset < ui.length)
  272. {
  273. if ((ui.length - offset) < 65536)
  274. swath = ui.length - offset;
  275. else
  276. swath = 65536;
  277. swath = (int)chm_retrieve_object(file, &ui, buffer, offset, swath);
  278. offset += swath;
  279. fwrite(buffer, 1, swath, fout);
  280. }
  281. fclose(fout);
  282. }
  283. static void service_request(int fd, struct chmFile *file)
  284. {
  285. char buffer[4096];
  286. char buffer2[4096];
  287. char *end;
  288. FILE *fout = fdopen(fd, "w+b");
  289. if (fout == NULL)
  290. {
  291. perror("chm_http: failed to fdopen client stream");
  292. write(fd, INTERNAL_ERROR, strlen(INTERNAL_ERROR));
  293. close(fd);
  294. return;
  295. }
  296. fgets(buffer, 4096, fout);
  297. while (1)
  298. {
  299. if (fgets(buffer2, 4096, fout) == NULL)
  300. break;
  301. if (buffer2[0] == '\r' || buffer2[0] == '\n' || buffer2[0] == '\0')
  302. break;
  303. }
  304. end = strrchr(buffer, ' ');
  305. if (strncmp(end+1, "HTTP", 4) == 0)
  306. *end = '\0';
  307. if (strncmp(buffer, "GET ", 4) == 0)
  308. deliver_content(fout, buffer+4, file);
  309. else
  310. {
  311. fprintf(fout, CONTENT_500);
  312. fclose(fout);
  313. return;
  314. }
  315. }