response.inl 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /* response.inl
  2. *
  3. * Bufferring for HTTP headers for HTTP response.
  4. * This function are only intended to be used at the server side.
  5. * Optional for HTTP/1.0 and HTTP/1.1, mandatory for HTTP/2.
  6. *
  7. * This file is part of the CivetWeb project.
  8. */
  9. #if defined(NO_RESPONSE_BUFFERING) && defined(USE_HTTP2)
  10. #error "HTTP2 currently works only if NO_RESPONSE_BUFFERING is not set"
  11. #endif
  12. /* Internal function to free header list */
  13. static void
  14. free_buffered_response_header_list(struct mg_connection *conn)
  15. {
  16. #if !defined(NO_RESPONSE_BUFFERING)
  17. while (conn->response_info.num_headers > 0) {
  18. conn->response_info.num_headers--;
  19. mg_free((void *)conn->response_info
  20. .http_headers[conn->response_info.num_headers]
  21. .name);
  22. conn->response_info.http_headers[conn->response_info.num_headers].name =
  23. 0;
  24. mg_free((void *)conn->response_info
  25. .http_headers[conn->response_info.num_headers]
  26. .value);
  27. conn->response_info.http_headers[conn->response_info.num_headers]
  28. .value = 0;
  29. }
  30. #else
  31. (void)conn; /* Nothing to do */
  32. #endif
  33. }
  34. /* Send first line of HTTP/1.x response */
  35. static void
  36. send_http1_response_status_line(struct mg_connection *conn)
  37. {
  38. const char *status_txt;
  39. const char *http_version = conn->request_info.http_version;
  40. int status_code = conn->status_code;
  41. if ((status_code < 100) || (status_code > 999)) {
  42. /* Set invalid status code to "500 Internal Server Error" */
  43. status_code = 500;
  44. }
  45. if (!http_version) {
  46. http_version = "1.0";
  47. }
  48. /* mg_get_response_code_text will never return NULL */
  49. status_txt = mg_get_response_code_text(conn, conn->status_code);
  50. mg_printf(conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt);
  51. }
  52. /* Initialize a new HTTP response
  53. * Parameters:
  54. * conn: Current connection handle.
  55. * status: HTTP status code (e.g., 200 for "OK").
  56. * Return:
  57. * 0: ok
  58. * -1: parameter error
  59. * -2: invalid connection type
  60. * -3: invalid connection status
  61. */
  62. int
  63. mg_response_header_start(struct mg_connection *conn, int status)
  64. {
  65. if ((conn == NULL) || (status < 100) || (status > 999)) {
  66. /* Parameter error */
  67. return -1;
  68. }
  69. if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
  70. || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
  71. /* Only allowed in server context */
  72. return -2;
  73. }
  74. if (conn->request_state != 0) {
  75. /* only allowed if nothing was sent up to now */
  76. return -3;
  77. }
  78. conn->status_code = status;
  79. conn->request_state = 1;
  80. /* Buffered response is stored, unbuffered response will be sent directly,
  81. * but we can only send HTTP/1.x response here */
  82. #if !defined(NO_RESPONSE_BUFFERING)
  83. free_buffered_response_header_list(conn);
  84. #else
  85. send_http1_response_status_line(conn);
  86. conn->request_state = 1; /* Reset from 10 to 1 */
  87. #endif
  88. return 0;
  89. }
  90. /* Add a new HTTP response header line
  91. * Parameters:
  92. * conn: Current connection handle.
  93. * header: Header name.
  94. * value: Header value.
  95. * value_len: Length of header value, excluding the terminating zero.
  96. * Use -1 for "strlen(value)".
  97. * Return:
  98. * 0: ok
  99. * -1: parameter error
  100. * -2: invalid connection type
  101. * -3: invalid connection status
  102. * -4: too many headers
  103. * -5: out of memory
  104. */
  105. int
  106. mg_response_header_add(struct mg_connection *conn,
  107. const char *header,
  108. const char *value,
  109. int value_len)
  110. {
  111. #if !defined(NO_RESPONSE_BUFFERING)
  112. int hidx;
  113. #endif
  114. if ((conn == NULL) || (header == NULL) || (value == NULL)) {
  115. /* Parameter error */
  116. return -1;
  117. }
  118. if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
  119. || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
  120. /* Only allowed in server context */
  121. return -2;
  122. }
  123. if (conn->request_state != 1) {
  124. /* only allowed if mg_response_header_start has been called before */
  125. return -3;
  126. }
  127. #if !defined(NO_RESPONSE_BUFFERING)
  128. hidx = conn->response_info.num_headers;
  129. if (hidx >= MG_MAX_HEADERS) {
  130. /* Too many headers */
  131. return -4;
  132. }
  133. /* Alloc new element */
  134. conn->response_info.http_headers[hidx].name =
  135. mg_strdup_ctx(header, conn->phys_ctx);
  136. if (value_len >= 0) {
  137. char *hbuf =
  138. (char *)mg_malloc_ctx((unsigned)value_len + 1, conn->phys_ctx);
  139. if (hbuf) {
  140. memcpy(hbuf, value, (unsigned)value_len);
  141. hbuf[value_len] = 0;
  142. }
  143. conn->response_info.http_headers[hidx].value = hbuf;
  144. } else {
  145. conn->response_info.http_headers[hidx].value =
  146. mg_strdup_ctx(value, conn->phys_ctx);
  147. }
  148. if ((conn->response_info.http_headers[hidx].name == 0)
  149. || (conn->response_info.http_headers[hidx].value == 0)) {
  150. /* Out of memory */
  151. mg_free((void *)conn->response_info.http_headers[hidx].name);
  152. conn->response_info.http_headers[hidx].name = 0;
  153. mg_free((void *)conn->response_info.http_headers[hidx].value);
  154. conn->response_info.http_headers[hidx].value = 0;
  155. return -5;
  156. }
  157. /* OK, header stored */
  158. conn->response_info.num_headers++;
  159. #else
  160. if (value_len >= 0) {
  161. mg_printf(conn, "%s: %.*s\r\n", header, (int)value_len, value);
  162. } else {
  163. mg_printf(conn, "%s: %s\r\n", header, value);
  164. }
  165. conn->request_state = 1; /* Reset from 10 to 1 */
  166. #endif
  167. return 0;
  168. }
  169. /* forward */
  170. static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]);
  171. /* Add a complete header string (key + value).
  172. * Parameters:
  173. * conn: Current connection handle.
  174. * http1_headers: Header line(s) in the form "name: value".
  175. * Return:
  176. * >=0: no error, number of header lines added
  177. * -1: parameter error
  178. * -2: invalid connection type
  179. * -3: invalid connection status
  180. * -4: too many headers
  181. * -5: out of memory
  182. */
  183. int
  184. mg_response_header_add_lines(struct mg_connection *conn,
  185. const char *http1_headers)
  186. {
  187. struct mg_header add_hdr[MG_MAX_HEADERS];
  188. int num_hdr, i, ret;
  189. char *workbuffer, *parse;
  190. /* We need to work on a copy of the work buffer, sice parse_http_headers
  191. * will modify */
  192. workbuffer = mg_strdup_ctx(http1_headers, conn->phys_ctx);
  193. if (!workbuffer) {
  194. /* Out of memory */
  195. return -5;
  196. }
  197. /* Call existing method to split header buffer */
  198. parse = workbuffer;
  199. num_hdr = parse_http_headers(&parse, add_hdr);
  200. ret = num_hdr;
  201. for (i = 0; i < num_hdr; i++) {
  202. int lret =
  203. mg_response_header_add(conn, add_hdr[i].name, add_hdr[i].value, -1);
  204. if ((ret > 0) && (lret < 0)) {
  205. /* Store error return value */
  206. ret = lret;
  207. }
  208. }
  209. /* mg_response_header_add created a copy, so we can free the original */
  210. mg_free(workbuffer);
  211. return ret;
  212. }
  213. #if defined USE_HTTP2
  214. static int http2_send_response_headers(struct mg_connection *conn);
  215. #endif
  216. /* Send http response
  217. * Parameters:
  218. * conn: Current connection handle.
  219. * Return:
  220. * 0: ok
  221. * -1: parameter error
  222. * -2: invalid connection type
  223. * -3: invalid connection status
  224. */
  225. int
  226. mg_response_header_send(struct mg_connection *conn)
  227. {
  228. #if !defined(NO_RESPONSE_BUFFERING)
  229. int i;
  230. int has_date = 0;
  231. int has_connection = 0;
  232. #endif
  233. if (conn == NULL) {
  234. /* Parameter error */
  235. return -1;
  236. }
  237. if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
  238. || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
  239. /* Only allowed in server context */
  240. return -2;
  241. }
  242. if (conn->request_state != 1) {
  243. /* only allowed if mg_response_header_start has been called before */
  244. return -3;
  245. }
  246. /* State: 2 */
  247. conn->request_state = 2;
  248. #if !defined(NO_RESPONSE_BUFFERING)
  249. if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
  250. #if defined USE_HTTP2
  251. int ret = http2_send_response_headers(conn);
  252. return ret ? 0 : 0; /* todo */
  253. #else
  254. return -2;
  255. #endif
  256. }
  257. /* Send */
  258. send_http1_response_status_line(conn);
  259. for (i = 0; i < conn->response_info.num_headers; i++) {
  260. mg_printf(conn,
  261. "%s: %s\r\n",
  262. conn->response_info.http_headers[i].name,
  263. conn->response_info.http_headers[i].value);
  264. /* Check for some special headers */
  265. if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) {
  266. has_date = 1;
  267. }
  268. if (!mg_strcasecmp("Connection",
  269. conn->response_info.http_headers[i].name)) {
  270. has_connection = 1;
  271. }
  272. }
  273. if (!has_date) {
  274. time_t curtime = time(NULL);
  275. char date[64];
  276. gmt_time_string(date, sizeof(date), &curtime);
  277. mg_printf(conn, "Date: %s\r\n", date);
  278. }
  279. if (!has_connection) {
  280. mg_printf(conn, "Connection: %s\r\n", suggest_connection_header(conn));
  281. }
  282. #endif
  283. mg_write(conn, "\r\n", 2);
  284. conn->request_state = 3;
  285. /* ok */
  286. return 0;
  287. }