responses.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /*
  2. * Copyright (C) 2012 by egnite GmbH
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the copyright holders nor the names of
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  22. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  23. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  25. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  26. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  27. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  29. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. *
  32. * For additional information see http://www.ethernut.de/
  33. */
  34. /*!
  35. * $Id$
  36. */
  37. #if !defined(HTTPD_EXCLUDE_DATE)
  38. #include <pro/rfctime.h>
  39. #endif
  40. #include <pro/uhttp/uhttpd.h>
  41. #include <stdlib.h>
  42. #include <stdarg.h>
  43. #include <string.h>
  44. #include <memdebug.h>
  45. typedef struct _HTTP_RESPONSE_STATUS HTTP_RESPONSE_STATUS;
  46. struct _HTTP_RESPONSE_STATUS {
  47. int rs_code;
  48. const char *rs_text;
  49. };
  50. #if HTTP_VERSION >= 0x10
  51. static const HTTP_RESPONSE_STATUS response_list[] = {
  52. { 100, "Continue" },
  53. { 101, "Switching Protocols" },
  54. { 102, "Processing" },
  55. { 103, "Checkpoint" },
  56. { 200, "OK" },
  57. { 201, "Created" },
  58. { 202, "Accepted" },
  59. #if HTTP_VERSION >= 0x11
  60. { 203, "Non-Authoritative Information" },
  61. #endif
  62. { 204, "No Content" },
  63. { 205, "Reset Content" },
  64. { 206, "Partial Content" },
  65. { 207, "Multi-Status" },
  66. { 208, "Already Reported" },
  67. { 226, "IM Used" },
  68. { 300, "Multiple Choices" },
  69. { 301, "Moved Permanently" },
  70. { 302, "Moved Temporarily" },
  71. #if HTTP_VERSION >= 0x11
  72. { 303, "See Other" },
  73. #endif
  74. { 304, "Not Modified" },
  75. #if HTTP_VERSION >= 0x11
  76. { 305, "Use Proxy" },
  77. #endif
  78. { 306, "Switch Proxy" },
  79. #if HTTP_VERSION >= 0x11
  80. { 307, "Temporary Redirect" },
  81. #endif
  82. { 308, "Resume Incomplete" },
  83. { 400, "Bad Request" },
  84. { 401, "Unauthorized" },
  85. { 402, "Payment Required" },
  86. { 403, "Forbidden" },
  87. { 404, "Not Found" },
  88. { 405, "Not Allowed" },
  89. { 406, "Not Acceptable" },
  90. { 407, "Proxy Authentication Required" },
  91. { 408, "Request Time-out" },
  92. { 409, "Conflict" },
  93. { 410, "Gone" },
  94. { 411, "Length Required" },
  95. { 412, "Precondition Failed" },
  96. { 413, "Request Entity Too Large" },
  97. { 414, "Request-URI Too Large" },
  98. { 415, "Unsupported Media Type" },
  99. { 416, "Requested Range Not Satisfiable" },
  100. { 417, "Expectation Failed" },
  101. { 418, "I'm a teapot" },
  102. { 422, "Unprocessable Entity" },
  103. { 423, "Locked" },
  104. { 424, "Failed Dependency" },
  105. { 425, "Unordered Collection" },
  106. { 426, "Upgrade Required" },
  107. { 428, "Precondition Required" },
  108. { 429, "Too Many Requests" },
  109. { 431, "Request Header Fields Too Large" },
  110. { 444, "No Response" },
  111. { 449, "Retry With" },
  112. { 499, "Client Closed Request" },
  113. { 500, "Internal Server Error" },
  114. { 501, "Method Not Implemented" },
  115. { 502, "Bad Gateway" },
  116. { 503, "Service Temporarily Unavailable" },
  117. { 504, "Gateway Time-out" },
  118. { 505, "HTTP Version Not Supported" },
  119. { 506, "Variant Also Negotiates" },
  120. { 507, "Insufficient Storage" },
  121. { 508, "Loop Detected" },
  122. { 509, "Bandwidth Limit Exceeded" },
  123. { 510, "Not Extended" },
  124. { 511, "Network Authentication Required" },
  125. { 598, "Network read timeout error" },
  126. { 599, "Network connect timeout error" }
  127. };
  128. #define HTTP_NUM_RESPONSES (sizeof(response_list) / sizeof(HTTP_RESPONSE_STATUS))
  129. #endif
  130. const char *HttpResponseText(int code)
  131. {
  132. const char *rp = NULL;
  133. #if HTTP_VERSION >= 0x10
  134. int i;
  135. for (i = 0; i < (int)HTTP_NUM_RESPONSES; i++) {
  136. if (code >= response_list[i].rs_code) {
  137. break;
  138. }
  139. }
  140. if (i < (int)HTTP_NUM_RESPONSES && code == response_list[i].rs_code) {
  141. rp = response_list[i].rs_text;
  142. }
  143. #endif
  144. if (rp == NULL) {
  145. static const char *err_txt = "Error";
  146. static const char *ok_txt = "OK";
  147. if (code < 400) {
  148. rp = ok_txt;
  149. } else {
  150. rp = err_txt;
  151. }
  152. }
  153. return rp;
  154. }
  155. void HttpSendStreamHeaderTop(HTTP_STREAM *stream, int status)
  156. {
  157. #if HTTP_VERSION >= 0x10
  158. static const char fmt_P[] = "HTTP/%d.%d %d %s\r\nServer: uHTTP 0.0\r\n";
  159. s_printf(stream, fmt_P, HTTP_MAJOR_VERSION, HTTP_MINOR_VERSION, status, HttpResponseText(status));
  160. #if !defined(HTTPD_EXCLUDE_DATE)
  161. {
  162. time_t now = time(NULL);
  163. s_vputs(stream, ct_Date, ": ", Rfc1123TimeString(gmtime(&now)), " GMT\r\n", NULL);
  164. }
  165. #endif
  166. #endif
  167. }
  168. void HttpSendHeaderTop(HTTPD_SESSION *hs, int status)
  169. {
  170. HttpSendStreamHeaderTop(hs->s_stream, status);
  171. }
  172. #if !defined(HTTPD_EXCLUDE_DATE)
  173. void HttpSendStreamHeaderDate(HTTP_STREAM *stream, time_t mtime)
  174. {
  175. #if HTTP_VERSION >= 0x10
  176. if (mtime) {
  177. s_vputs(stream, ct_Last_Modified, ": ", Rfc1123TimeString(gmtime(&mtime)), " GMT\r\n", NULL);
  178. }
  179. #endif
  180. }
  181. void HttpSendHeaderDate(HTTPD_SESSION *hs, time_t mtime)
  182. {
  183. #if HTTP_VERSION >= 0x10
  184. HttpSendStreamHeaderDate(hs->s_stream, mtime);
  185. #endif
  186. }
  187. #endif
  188. void HttpSendStreamHeaderBottom(HTTP_STREAM *stream, const char *type, const char *subtype, int conn, long bytes)
  189. {
  190. #if HTTP_VERSION >= 0x10
  191. (void)conn;
  192. if (type && subtype) {
  193. s_vputs(stream, ct_Content_Type, ": ", type, "/", subtype, "\r\n", NULL);
  194. }
  195. if (bytes >= 0) {
  196. s_printf(stream, "%s: %ld\r\n", ct_Content_Length, bytes);
  197. }
  198. #if HTTP_VERSION >= 0x11
  199. #if HTTP_KEEP_ALIVE_REQ
  200. if (conn != HTTP_CONN_KEEP_ALIVE)
  201. #endif
  202. {
  203. s_puts("Connection: close\r\n", stream);
  204. }
  205. #elif HTTP_KEEP_ALIVE_REQ
  206. if (conn == HTTP_CONN_KEEP_ALIVE) {
  207. s_puts("Connection: keep-alive\r\n", stream);
  208. }
  209. #endif
  210. s_puts("\r\n", stream);
  211. #endif
  212. }
  213. void HttpSendHeaderBottom(HTTPD_SESSION *hs, const char *type, const char *subtype, long bytes)
  214. {
  215. #if HTTP_VERSION >= 0x10
  216. #if HTTP_KEEP_ALIVE_REQ
  217. if (bytes < 0) {
  218. hs->s_req.req_connection = HTTP_CONN_CLOSE;
  219. }
  220. #endif
  221. #if 0
  222. #define GZIP_ID 0x8b1f
  223. if (first2bytes == GZIP_ID) {
  224. s_puts("Content-Encoding: gzip\r\n", hs->s_stream);
  225. }
  226. #endif
  227. HttpSendStreamHeaderBottom(hs->s_stream, type, subtype, hs->s_req.req_connection, bytes);
  228. #endif
  229. }
  230. void HttpSendStreamError(HTTP_STREAM *stream, int status, const char *realm)
  231. {
  232. static const char body[] = "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD><BODY>%d %s</BODY></HTML>\r\n";
  233. const char *text = HttpResponseText(status);
  234. HttpSendStreamHeaderTop(stream, status);
  235. #if HTTP_VERSION >= 0x10
  236. if (realm) {
  237. static const char auth_fmt_P[] = "WWW-Authenticate: Basic realm=\"%s\"\r\n";
  238. s_printf(stream, auth_fmt_P, realm);
  239. }
  240. HttpSendStreamHeaderBottom(stream, "text", "html", HTTP_CONN_CLOSE, sizeof(body) - 1 + 2 * (1 + strlen(text) - 2));
  241. #endif
  242. s_printf(stream, body, status, text, status, text);
  243. s_flush(stream);
  244. }
  245. void HttpSendError(HTTPD_SESSION *hs, int status)
  246. {
  247. char *realm = NULL;
  248. #if HTTP_KEEP_ALIVE_REQ && (HTTP_VERSION >= 0x10)
  249. if (status >= 400) {
  250. //hs->s_req.req_connection = HTTP_CONN_CLOSE;
  251. }
  252. #endif
  253. #if HTTP_VERSION >= 0x10
  254. if (status == 401) {
  255. realm = hs->s_req.req_realm;
  256. }
  257. #endif
  258. HttpSendStreamError(hs->s_stream, status, realm);
  259. }
  260. /*!
  261. * \brief Transmit a redirection page.
  262. */
  263. int HttpSendRedirection(HTTPD_SESSION *hs, int code, ...)
  264. {
  265. va_list ap;
  266. int len;
  267. char *cp;
  268. char *loc;
  269. va_start(ap, code);
  270. for (len = 0; (cp = va_arg(ap, char *)) != NULL; len += strlen(cp));
  271. va_end(ap);
  272. loc = malloc(len + 1);
  273. if (loc) {
  274. static const char body[] =
  275. "<html><body><a href=\"%s\">Continue</a></body></html>\r\n";
  276. HTTP_STREAM *sp = hs->s_stream;
  277. HttpSendHeaderTop(hs, code);
  278. va_start(ap, code);
  279. for (*loc = '\0'; (cp = va_arg(ap, char *)) != NULL; strcat(loc, cp));
  280. va_end(ap);
  281. #if HTTP_VERSION >= 0x10
  282. s_vputs(sp, ct_Location, ": ", loc, "\r\n", NULL);
  283. HttpSendHeaderBottom(hs, "text", "html", sizeof(body) - 1 + strlen(loc) - 2);
  284. #endif
  285. s_printf(sp, body, loc);
  286. s_flush(sp);
  287. free(loc);
  288. }
  289. return 0;
  290. }