wn_module.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. * File : wn_module.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
  5. *
  6. * This software is dual-licensed: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation. For the terms of this
  9. * license, see <http://www.gnu.org/licenses/>.
  10. *
  11. * You are free to use this software under the terms of the GNU General
  12. * Public License, but WITHOUT ANY WARRANTY; without even the implied
  13. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. * See the GNU General Public License for more details.
  15. *
  16. * Alternatively for commercial application, you can contact us
  17. * by email <business@rt-thread.com> for commercial license.
  18. *
  19. * Change Logs:
  20. * Date Author Notes
  21. * 2011-08-02 Bernard the first version
  22. * 2012-06-25 Bernard add SSI and Upload module
  23. */
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <time.h>
  27. #include <webnet.h>
  28. #include <wn_module.h>
  29. #include <wn_utils.h>
  30. #ifdef RT_USING_DFS
  31. #include <dfs_posix.h>
  32. #endif
  33. static int _webnet_module_system_init(struct webnet_session *session, int event)
  34. {
  35. #ifdef WEBNET_USING_LOG
  36. webnet_module_log(session, event);
  37. #endif
  38. #ifdef WEBNET_USING_SSL
  39. webnet_module_ssl(session, event);
  40. #endif
  41. #ifdef WEBNET_USING_CGI
  42. webnet_module_cgi(session, event);
  43. #endif
  44. #ifdef WEBNET_USING_DMR
  45. webnet_module_dmr(session, event);
  46. #endif
  47. return WEBNET_MODULE_CONTINUE;
  48. }
  49. static int _webnet_module_system_uri_physical(struct webnet_session *session, int event)
  50. {
  51. int result;
  52. result = WEBNET_MODULE_CONTINUE;
  53. #ifdef WEBNET_USING_LOG
  54. webnet_module_log(session, event);
  55. #endif
  56. #ifdef WEBNET_USING_ALIAS
  57. result = webnet_module_alias(session, event);
  58. if (result == WEBNET_MODULE_FINISHED) return result;
  59. #endif
  60. #ifdef WEBNET_USING_AUTH
  61. result = webnet_module_auth(session, event);
  62. if (result == WEBNET_MODULE_FINISHED) return result;
  63. #endif
  64. #ifdef WEBNET_USING_CGI
  65. result = webnet_module_cgi(session, event);
  66. if (result == WEBNET_MODULE_FINISHED) return result;
  67. #endif
  68. #ifdef WEBNET_USING_DMR
  69. result = webnet_module_dmr(session, event);
  70. if (result == WEBNET_MODULE_FINISHED) return result;
  71. #endif
  72. #ifdef WEBNET_USING_UPLOAD
  73. result = webnet_module_upload(session, event);
  74. if (result == WEBNET_MODULE_FINISHED) return result;
  75. #endif
  76. return result;
  77. }
  78. static void _webnet_dofile_handle(struct webnet_session *session, int event)
  79. {
  80. int fd = session->user_data;
  81. if (event & WEBNET_EVENT_WRITE)
  82. {
  83. rt_size_t readbytes;
  84. rt_size_t length = RT_ALIGN_DOWN(WEBNET_SESSION_BUFSZ, 4);
  85. #ifdef WEBNET_USING_RANGE
  86. if(session->request->Range)
  87. {
  88. length = session->request->pos_end - session->request->pos_start;
  89. if(length == 0)
  90. {
  91. goto __exit;
  92. }
  93. if(length > WEBNET_SESSION_BUFSZ)
  94. {
  95. length = WEBNET_SESSION_BUFSZ;
  96. }
  97. lseek(fd, session->request->pos_start, SEEK_SET);
  98. session->request->pos_start += length;
  99. }
  100. #endif
  101. readbytes = read(fd, session->buffer, length);
  102. if (readbytes <= 0) /* end of file */
  103. goto __exit;
  104. if (webnet_session_write(session, session->buffer, readbytes) == 0)
  105. goto __exit;
  106. return;
  107. }
  108. __exit:
  109. close(fd);
  110. session->user_data = 0;
  111. session->session_event_mask = 0; /* clean event */
  112. /* destroy session */
  113. session->session_phase = WEB_PHASE_CLOSE;
  114. return;
  115. }
  116. static const struct webnet_session_ops _dofile_ops =
  117. {
  118. _webnet_dofile_handle,
  119. RT_NULL
  120. };
  121. /* send a file to http client */
  122. int webnet_module_system_dofile(struct webnet_session *session)
  123. {
  124. int fd = -1; /* file descriptor */
  125. struct stat file_stat;
  126. const char *mimetype;
  127. rt_size_t file_length;
  128. struct webnet_request *request;
  129. #if WEBNET_CACHE_LEVEL > 0
  130. char ctime_str[32];
  131. char gmtime_str[32];
  132. struct tm* info;
  133. int stat_result = -1;
  134. #endif /* WEBNET_CACHE_LEVEL */
  135. RT_ASSERT(session != RT_NULL);
  136. request = session->request;
  137. RT_ASSERT(request != RT_NULL);
  138. #if WEBNET_CACHE_LEVEL > 0
  139. #ifdef WEBNET_USING_GZIP
  140. /* get .gz Last-Modified. */
  141. if (request->support_gzip)
  142. {
  143. struct stat file_stat;
  144. char *path_gz = wn_malloc(strlen(request->path) + 4); /* ".gz\0" */
  145. if (path_gz != RT_NULL)
  146. {
  147. rt_sprintf(path_gz, "%s.gz", request->path);
  148. stat_result = stat(request->path, &file_stat);
  149. wn_free(path_gz);
  150. }
  151. if (stat_result == 0)
  152. {
  153. rt_enter_critical();
  154. strcpy(ctime_str, ctime((time_t *)&file_stat.st_mtime));
  155. rt_exit_critical();
  156. ctime_str[strlen(ctime_str) - 1] = '\0'; /* clear the end \n */
  157. if ((request->modified != RT_NULL)
  158. && (strcmp(request->modified, ctime_str) == 0))
  159. {
  160. request->result_code = 304;
  161. return WEBNET_MODULE_FINISHED;
  162. }
  163. }
  164. }
  165. /* .gz not exist, use raw. */
  166. #endif /* WEBNET_USING_GZIP */
  167. /* get Last-Modified. */
  168. if (stat_result != 0)
  169. {
  170. struct stat file_stat;
  171. stat_result = stat(request->path, &file_stat);
  172. if (stat_result == 0)
  173. {
  174. rt_enter_critical();
  175. info = localtime((time_t *)&file_stat.st_mtime);
  176. rt_memset(gmtime_str,0,32);
  177. strftime(gmtime_str,sizeof(ctime_str),"%a, %d %b %Y %H:%M:%S GMT",info);
  178. strcpy(ctime_str, ctime((time_t *)&file_stat.st_mtime));
  179. rt_exit_critical();
  180. ctime_str[strlen(ctime_str) - 1] = '\0'; /* clear the end \n */
  181. gmtime_str[strlen(gmtime_str)] = '\0'; /* clear the end \n */
  182. if ((request->modified != RT_NULL)
  183. && ((strcmp(request->modified, ctime_str) == 0)||strcmp(request->modified, gmtime_str) == 0))
  184. {
  185. request->result_code = 304;
  186. return WEBNET_MODULE_FINISHED;
  187. }
  188. }
  189. }
  190. #endif /* WEBNET_CACHE_LEVEL > 0 */
  191. /* get mime type */
  192. mimetype = mime_get_type(request->path);
  193. #ifdef WEBNET_USING_GZIP
  194. if (request->support_gzip)
  195. {
  196. char *path_gz = wn_malloc(strlen(request->path) + 4); /* ".gz\0" */
  197. if (path_gz != RT_NULL)
  198. {
  199. rt_sprintf(path_gz, "%s.gz", request->path);
  200. fd = open(path_gz, O_RDONLY, 0);
  201. wn_free(path_gz);
  202. if (fd < 0)
  203. {
  204. /* .gz not exist, use raw. */
  205. request->support_gzip = RT_FALSE;
  206. }
  207. }
  208. }
  209. /* .gz not exist, use raw. */
  210. #endif /* WEBNET_USING_GZIP */
  211. if (fd < 0 && stat(request->path, &file_stat) >= 0 && !S_ISDIR(file_stat.st_mode))
  212. {
  213. fd = open(request->path, O_RDONLY, 0);
  214. }
  215. if (fd < 0)
  216. {
  217. request->result_code = 404;
  218. return WEBNET_MODULE_FINISHED;
  219. }
  220. /* get file size */
  221. file_length = lseek(fd, 0, SEEK_END);
  222. /* seek to beginning of file */
  223. lseek(fd, 0, SEEK_SET);
  224. /*************todo**********************/
  225. #ifdef WEBNET_USING_RANGE
  226. if (request->Range)
  227. {
  228. char *range_start, *range_end;
  229. int32_t pos_start = 0;
  230. uint32_t pos_end = file_length - 1;
  231. range_start = strstr(request->Range, "bytes=");
  232. if (range_start)
  233. {
  234. range_start += 6;
  235. range_end = strstr(range_start, "-");
  236. if (range_start == range_end)
  237. pos_start = 0;
  238. else
  239. pos_start = atoi(range_start);
  240. /* send file to remote */
  241. if ((!range_end) || (strstr(range_start, ",")))
  242. {
  243. request->result_code = 400;
  244. goto _error_exit;
  245. }
  246. if (range_end)
  247. {
  248. *range_end = '\0';
  249. range_end += 1;
  250. pos_end = atoi(range_end);
  251. }
  252. }
  253. #ifdef WEBNET_USING_GZIP
  254. if (request->support_gzip)
  255. {
  256. pos_start = 0; /* */
  257. }
  258. #endif /* WEBNET_USING_GZIP */
  259. if ((pos_start >= file_length) || (pos_end >= file_length))
  260. {
  261. request->result_code = 416;
  262. webnet_session_set_header_status_line(session, request->result_code, "Requested Range Not Satisfiable");
  263. goto _error_exit;
  264. }
  265. if (lseek(fd, pos_start, SEEK_SET) != pos_start)
  266. {
  267. request->result_code = 500;
  268. goto _error_exit;
  269. }
  270. if (pos_end == 0)
  271. {
  272. pos_end = file_length - 1;
  273. }
  274. file_length = pos_end - pos_start + 1;
  275. request->result_code = 216;
  276. request->pos_start = pos_start;
  277. request->pos_end = pos_end;
  278. webnet_session_set_header_status_line(session, request->result_code, "Partial Content");
  279. webnet_session_printf(session, "Content-Range: %d-%d/%d\r\n", pos_start, pos_end, file_length);
  280. }else
  281. #endif /* WEBNET_USING_RANGE */
  282. /*************todo**********************/
  283. {
  284. /* send file to remote */
  285. request->result_code = 200;
  286. webnet_session_set_header_status_line(session, request->result_code, "OK");
  287. }
  288. #if WEBNET_CACHE_LEVEL > 0
  289. /* send Last-Modified. */
  290. webnet_session_printf(session,
  291. "Last-Modified: %s\r\n",
  292. ctime_str);
  293. #endif /* WEBNET_CACHE_LEVEL > 0 */
  294. #if WEBNET_CACHE_LEVEL > 1
  295. /* Cache-Control. */
  296. webnet_session_printf(session,
  297. "Cache-Control: max-age=%d\r\n",
  298. WEBNET_CACHE_MAX_AGE);
  299. #endif /* WEBNET_CACHE_LEVEL > 1 */
  300. /* send Content-Type. */
  301. webnet_session_printf(session,
  302. "Content-Type: %s\r\n",
  303. mimetype);
  304. /* send Content-Length. */
  305. webnet_session_printf(session,
  306. "Content-Length: %ld\r\n",
  307. file_length);
  308. #ifdef WEBNET_USING_KEEPALIVE
  309. if(session->request->connection == WEBNET_CONN_KEEPALIVE)
  310. {
  311. webnet_session_printf(session,
  312. "Connection: %s\r\n",
  313. "Keep-Alive");
  314. }
  315. else
  316. {
  317. webnet_session_printf(session,
  318. "Connection: %s\r\n",
  319. "close");
  320. }
  321. #else
  322. webnet_session_printf(session,
  323. "Connection: %s\r\n",
  324. "close");
  325. #endif
  326. #ifdef WEBNET_USING_GZIP
  327. if (request->support_gzip)
  328. {
  329. /* gzip deflate. */
  330. webnet_session_printf(session, "Content-Encoding: gzip\r\n");
  331. }
  332. #endif /* WEBNET_USING_GZIP */
  333. /* send Access-Control-Allow-Origin. */
  334. webnet_session_printf(session, "Access-Control-Allow-Origin:*\r\n");
  335. /* send http header end. */
  336. webnet_session_printf(session, "\r\n");
  337. if (file_length <= 0)
  338. {
  339. close(fd);
  340. return WEBNET_MODULE_FINISHED;
  341. }
  342. /*
  343. * set session write context
  344. */
  345. if (request->method != WEBNET_HEADER)
  346. {
  347. /* set dofile session ops */
  348. session->session_event_mask = WEBNET_EVENT_WRITE;
  349. session->user_data = (rt_uint32_t)fd;
  350. session->session_ops = &_dofile_ops;
  351. }
  352. return WEBNET_MODULE_FINISHED;
  353. _error_exit:
  354. if (fd >= 0)
  355. {
  356. close(fd);
  357. }
  358. return WEBNET_MODULE_FINISHED;
  359. }
  360. static int _webnet_module_system_uri_post(struct webnet_session *session, int event)
  361. {
  362. int result;
  363. result = WEBNET_MODULE_CONTINUE;
  364. #ifdef WEBNET_USING_LOG
  365. webnet_module_log(session, event);
  366. #endif
  367. #ifdef WEBNET_USING_LUA
  368. result = webnet_module_lua(session, event);
  369. if (result == WEBNET_MODULE_FINISHED) return result;
  370. #endif
  371. #ifdef WEBNET_USING_ASP
  372. result = webnet_module_asp(session, event);
  373. if (result == WEBNET_MODULE_FINISHED) return result;
  374. #endif
  375. #ifdef WEBNET_USING_SSI
  376. result = webnet_module_ssi(session, event);
  377. if (result == WEBNET_MODULE_FINISHED) return result;
  378. #endif
  379. #ifdef WEBNET_USING_DAV
  380. result = webnet_module_dav(session, event);
  381. if (result == WEBNET_MODULE_FINISHED) return result;
  382. #endif
  383. #ifdef WEBNET_USING_INDEX
  384. result = webnet_module_dirindex(session, event);
  385. if (result == WEBNET_MODULE_FINISHED) return result;
  386. #endif
  387. /* always module finished in dofile */
  388. result = webnet_module_system_dofile(session);
  389. if (result == WEBNET_MODULE_FINISHED) return result;
  390. return WEBNET_MODULE_CONTINUE;
  391. }
  392. static int _webnet_module_system_response_header(struct webnet_session *session, int event)
  393. {
  394. int result;
  395. result = WEBNET_MODULE_CONTINUE;
  396. return result;
  397. }
  398. static int _webnet_module_system_response_file(struct webnet_session *session, int event)
  399. {
  400. int result;
  401. result = WEBNET_MODULE_CONTINUE;
  402. return result;
  403. }
  404. int webnet_module_handle_event(struct webnet_session *session, int event)
  405. {
  406. switch (event)
  407. {
  408. case WEBNET_EVENT_INIT:
  409. return _webnet_module_system_init(session, event);
  410. case WEBNET_EVENT_URI_PHYSICAL:
  411. return _webnet_module_system_uri_physical(session, event);
  412. case WEBNET_EVENT_URI_POST:
  413. return _webnet_module_system_uri_post(session, event);
  414. case WEBNET_EVENT_RSP_HEADER:
  415. return _webnet_module_system_response_header(session, event);
  416. case WEBNET_EVENT_RSP_FILE:
  417. return _webnet_module_system_response_file(session, event);
  418. default:
  419. RT_ASSERT(0);
  420. break;
  421. }
  422. return WEBNET_MODULE_CONTINUE;
  423. }
  424. /* default index file */
  425. static const char *default_files[] =
  426. {
  427. "",
  428. "/index.html",
  429. "/index.htm",
  430. RT_NULL
  431. };
  432. /**
  433. * handle uri
  434. * there are two phases on uri handling:
  435. * - map url to physical
  436. * - url handling
  437. */
  438. int webnet_module_handle_uri(struct webnet_session *session)
  439. {
  440. int result, fd;
  441. char *full_path;
  442. rt_uint32_t index;
  443. struct webnet_request *request;
  444. RT_ASSERT(session != RT_NULL);
  445. /* get request */
  446. request = session->request;
  447. RT_ASSERT(request != RT_NULL);
  448. /* map uri to physical */
  449. result = webnet_module_handle_event(session, WEBNET_EVENT_URI_PHYSICAL);
  450. if (result == WEBNET_MODULE_FINISHED) return result;
  451. /* made a full physical path */
  452. full_path = (char *) wn_malloc(WEBNET_PATH_MAX);
  453. RT_ASSERT(full_path != RT_NULL);
  454. /* only GET or POST need try default page. */
  455. if ((session->request->method != WEBNET_GET)
  456. && (session->request->method != WEBNET_POST))
  457. {
  458. index = sizeof(default_files) / sizeof(default_files[0]);
  459. index -= 1;
  460. goto _end_default_files;
  461. }
  462. index = 0;
  463. while (default_files[index] != RT_NULL)
  464. {
  465. struct stat file_stat;
  466. /* made a full path */
  467. rt_snprintf(full_path, WEBNET_PATH_MAX, "%s/%s%s",
  468. webnet_get_root(), request->path, default_files[index]);
  469. /* normalize path */
  470. str_normalize_path(full_path);
  471. if (stat(full_path, &file_stat) >= 0 && !S_ISDIR(file_stat.st_mode))
  472. {
  473. break;
  474. }
  475. index ++;
  476. }
  477. _end_default_files:
  478. /* no this file */
  479. if (default_files[index] == RT_NULL)
  480. {
  481. /* use old full path */
  482. rt_snprintf(full_path, WEBNET_PATH_MAX, "%s/%s", webnet_get_root(), request->path);
  483. /* normalize path */
  484. str_normalize_path(full_path);
  485. }
  486. /* mark path as full physical path */
  487. wn_free(request->path);
  488. request->path = full_path;
  489. /* check uri valid */
  490. if (!str_begin_with(request->path, webnet_get_root()))
  491. {
  492. /* not found */
  493. request->result_code = 404;
  494. return WEBNET_MODULE_FINISHED;
  495. }
  496. /* uri post handle */
  497. return webnet_module_handle_event(session, WEBNET_EVENT_URI_POST);
  498. }