httpd.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164
  1. /*
  2. * Copyright (C) 2008 by egnite GmbH. All rights reserved.
  3. * Copyright (C) 2001-2006 by egnite Software GmbH. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the copyright holders nor the names of
  15. * contributors may be used to endorse or promote products derived
  16. * from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  21. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  22. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  23. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  24. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  25. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  26. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  27. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  28. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  29. * SUCH DAMAGE.
  30. *
  31. * For additional information see http://www.ethernut.de/
  32. */
  33. /*!
  34. * \file pro/httpd.c
  35. * \brief HTTP server support routines.
  36. *
  37. * \verbatim
  38. *
  39. * $Log$
  40. * Revision 1.26 2009/02/13 14:52:05 haraldkipp
  41. * Include memdebug.h for heap management debugging support.
  42. *
  43. * Revision 1.25 2009/02/06 15:40:29 haraldkipp
  44. * Using newly available strdup() and calloc().
  45. * Replaced NutHeap routines by standard malloc/free.
  46. * Replaced pointer value 0 by NULL.
  47. *
  48. * Revision 1.24 2008/08/11 07:00:35 haraldkipp
  49. * BSD types replaced by stdint types (feature request #1282721).
  50. *
  51. * Revision 1.23 2008/07/17 11:36:32 olereinhardt
  52. * - Moved some functions used in httpd.c as well as in ssi.c into httpd_p.c
  53. * - Implemeted $QUERY_STRING parameter in for CGIs included by a ssi file
  54. *
  55. * Revision 1.22 2008/07/14 13:11:15 haraldkipp
  56. * Added file length check to avoid loading directories when using PHAT.
  57. *
  58. * Revision 1.21 2008/07/10 12:09:39 haraldkipp
  59. * Wrong mime type was returned for default files within subdirectories.
  60. * Put duplicate code in a new static function GetMimeEntry.
  61. *
  62. * Revision 1.20 2008/07/08 13:27:55 haraldkipp
  63. * Several HTTP server options are now configurable.
  64. * Keepalive is now disabled by default to maintain backward compatibility.
  65. *
  66. * Revision 1.19 2008/05/21 14:10:19 thiagocorrea
  67. * Workaround HTTP stall connections. Details in bug id 1968754
  68. *
  69. * Revision 1.18 2008/05/16 03:38:27 thiagocorrea
  70. * Revert httpd memory allocation calls to NutHeapAlloc for consistency and
  71. * move DestroyRequestInfo to a shared file (reduces code size and remove duplicates
  72. * from httpd.c and ssi.c)
  73. *
  74. * Revision 1.17 2008/04/15 05:13:30 hwmaier
  75. * Fixed compilation error with avr-gcc 3.4.6
  76. *
  77. * Revision 1.16 2008/04/01 10:11:35 haraldkipp
  78. * Added the new, enhanced httpd API library.
  79. * Bugs #1839026 and #1839029 fixed.
  80. *
  81. * Revision 1.15 2006/11/08 08:52:31 haraldkipp
  82. * Bugfix, kindly provided by Steve Venroy. Already released request
  83. * structure was passed to NutHttpSendError().
  84. *
  85. * Revision 1.14 2006/10/08 16:48:22 haraldkipp
  86. * Documentation fixed
  87. *
  88. * Revision 1.13 2006/03/16 15:25:38 haraldkipp
  89. * Changed human readable strings from u_char to char to stop GCC 4 from
  90. * nagging about signedness.
  91. *
  92. * Revision 1.12 2006/01/06 09:19:42 haraldkipp
  93. * NutHttpURLEncode() no longer encodes everything that isn't alphanumeric.
  94. * See RFC2396. Thanks to Lloyd Bailey for this update.
  95. *
  96. * Revision 1.11 2005/10/24 11:02:28 haraldkipp
  97. * Integer division hack for ARM without CRT removed.
  98. *
  99. * Revision 1.10 2005/08/26 14:12:39 olereinhardt
  100. * Added NutHttpProcessPostQuery(FILE *stream, REQUEST * req)
  101. *
  102. * Revision 1.9 2005/08/05 11:23:11 olereinhardt
  103. * Added support to register a custom handler for mime types.
  104. * Added Server side include support and ASP support.
  105. *
  106. * Revision 1.8 2005/04/30 13:08:15 chaac
  107. * Added support for parsing Content-Length field in HTTP requests.
  108. *
  109. * Revision 1.7 2005/04/05 17:58:02 haraldkipp
  110. * Avoid integer division on ARM platform as long as we run without crtlib.
  111. *
  112. * Revision 1.6 2004/12/16 10:17:18 haraldkipp
  113. * Added Mikael Adolfsson's excellent parameter parsing routines.
  114. *
  115. * Revision 1.5 2004/07/30 19:45:48 drsung
  116. * Slightly improved handling if socket was closed by peer.
  117. *
  118. * Revision 1.4 2004/03/02 10:09:59 drsung
  119. * Small bugfix in NutHttpSendError. Thanks to Damian Slee.
  120. *
  121. * Revision 1.3 2003/07/20 16:03:27 haraldkipp
  122. * Saved some RAM by moving string literals to program memory.
  123. *
  124. * Revision 1.2 2003/07/17 12:28:21 haraldkipp
  125. * Memory hole bugfix
  126. *
  127. * Revision 1.1.1.1 2003/05/09 14:41:58 haraldkipp
  128. * Initial using 3.2.1
  129. *
  130. * Revision 1.14 2003/02/04 18:17:32 harald
  131. * Version 3 released
  132. *
  133. * Revision 1.13 2003/01/14 17:04:20 harald
  134. * Using FAT file system and added types
  135. *
  136. * Revision 1.12 2002/10/31 16:32:45 harald
  137. * Mods by troth for Linux
  138. *
  139. * Revision 1.11 2002/09/15 17:08:44 harald
  140. * Allow different character sets
  141. *
  142. * Revision 1.10 2002/06/26 17:29:49 harald
  143. * First pre-release with 2.4 stack
  144. *
  145. * \endverbatim
  146. */
  147. #include <cfg/arch.h>
  148. #include <cfg/http.h>
  149. #include <string.h>
  150. #include <io.h>
  151. #include <fcntl.h>
  152. #include <ctype.h>
  153. #include <stdlib.h>
  154. #include <sys/stat.h>
  155. #include <memdebug.h>
  156. #include <sys/heap.h>
  157. #include <sys/version.h>
  158. #include <pro/rfctime.h>
  159. #include <pro/httpd.h>
  160. #include "dencode.h"
  161. #include "httpd_p.h"
  162. /*! \brief Local major HTTP version. */
  163. #ifndef HTTP_MAJOR_VERSION
  164. #define HTTP_MAJOR_VERSION 1
  165. #endif
  166. /*! \brief Local minor HTTP version. */
  167. #ifndef HTTP_MINOR_VERSION
  168. #define HTTP_MINOR_VERSION 1
  169. #endif
  170. /*! \brief Maximum number of requests per connection. */
  171. #ifndef HTTP_KEEP_ALIVE_REQ
  172. #define HTTP_KEEP_ALIVE_REQ 0
  173. #endif
  174. /*! \brief Maximum size of an incoming request line. */
  175. #ifndef HTTP_MAX_REQUEST_SIZE
  176. #define HTTP_MAX_REQUEST_SIZE 256
  177. #endif
  178. /*! \brief Chunk size while sending files. */
  179. #ifndef HTTP_FILE_CHUNK_SIZE
  180. #define HTTP_FILE_CHUNK_SIZE 512
  181. #endif
  182. /*! \brief Enable GZIP support. */
  183. #ifndef HTTPD_SUPPORT_GZIP
  184. #define HTTPD_SUPPORT_GZIP 0
  185. #endif
  186. /*!
  187. * \addtogroup xgHTTPD
  188. */
  189. /*@{*/
  190. /*!
  191. * \brief Structure for table of interpreted header names.
  192. */
  193. typedef struct _REQUEST_LOOKUP {
  194. uint_fast8_t rlu_len;
  195. char *rlu_str;
  196. } REQUEST_LOOKUP;
  197. /*!
  198. * \brief Table for verifying header names.
  199. *
  200. * Modify with care, it might become a bit tricky.
  201. *
  202. * In any case the entries must be in sorted order. When inserting
  203. * a new entry, you need to adapt the switch/case statement in
  204. * function ParseHeaderLines(). If the new entry is larger than
  205. * all existing entries, do not forget to update MAX_REQUEST_NAME_SIZE.
  206. */
  207. static const REQUEST_LOOKUP req_lookup[] = {
  208. { 15, "accept-encoding" },
  209. { 13, "authorization" },
  210. #if HTTP_KEEP_ALIVE_REQ
  211. { 10, "connection" },
  212. #else
  213. { 0, NULL },
  214. #endif
  215. { 14, "content-length" },
  216. { 12, "content-type" },
  217. { 6, "cookie" },
  218. { 4, "host" },
  219. #if defined(HTTPD_EXCLUDE_DATE)
  220. { 0, NULL },
  221. #else
  222. { 17, "if-modified-since" },
  223. #endif
  224. { 7, "referer" },
  225. { 10, "user-agent" }
  226. };
  227. /*!
  228. * \brief Number of entries in the header name table.
  229. */
  230. #define NUM_REQUEST_LOOKUP sizeof(req_lookup) / sizeof(REQUEST_LOOKUP)
  231. /*!
  232. * \brief Size of the largest entry in the header name table.
  233. */
  234. #if defined(HTTPD_EXCLUDE_DATE)
  235. #define MAX_REQUEST_NAME_SIZE 15
  236. #else
  237. #define MAX_REQUEST_NAME_SIZE 17
  238. #endif
  239. /*!
  240. * \brief Known mime types.
  241. */
  242. MIMETYPES mimeTypes[] = {
  243. {
  244. ".txt", "text/plain", NULL}, {
  245. ".html", "text/html", NULL}, {
  246. ".shtml", "text/html", NULL}, {
  247. ".asp", "text/html", NULL}, {
  248. ".htm", "text/html", NULL}, {
  249. ".gif", "image/gif", NULL}, {
  250. ".jpg", "image/jpeg", NULL}, {
  251. ".png", "image/png", NULL}, {
  252. ".bmp", "image/bmp", NULL}, {
  253. ".pdf", "application/pdf", NULL}, {
  254. ".js", "application/x-javascript", NULL}, {
  255. ".jar", "application/x-java-archive", NULL}, {
  256. ".css", "text/css", NULL}, {
  257. ".xml", "text/xml", NULL}, {
  258. ".svg", "image/svg+xml", NULL}, {
  259. NULL, NULL, NULL}
  260. };
  261. static uint32_t http_optflags;
  262. /*!
  263. * \brief Send top lines of a standard HTML header.
  264. *
  265. * Sends HTTP and Server version lines.
  266. *
  267. * \param stream Stream of the socket connection, previously opened for
  268. * binary read and write.
  269. * \param req The associated client request.
  270. * \param status Response status, error code or 200, if no error occurred.
  271. * \param title Error text, or OK, if no error occurred.
  272. */
  273. void NutHttpSendHeaderTop(FILE * stream, REQUEST * req, int status, char *title)
  274. {
  275. static const char fmt_P[] PROGMEM = "HTTP/%d.%d %d %s\r\nServer: Ethernut %s\r\n";
  276. fprintf_P(stream, fmt_P, HTTP_MAJOR_VERSION, HTTP_MINOR_VERSION, status, title, NutVersionString());
  277. #if !defined(HTTPD_EXCLUDE_DATE)
  278. if (http_optflags & HTTP_OF_USE_HOST_TIME) {
  279. time_t now = time(NULL);
  280. fprintf(stream, "Date: %s GMT\r\n", Rfc1123TimeString(gmtime(&now)));
  281. }
  282. #endif
  283. }
  284. /*!
  285. * \brief Send bottom lines of a standard HTML header.
  286. *
  287. * Sends Content-Type and Content-Length.
  288. *
  289. * \deprecated Use NutHttpSendHeaderBottom().
  290. *
  291. * \param stream Stream of the socket connection, previously opened
  292. * for binary read and write.
  293. * \param mime_type Points to a string that specifies the content type.
  294. * Examples are "text/html", "image/png",
  295. * "image/gif", "video/mpeg" or "text/css".
  296. * A null pointer is ignored.
  297. * \param bytes Content length of the data following this
  298. * header. Ignored, if negative.
  299. */
  300. void NutHttpSendHeaderBot(FILE * stream, char *mime_type, long bytes)
  301. {
  302. NutHttpSendHeaderBottom( stream, 0, mime_type, bytes);
  303. }
  304. /*!
  305. * \brief Send bottom lines of a standard HTML header.
  306. *
  307. * Sends Content-Type, Content-Lenght and Connection lines.
  308. *
  309. * \param stream Stream of the socket connection, previously opened
  310. * for binary read and write.
  311. * \param mime_type Points to a string that specifies the content type.
  312. * Examples are "text/html", "image/png",
  313. * "image/gif", "video/mpeg" or "text/css".
  314. * A null pointer is ignored.
  315. * \param bytes Content length of the data following this
  316. * header. Ignored, if negative.
  317. * \param first2bytes The first two bytes of the file.
  318. */
  319. static void NutHttpSendHeaderBottomEx(FILE * stream, REQUEST * req, char *mime_type, long bytes, unsigned short first2bytes)
  320. {
  321. static const char typ_fmt_P[] PROGMEM = "Content-Type: %s\r\n";
  322. static const char len_fmt_P[] PROGMEM = "Content-Length: %ld\r\n";
  323. static const char enc_fmt_P[] PROGMEM = "Content-Encoding: gzip\r\n";
  324. static const char con_str_P[] PROGMEM = "Connection: ";
  325. static const char ccl_str_P[] PROGMEM = "close\r\n\r\n";
  326. #define GZIP_ID 0x8b1f
  327. if (mime_type)
  328. fprintf_P(stream, typ_fmt_P, mime_type);
  329. if (bytes >= 0)
  330. fprintf_P(stream, len_fmt_P, bytes);
  331. if (first2bytes == GZIP_ID)
  332. fputs_P(enc_fmt_P, stream);
  333. fputs_P(con_str_P, stream);
  334. #if HTTP_KEEP_ALIVE_REQ
  335. if ( req && req->req_connection == HTTP_CONN_KEEP_ALIVE) {
  336. static const char cka_str_P[] PROGMEM = "Keep-Alive\r\n\r\n";
  337. fputs_P(cka_str_P, stream);
  338. }
  339. else {
  340. fputs_P(ccl_str_P, stream);
  341. }
  342. #else
  343. fputs_P(ccl_str_P, stream);
  344. #endif
  345. }
  346. /*!
  347. * \brief Send bottom lines of a standard HTML header.
  348. *
  349. * Sends Content-Type, Content-Lenght and Connection lines.
  350. *
  351. * \param stream Stream of the socket connection, previously opened
  352. * for binary read and write.
  353. * \param mime_type Points to a string that specifies the content type.
  354. * Examples are "text/html", "image/png",
  355. * "image/gif", "video/mpeg" or "text/css".
  356. * A null pointer is ignored.
  357. * \param bytes Content length of the data following this
  358. * header. Ignored, if negative.
  359. */
  360. void NutHttpSendHeaderBottom(FILE * stream, REQUEST * req, char *mime_type, long bytes)
  361. {
  362. NutHttpSendHeaderBottomEx(stream, req, mime_type, bytes, 0);
  363. }
  364. /*!
  365. * \brief Send a HTTP error response.
  366. *
  367. * A canned error file is used.
  368. *
  369. * \param stream Stream of the socket connection, previously opened for
  370. * binary read and write.
  371. * \param req Contains the HTTP request.
  372. * \param status Error code to be returned.
  373. */
  374. void NutHttpSendError(FILE * stream, REQUEST * req, int status)
  375. {
  376. static const char err_fmt_P[] PROGMEM = "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD><BODY>%d %s</BODY></HTML>\r\n";
  377. static const char auth_fmt_P[] PROGMEM = "WWW-Authenticate: Basic realm=\"%s\"\r\n";
  378. char *title;
  379. switch (status) {
  380. case 304:
  381. title = "Not Modified";
  382. break;
  383. case 400:
  384. title = "Bad Request";
  385. break;
  386. case 401:
  387. title = "Unauthorized";
  388. break;
  389. case 404:
  390. title = "Not Found";
  391. break;
  392. case 500:
  393. title = "Internal Error";
  394. break;
  395. case 501:
  396. title = "Not Implemented";
  397. break;
  398. default:
  399. title = "Error";
  400. break;
  401. }
  402. #if HTTP_KEEP_ALIVE_REQ
  403. if (status >= 400) {
  404. req->req_connection = HTTP_CONN_CLOSE;
  405. }
  406. #endif
  407. NutHttpSendHeaderTop(stream, req, status, title);
  408. if (status == 401) {
  409. char *cp = 0;
  410. char *realm = req->req_url;
  411. if ((cp = strrchr(realm, '/')) != NULL)
  412. *cp = 0;
  413. else
  414. realm = ".";
  415. fprintf_P(stream, auth_fmt_P, realm);
  416. if (cp)
  417. *cp = '/';
  418. }
  419. NutHttpSendHeaderBottom(stream, req, "text/html", -1);
  420. fprintf_P(stream, err_fmt_P, status, title, status, title);
  421. }
  422. static MIMETYPES *GetMimeEntry(char *name)
  423. {
  424. MIMETYPES *rc;
  425. int fl;
  426. if (name == NULL || (fl = strlen(name)) == 0) {
  427. return &mimeTypes[1];
  428. }
  429. for (rc = mimeTypes; rc->mtyp_ext; rc++) {
  430. if (strcasecmp(&(name[fl - strlen(rc->mtyp_ext)]), rc->mtyp_ext) == 0) {
  431. return rc;
  432. }
  433. }
  434. return &mimeTypes[0];
  435. }
  436. /*!
  437. * \brief Return the mime type description of a specified file name.
  438. *
  439. * The mime type returned is based on the file extension.
  440. *
  441. * \param name Name of the file.
  442. *
  443. * \return A pointer to a static string, containing the
  444. * associated mime type description. If the extension
  445. * is not registered, "text/plain; charset=iso-8859-1"
  446. * is returned. If the filename is empty, then
  447. * "text/html; charset=iso-8859-1" is returned.
  448. */
  449. char *NutGetMimeType(char *name)
  450. {
  451. return GetMimeEntry(name)->mtyp_type;
  452. }
  453. /*!
  454. * \brief Return the mime type handler of a specified file name.
  455. *
  456. * This is the function that handles / sends a specific file type to the
  457. * client. Specially used for server side includes (shtml files)
  458. *
  459. * \param name Name of the file.
  460. *
  461. * \return A pointer to a function of the type void (u_char * filename)
  462. * If the extension is not registered, the handler for
  463. * "text/plain; charset=iso-8859-1" is returned.
  464. * If the filename is empty, then the handler for
  465. * "text/html; charset=iso-8859-1" is returned.
  466. */
  467. void *NutGetMimeHandler(char *name)
  468. {
  469. return GetMimeEntry(name)->mtyp_handler;
  470. }
  471. /*!
  472. * \brief URLDecodes a string
  473. *
  474. * Takes a url-encoded string and decodes it.
  475. *
  476. * \param str String to decode. This is overwritten with
  477. * the decoded string
  478. *
  479. * \warning To save RAM, the str parameter will be
  480. * overwritten with the encoded string.
  481. */
  482. void NutHttpURLDecode(char *str)
  483. {
  484. register char *ptr1, *ptr2, ch;
  485. char hexstr[3] = { 0, 0, 0 };
  486. for (ptr1 = ptr2 = str; *ptr1; ptr1++) {
  487. if (*ptr1 == '+')
  488. *ptr2++ = ' ';
  489. else if (*ptr1 == '%') {
  490. hexstr[0] = ptr1[1];
  491. hexstr[1] = ptr1[2];
  492. ch = strtol(hexstr, 0, 16);
  493. *ptr2++ = ch;
  494. ptr1 += 2;
  495. } else
  496. *ptr2++ = *ptr1;
  497. }
  498. *ptr2 = 0;
  499. }
  500. /*!
  501. * \brief Parses the QueryString
  502. *
  503. * Reads the QueryString from a request, and parses it into
  504. * name/value table. To save RAM, this method overwrites the
  505. * contents of req_query, and creates a table of pointers
  506. * into the req_query buffer.
  507. *
  508. * \param req Request object to parse
  509. */
  510. void NutHttpProcessQueryString(REQUEST * req)
  511. {
  512. register int i;
  513. register char *ptr;
  514. if (!req->req_query)
  515. return;
  516. req->req_numqptrs = 1;
  517. for (ptr = req->req_query; *ptr; ptr++)
  518. if (*ptr == '&')
  519. req->req_numqptrs++;
  520. req->req_qptrs = (char **) malloc(sizeof(char *) * (req->req_numqptrs * 2));
  521. if (req->req_qptrs == NULL) {
  522. /* Out of memory */
  523. req->req_numqptrs = 0;
  524. return;
  525. }
  526. req->req_qptrs[0] = req->req_query;
  527. req->req_qptrs[1] = NULL;
  528. for (ptr = req->req_query, i = 2; *ptr; ptr++) {
  529. if (*ptr == '&') {
  530. req->req_qptrs[i] = ptr + 1;
  531. req->req_qptrs[i + 1] = NULL;
  532. *ptr = 0;
  533. i += 2;
  534. }
  535. }
  536. for (i = 0; i < req->req_numqptrs; i++) {
  537. for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
  538. if (*ptr == '=') {
  539. req->req_qptrs[i * 2 + 1] = ptr + 1;
  540. *ptr = 0;
  541. NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
  542. break;
  543. }
  544. }
  545. NutHttpURLDecode(req->req_qptrs[i * 2]);
  546. }
  547. }
  548. static void NutHttpProcessFileRequest(FILE * stream, REQUEST * req)
  549. {
  550. int fd;
  551. int n;
  552. char *data;
  553. long file_len;
  554. void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
  555. char *mime_type;
  556. char *filename = NULL;
  557. char *modstr = NULL;
  558. unsigned short first2bytes = 0;
  559. /*
  560. * Validate authorization.
  561. */
  562. if (NutHttpAuthValidate(req)) {
  563. NutHttpSendError(stream, req, 401);
  564. return;
  565. }
  566. /*
  567. * Process CGI.
  568. */
  569. if (NutCgiCheckRequest(stream, req)) {
  570. return;
  571. }
  572. for (n = 0, fd = -1; default_files[n]; n++) {
  573. filename = CreateFilePath(req->req_url, default_files[n]);
  574. if (filename == NULL) {
  575. NutHttpSendError(stream, req, 500);
  576. return;
  577. }
  578. /*
  579. * Note, that simple file systems may not provide stat() or access(),
  580. * thus trying to open the file is the only way to check for existence.
  581. * Another problem is, that PHAT allows to open directories. We use
  582. * the file length to ensure, that we got a normal file.
  583. */
  584. if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) != -1) {
  585. if (_filelength(fd)) {
  586. break;
  587. }
  588. _close(fd);
  589. }
  590. free(filename);
  591. }
  592. if (fd == -1) {
  593. NutHttpSendError(stream, req, 404);
  594. return;
  595. }
  596. /* Check for mime type and handler. */
  597. mime_type = NutGetMimeType(filename);
  598. handler = NutGetMimeHandler(filename);
  599. #if !defined(HTTPD_EXCLUDE_DATE)
  600. /*
  601. * Optionally process modification time.
  602. */
  603. if (handler == NULL && (http_optflags & HTTP_OF_USE_FILE_TIME)) {
  604. struct stat s;
  605. time_t ftime;
  606. char *time_str;
  607. if (stat(filename, &s) == 0) {
  608. ftime = s.st_mtime;
  609. }
  610. else {
  611. /* Use compile time if stat not available. */
  612. ftime = RfcTimeParse("Fri " __DATE__ " " __TIME__);
  613. }
  614. /* Check if-modified-since condition. */
  615. if (req->req_ims && s.st_mtime <= req->req_ims) {
  616. _close(fd);
  617. NutHttpSendError(stream, req, 304);
  618. free(filename);
  619. return;
  620. }
  621. /* Save static buffer contents. */
  622. time_str = Rfc1123TimeString(gmtime(&ftime));
  623. modstr = strdup(time_str);
  624. }
  625. #endif /* HTTPD_EXCLUDE_DATE */
  626. /* Filename no longer used. */
  627. free(filename);
  628. NutHttpSendHeaderTop(stream, req, 200, "Ok");
  629. if (modstr) {
  630. fprintf(stream, "Last-Modified: %s GMT\r\n", modstr);
  631. free(modstr);
  632. }
  633. file_len = _filelength(fd);
  634. /* Use mime handler, if one has been registered. */
  635. if (handler) {
  636. NutHttpSendHeaderBottom(stream, req, mime_type, -1);
  637. handler(stream, fd, file_len, http_root, req);
  638. }
  639. /* Use default transfer, if no registered mime handler is available. */
  640. else {
  641. #if (HTTPD_SUPPORT_GZIP >= 1)
  642. /* Check for Accept-Encoding: gzip support */
  643. if (req->req_encoding != NULL) {
  644. if (strstr(req->req_encoding, "gzip") != NULL) {
  645. /* Read first two bytes, needed for gzip header check */
  646. _read(fd, &first2bytes, 2);
  647. _seek(fd, -2, SEEK_CUR);
  648. }
  649. }
  650. #endif
  651. NutHttpSendHeaderBottomEx(stream, req, mime_type, file_len, first2bytes);
  652. if (req->req_method != METHOD_HEAD) {
  653. size_t size = HTTP_FILE_CHUNK_SIZE;
  654. if ((data = malloc(size)) != NULL) {
  655. while (file_len) {
  656. if (file_len < HTTP_FILE_CHUNK_SIZE)
  657. size = (size_t) file_len;
  658. n = _read(fd, data, size);
  659. if (n <= 0) {
  660. /* We can't do much here, the header is out already. */
  661. break;
  662. }
  663. if (fwrite(data, 1, n, stream) == 0) {
  664. break;
  665. }
  666. file_len -= (long) n;
  667. }
  668. free(data);
  669. }
  670. }
  671. }
  672. _close(fd);
  673. }
  674. /*!
  675. *
  676. */
  677. static char *NextWord(char *str)
  678. {
  679. while (*str && *str != ' ' && *str != '\t')
  680. str++;
  681. if (*str)
  682. *str++ = 0;
  683. while (*str == ' ' || *str == '\t')
  684. str++;
  685. return str;
  686. }
  687. /*!
  688. * \brief Create a new request info structure.
  689. *
  690. * \return Pointer to an allocated structure or NULL if out of memory.
  691. */
  692. static REQUEST *CreateRequestInfo(void)
  693. {
  694. REQUEST *req;
  695. if ((req = calloc(1, sizeof(REQUEST))) != NULL) {
  696. req->req_version = HTTP_MAJOR_VERSION * 10 + HTTP_MINOR_VERSION;
  697. }
  698. return req;
  699. }
  700. /*!
  701. * \brief Register the HTTP server's root directory.
  702. *
  703. * Only one root directory is supported. Subsequent calls will
  704. * override previous settings.
  705. *
  706. * \param path Pathname of the root directory. Must include the
  707. * device name followed by a colon followed by a
  708. * directory path followed by a trailing slash.
  709. *
  710. * \return 0 on success, -1 otherwise.
  711. */
  712. int NutRegisterHttpRoot(char *path)
  713. {
  714. int len;
  715. if (http_root)
  716. free(http_root);
  717. if (path && (len = strlen(path)) != 0) {
  718. if ((http_root = malloc(len + 1)) != NULL)
  719. strcpy(http_root, path);
  720. else
  721. return -1;
  722. } else
  723. http_root = NULL;
  724. return 0;
  725. }
  726. /*!
  727. * \brief Set HTTP option flags.
  728. *
  729. * \param flags Option flags to set. Any of the following may be or'ed:
  730. * - HTTP_OF_USE_HOST_TIME Date header will be included in response.
  731. * - HTTP_OF_USE_FILE_TIME Handle file modification time.
  732. *
  733. */
  734. void NutHttpSetOptionFlags(uint32_t flags)
  735. {
  736. http_optflags = flags;
  737. }
  738. /*!
  739. * \brief Retrieve HTTP option flags.
  740. *
  741. * \return Option flags.
  742. */
  743. uint32_t NutHttpGetOptionFlags(void)
  744. {
  745. return http_optflags;
  746. }
  747. /*!
  748. * \brief Allocate a buffer for a header field value.
  749. *
  750. * \param hfvp Points to the character pointer variable that will receive
  751. * the pointer to the header field value. If the variable does
  752. * not contain a NULL pointer upon entry, the routine will
  753. * return immediately and will not extract any value. If it
  754. * contains a NULL pointer on entry, the routine will allocate
  755. * heap memory to store a copy of the extracted value. In this
  756. * case the caller is responsible to release the allocation.
  757. * \param str Points into a request line, right after the colon. Leading
  758. * spaces will be skipped before creating a copy.
  759. *
  760. * \return -1 if out of memory, otherwise 0.
  761. */
  762. static int HeaderFieldValue(char **hfvp, const char *str)
  763. {
  764. /* Do not override existing values. */
  765. if (*hfvp == NULL) {
  766. /* Skip spaces. */
  767. while (*str == ' ' || *str == '\t')
  768. str++;
  769. /* Allocate a string copy. */
  770. if ((*hfvp = strdup(str)) == NULL)
  771. return -1;
  772. }
  773. return 0;
  774. }
  775. /*!
  776. * \brief Interpret next header from a given stream.
  777. *
  778. * This function reads single characters from the stream as long
  779. * as they fit with an entry in the req_lookup table. It will
  780. * return as soon as an unknown entry had been detected, or when
  781. * a colon or linefeed has been read, or if reading from the stream
  782. * fails.
  783. *
  784. * \param stream Stream to read from.
  785. * \param idx Pointer to a variable that receives the index
  786. * of a known header. This is only valid if the
  787. * function returns a colon character.
  788. *
  789. * \return Last character read from the stream, EOF on error or
  790. * timeout, or zero on empty lines.
  791. */
  792. static int NextHeaderName(FILE * stream, uint_fast8_t *idx)
  793. {
  794. uint_fast8_t i = 0;
  795. int ch = 0;
  796. *idx = 0;
  797. /* Read the first character, which is not a carriage returns. */
  798. do {
  799. ch = fgetc(stream);
  800. } while (ch == '\r');
  801. /* Return 0, if we got the linefeed. This is an empty line,
  802. which should be interpreted by the caller as the end of
  803. the HTTP header. */
  804. if (ch == '\n') {
  805. return 0;
  806. }
  807. /* Lookup the header line name. */
  808. while (i < MAX_REQUEST_NAME_SIZE && *idx < NUM_REQUEST_LOOKUP) {
  809. /* Stop, if the last read failed or if we found the end of the
  810. line or the name field. */
  811. if (ch == EOF || ch == '\n') {
  812. break;
  813. }
  814. /* Check if the length is correct */
  815. if (ch == ':') {
  816. if (i == req_lookup[*idx].rlu_len) {
  817. /* The correct element was found */
  818. break;
  819. } else {
  820. /* The element does not match */
  821. *idx = -1;
  822. break;
  823. }
  824. }
  825. /* Check if all characters read so far fits with any header
  826. we are interested in. */
  827. for (; *idx < NUM_REQUEST_LOOKUP; (*idx)++) {
  828. if (i < req_lookup[*idx].rlu_len &&
  829. *(req_lookup[*idx].rlu_str + i) == tolower(ch)) {
  830. /* So far, this header fits. */
  831. break;
  832. }
  833. }
  834. /* Read the next character, ignoring carriage returns. */
  835. i++;
  836. do {
  837. ch = fgetc(stream);
  838. } while (ch == '\r');
  839. }
  840. return ch;
  841. }
  842. /*!
  843. * \brief Read characters from a given stream until EOL.
  844. */
  845. static void SkipLine(FILE * stream)
  846. {
  847. int ch;
  848. do {
  849. ch = fgetc(stream);
  850. } while (ch != EOF && ch != '\n');
  851. }
  852. static int ParserHeaderLines(FILE *stream, REQUEST *req)
  853. {
  854. char *cp;
  855. int ch;
  856. uint_fast8_t req_idx;
  857. char **strval;
  858. char *line;
  859. line = malloc(HTTP_MAX_REQUEST_SIZE);
  860. if (line) {
  861. for (;;) {
  862. /* Parse the next header name. */
  863. ch = NextHeaderName(stream, &req_idx);
  864. if (ch == EOF) {
  865. /* Broken connection, stop parsing. */
  866. break;
  867. }
  868. if (ch == 0) {
  869. /* Empty line marks the end of the request header. */
  870. free(line);
  871. return 0;
  872. }
  873. if (ch != ':' || req_idx >= NUM_REQUEST_LOOKUP) {
  874. /* No valid name/value pair or unexpected header line.
  875. Skip this line. */
  876. SkipLine(stream);
  877. } else {
  878. /* At this point we got a header we are interested in.
  879. Read the rest of this line, it contains the value. */
  880. if (fgets(line, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
  881. /* Broken connection, stop parsing. */
  882. break;
  883. }
  884. /* Make sure we got a complete line. */
  885. cp = strchr(line, '\n');
  886. if (cp == NULL) {
  887. /* Incomplete line, skip it. */
  888. SkipLine(stream);
  889. /* May be we should return an internal error to the browser. */
  890. } else {
  891. /* We got the value, chop off any CR-LF. */
  892. *cp = '\0';
  893. if (cp > line && *--cp == '\r') {
  894. *cp = '\0';
  895. }
  896. //printf("Header '%s: %s'\n", req_lookup[req_idx].rlu_str, line);
  897. /* Store the value in the request info structure.
  898. Impotant! This switch statement must correspond
  899. to the req_lookup table. */
  900. strval = NULL;
  901. switch (req_idx) {
  902. case 0:
  903. /* Accept-encoding */
  904. strval = &req->req_encoding;
  905. break;
  906. case 1:
  907. /* Authorization: Store as string. */
  908. strval = &req->req_auth;
  909. break;
  910. #if HTTP_KEEP_ALIVE_REQ
  911. case 2:
  912. /* Connection: Interpret type. */
  913. if (strncasecmp(line, "close", 5) == 0) {
  914. req->req_connection = HTTP_CONN_CLOSE;
  915. }
  916. else if (strncasecmp(line, "Keep-Alive", 10) == 0) {
  917. req->req_connection = HTTP_CONN_KEEP_ALIVE;
  918. }
  919. break;
  920. #endif
  921. case 3:
  922. /* Content-Length: Get size. */
  923. req->req_length = atol(line);
  924. break;
  925. case 4:
  926. /* Content-Type: Store as string. */
  927. strval = &req->req_type;
  928. break;
  929. case 5:
  930. /* Cookie: Store as string. */
  931. strval = &req->req_cookie;
  932. break;
  933. case 6:
  934. /* Host: Store as string. */
  935. strval = &req->req_host;
  936. break;
  937. #if !defined(HTTPD_EXCLUDE_DATE)
  938. case 7:
  939. /* If-Modified-Since: Interpret RFC date. */
  940. req->req_ims = RfcTimeParse(line);
  941. break;
  942. #endif
  943. case 8:
  944. /* Referer: Store as string. */
  945. strval = &req->req_referer;
  946. break;
  947. case 9:
  948. /* User-Agent: Store as string. */
  949. strval = &req->req_agent;
  950. break;
  951. }
  952. /* Anything to store as a string. */
  953. if (strval && HeaderFieldValue(strval, line)) {
  954. break;
  955. }
  956. }
  957. }
  958. }
  959. /* All header lines processed. */
  960. free(line);
  961. }
  962. return -1;
  963. }
  964. /*!
  965. * \brief Process the next HTTP request.
  966. *
  967. * Waits for the next HTTP request on an established connection
  968. * and processes it.
  969. *
  970. * \param stream Stream of the socket connection, previously opened for
  971. * binary read and write.
  972. */
  973. void NutHttpProcessRequest(FILE * stream)
  974. {
  975. REQUEST *req = NULL;
  976. char *method = NULL;
  977. char *path;
  978. char *protocol;
  979. char *cp;
  980. #if HTTP_KEEP_ALIVE_REQ
  981. int keep_alive_max = HTTP_KEEP_ALIVE_REQ;
  982. #endif
  983. for(;;) {
  984. /* Release resources used on the previous connect. */
  985. DestroyRequestInfo(req);
  986. if ((req = CreateRequestInfo()) == NULL)
  987. break;
  988. if (method)
  989. free(method);
  990. /* The first line contains method, path and protocol. */
  991. if ((method = malloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
  992. break;
  993. }
  994. if (fgets(method, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
  995. break;
  996. }
  997. if ((cp = strchr(method, '\r')) != NULL)
  998. *cp = 0;
  999. if ((cp = strchr(method, '\n')) != NULL)
  1000. *cp = 0;
  1001. /* Parse remaining request header lines. */
  1002. if (ParserHeaderLines(stream, req)) {
  1003. break;
  1004. }
  1005. /* Further break up the first header line. */
  1006. path = NextWord(method);
  1007. protocol = NextWord(path);
  1008. NextWord(protocol);
  1009. /* Determine the request method. */
  1010. if (strcasecmp(method, "GET") == 0)
  1011. req->req_method = METHOD_GET;
  1012. else if (strcasecmp(method, "HEAD") == 0)
  1013. req->req_method = METHOD_HEAD;
  1014. else if (strcasecmp(method, "POST") == 0)
  1015. req->req_method = METHOD_POST;
  1016. else {
  1017. NutHttpSendError(stream, req, 501);
  1018. break;
  1019. }
  1020. if (*path == 0 || *protocol == 0) {
  1021. NutHttpSendError(stream, req, 400);
  1022. break;
  1023. }
  1024. /* Determine the client's HTTP version. */
  1025. if (strcasecmp(protocol, "HTTP/1.0") == 0) {
  1026. req->req_version = 10;
  1027. #if HTTP_KEEP_ALIVE_REQ
  1028. if (req->req_connection != HTTP_CONN_KEEP_ALIVE) {
  1029. req->req_connection = HTTP_CONN_CLOSE;
  1030. }
  1031. #endif
  1032. }
  1033. #if HTTP_KEEP_ALIVE_REQ
  1034. else if (req->req_connection != HTTP_CONN_CLOSE) {
  1035. req->req_connection = HTTP_CONN_KEEP_ALIVE;
  1036. }
  1037. /* Limit the number of requests per connection. */
  1038. if (keep_alive_max > 0) {
  1039. keep_alive_max--;
  1040. }
  1041. else {
  1042. req->req_connection = HTTP_CONN_CLOSE;
  1043. }
  1044. #else
  1045. req->req_connection = HTTP_CONN_CLOSE;
  1046. #endif
  1047. if ((cp = strchr(path, '?')) != 0) {
  1048. *cp++ = 0;
  1049. if ((req->req_query = strdup(cp)) == NULL) {
  1050. break;
  1051. }
  1052. NutHttpProcessQueryString(req);
  1053. }
  1054. if ((req->req_url = strdup(path)) == NULL) {
  1055. break;
  1056. }
  1057. if (NutDecodePath(req->req_url) == 0) {
  1058. NutHttpSendError(stream, req, 400);
  1059. } else {
  1060. NutHttpProcessFileRequest(stream, req);
  1061. }
  1062. fflush(stream);
  1063. if (req->req_connection == HTTP_CONN_CLOSE) {
  1064. break;
  1065. }
  1066. }
  1067. DestroyRequestInfo(req);
  1068. if (method)
  1069. free(method);
  1070. }
  1071. /*@}*/