libasyncd
ad_http_handler.c
Go to the documentation of this file.
1 /******************************************************************************
2  * libasyncd
3  *
4  * Copyright (c) 2014 Seungyoung Kim.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *****************************************************************************/
28 
29 /**
30  * HTTP protocol request/response handler.
31  *
32  * @file ad_http_handler.c
33  */
34 
35 #include <stdbool.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <unistd.h>
40 #include <limits.h>
41 #include <assert.h>
42 #include <errno.h>
43 #include <event2/buffer.h>
44 #include "qlibc/qlibc.h"
45 #include "ad_server.h"
46 #include "ad_http_handler.h"
47 #include "macro.h"
48 
49 #ifndef _DOXYGEN_SKIP
50 static ad_http_t *http_new(struct evbuffer *out);
51 static void http_free(ad_http_t *http);
52 static void http_free_cb(ad_conn_t *conn, void *userdata);
53 static size_t http_add_inbuf(struct evbuffer *buffer, ad_http_t *http,
54  size_t maxsize);
55 
56 static int http_parser(ad_http_t *http, struct evbuffer *in);
57 static int parse_requestline(ad_http_t *http, char *line);
58 static int parse_headers(ad_http_t *http, struct evbuffer *in);
59 static int parse_body(ad_http_t *http, struct evbuffer *in);
60 static ssize_t parse_chunked_body(ad_http_t *http, struct evbuffer *in);
61 
62 static bool isValidPathname(const char *path);
63 static void correctPathname(char *path);
64 static char *evbuffer_peekln(struct evbuffer *buffer, size_t *n_read_out,
65  enum evbuffer_eol_style eol_style);
66 static ssize_t evbuffer_drainln(struct evbuffer *buffer, size_t *n_read_out,
67  enum evbuffer_eol_style eol_style);
68 
69 #endif
70 
71 /**
72  * HTTP protocol handler hook.
73  *
74  * This hook provides an easy way to handle HTTP request/response.
75  *
76  * @note
77  * This hook must be registered at the top of hook chain.
78  *
79  * @code
80  * ad_server_t *server = ad_server_new();
81  * ad_server_register_hook(server, ad_http_handler, NULL);
82  * @endcode
83  */
84 int ad_http_handler(short event, ad_conn_t *conn, void *userdata) {
85  if (event & AD_EVENT_INIT) {
86  DEBUG("==> HTTP INIT");
87  ad_http_t *http = http_new(conn->out);
88  if (http == NULL)
89  return AD_CLOSE;
90  ad_conn_set_extra(conn, http, http_free_cb);
91  return AD_OK;
92  } else if (event & AD_EVENT_READ) {
93  DEBUG("==> HTTP READ");
94  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
95  int status = http_parser(http, conn->in);
96  if (conn->method == NULL && http->request.method != NULL) {
97  ad_conn_set_method(conn, http->request.method);
98  }
99  return status;
100  } else if (event & AD_EVENT_WRITE) {
101  DEBUG("==> HTTP WRITE");
102  return AD_OK;
103  } else if (event & AD_EVENT_CLOSE) {
104  DEBUG("==> HTTP CLOSE=%x (TIMEOUT=%d, SHUTDOWN=%d)",
105  event, event & AD_EVENT_TIMEOUT, event & AD_EVENT_SHUTDOWN);
106  return AD_OK;
107  }
108 
109  BUG_EXIT();
110  return AD_CLOSE;
111 }
112 
113 /**
114  * Return the request status.
115  */
117  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
118  if (http == NULL)
119  return AD_HTTP_ERROR;
120  return http->request.status;
121 }
122 
123 struct evbuffer *ad_http_get_inbuf(ad_conn_t *conn) {
124  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
125  return http->request.inbuf;
126 }
127 
128 struct evbuffer *ad_http_get_outbuf(ad_conn_t *conn) {
129  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
130  return http->response.outbuf;
131 }
132 
133 /**
134  * Get request header.
135  *
136  * @param name name of header.
137  *
138  * @return value of string if found, otherwise NULL.
139  */
140 const char *ad_http_get_request_header(ad_conn_t *conn, const char *name) {
141  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
142  return http->request.headers->getstr(http->request.headers, name, false);
143 }
144 
145 /**
146  * Return the size of content from the request.
147  */
149  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
150  return http->request.contentlength;
151 }
152 
153 
154 /**
155  * Return the actual size of data stored in in-buffer
156  */
158  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
159  return evbuffer_get_length(http->request.inbuf);
160 }
161 
162 /**
163  * Remove content from the in-buffer.
164  *
165  * The return data gets null terminated for convenience. For an example,
166  * if it reads 3 bytes, it will allocate 4 bytes and the 4th byte will
167  * be set to null terminator. `storedsized` will still return 3.
168  *
169  * @param maxsize maximum length of data to read. 0 to read everything.
170  * @param storedsize the size of data read and stored in the return.
171  */
172 void *ad_http_get_content(ad_conn_t *conn, size_t maxsize, size_t *storedsize) {
173  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
174 
175  size_t inbuflen = evbuffer_get_length(http->request.inbuf);
176  size_t readlen =
177  (maxsize == 0) ?
178  inbuflen : ((inbuflen < maxsize) ? inbuflen : maxsize);
179  if (readlen == 0)
180  return NULL;
181 
182  void *data = malloc(readlen + 1);
183  if (data == NULL)
184  return NULL;
185 
186  size_t removedlen = evbuffer_remove(http->request.inbuf, data, readlen);
187  ((char*)data)[removedlen] = '\0';
188  if (storedsize)
189  *storedsize = removedlen;
190 
191  return data;
192 }
193 
194 /**
195  * Return whether the request is keep-alive request or not.
196  *
197  * @return 1 if keep-alive request, otherwise 0.
198  */
200  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
201  if (http->request.httpver == NULL) {
202  return 0;
203  }
204 
205  const char *connection = ad_http_get_request_header(conn, "Connection");
206  if (!strcmp(http->request.httpver, HTTP_PROTOCOL_11)) {
207  // In HTTP/1.1, Keep-Alive is on by default unless explicitly specified.
208  if (connection != NULL && !strcmp(connection, "close")) {
209  return 0;
210  }
211  return 1;
212  } else {
213  // In older version, Keep-Alive is off by default unless requested.
214  if (connection != NULL
215  && (!strcmp(connection, "Keep-Alive")
216  || !strcmp(connection, "TE"))) {
217  return 1;
218  }
219  return 0;
220  }
221 }
222 
223 /**
224  * Set response header.
225  *
226  * @param name name of header.
227  * @param value value string to set. NULL to remove the header.
228  *
229  * @return 0 on success, -1 if we already sent it out.
230  */
231 int ad_http_set_response_header(ad_conn_t *conn, const char *name,
232  const char *value) {
233  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
234  if (http->response.frozen_header) {
235  return -1;
236  }
237 
238  if (value != NULL) {
239  http->response.headers->putstr(http->response.headers, name, value);
240  } else {
241  http->response.headers->remove(http->response.headers, name);
242  }
243 
244  return 0;
245 }
246 
247 /**
248  * Get response header.
249  *
250  * @param name name of header.
251  *
252  * @return value of string if found, otherwise NULL.
253  */
254 const char *ad_http_get_response_header(ad_conn_t *conn, const char *name) {
255  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
256  return http->response.headers->getstr(http->response.headers, name, false);
257 }
258 
259 /**
260  *
261  * @return 0 on success, -1 if we already sent it out.
262  */
263 int ad_http_set_response_code(ad_conn_t *conn, int code, const char *reason) {
264  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
265  if (http->response.frozen_header) {
266  return -1;
267  }
268 
269  http->response.code = code;
270  if (reason)
271  http->response.reason = strdup(reason);
272 
273  return 0;
274 }
275 
276 /**
277  *
278  * @param size content size. -1 for chunked transfer encoding.
279  * @return 0 on success, -1 if we already sent it out.
280  */
281 int ad_http_set_response_content(ad_conn_t *conn, const char *contenttype,
282  off_t size) {
283  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
284  if (http->response.frozen_header) {
285  return -1;
286  }
287 
288  // Set Content-Type header.
290  conn, "Content-Type",
291  (contenttype) ? contenttype : HTTP_DEF_CONTENTTYPE);
292  if (size >= 0) {
293  char clenval[20 + 1];
294  sprintf(clenval, "%jd", size);
295  ad_http_set_response_header(conn, "Content-Length", clenval);
296  http->response.contentlength = size;
297  } else {
298  ad_http_set_response_header(conn, "Transfer-Encoding", "chunked");
299  http->response.contentlength = -1;
300  }
301 
302  return 0;
303 }
304 
305 /**
306  * @return total bytes sent, 0 on error.
307  */
308 size_t ad_http_response(ad_conn_t *conn, int code, const char *contenttype,
309  const void *data, off_t size) {
310  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
311  if (http->response.frozen_header) {
312  return 0;
313  }
314 
315  // Set response headers.
316  if (ad_http_get_response_header(conn, "Connection") == NULL) {
318  conn, "Connection",
319  (ad_http_is_keepalive_request(conn)) ? "Keep-Alive" : "close");
320  }
321 
323  ad_http_set_response_content(conn, contenttype, size);
324  return ad_http_send_data(conn, data, size);
325 }
326 
327 /**
328  *
329  * @return 0 total bytes put in out buffer, -1 if we already sent it out.
330  */
332  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
333  if (http->response.frozen_header) {
334  return 0;
335  }
336  http->response.frozen_header = true;
337 
338  // Send status line.
339  const char *reason =
340  (http->response.reason) ?
341  http->response.reason :
343  evbuffer_add_printf(http->response.outbuf, "%s %d %s" HTTP_CRLF,
344  http->request.httpver, http->response.code, reason);
345 
346  // Send headers.
347  qlisttbl_obj_t obj;
348  bzero((void*) &obj, sizeof(obj));
349  qlisttbl_t *tbl = http->response.headers;
350  tbl->lock(tbl);
351  while (tbl->getnext(tbl, &obj, NULL, false)) {
352  evbuffer_add_printf(http->response.outbuf, "%s: %s" HTTP_CRLF,
353  (char*) obj.name, (char*) obj.data);
354  }
355  tbl->unlock(tbl);
356 
357  // Send empty line, indicator of end of header.
358  evbuffer_add(http->response.outbuf, HTTP_CRLF, CONST_STRLEN(HTTP_CRLF));
359 
360  return evbuffer_get_length(http->response.outbuf);
361 }
362 
363 /**
364  *
365  * @return 0 on success, -1 if we already sent it out.
366  */
367 size_t ad_http_send_data(ad_conn_t *conn, const void *data, size_t size) {
368  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
369 
370  if (http->response.contentlength < 0) {
371  WARN("Content-Length is not set. Invalid usage.");
372  return 0;
373  }
374 
375  if ((http->response.bodyout + size) > http->response.contentlength) {
376  WARN("Trying to send more data than supposed to");
377  return 0;
378  }
379 
380  size_t beforesize = evbuffer_get_length(http->response.outbuf);
381  if (!http->response.frozen_header) {
382  ad_http_send_header(conn);
383  }
384 
385  if (data != NULL && size > 0) {
386  if (evbuffer_add(http->response.outbuf, data, size))
387  return 0;
388  }
389 
390  http->response.bodyout += size;
391  return (evbuffer_get_length(http->response.outbuf) - beforesize);
392 }
393 
394 size_t ad_http_send_chunk(ad_conn_t *conn, const void *data, size_t size) {
395  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
396 
397  if (http->response.contentlength >= 0) {
398  WARN("Content-Length is set. Invalid usage.");
399  return 0;
400  }
401 
402  if (!http->response.frozen_header) {
403  ad_http_send_header(conn);
404  }
405 
406  size_t beforesize = evbuffer_get_length(http->response.outbuf);
407  int status = 0;
408  if (size > 0) {
409  status += evbuffer_add_printf(http->response.outbuf, "%zu" HTTP_CRLF,
410  size);
411  status += evbuffer_add(http->response.outbuf, data, size);
412  status += evbuffer_add(http->response.outbuf, HTTP_CRLF,
413  CONST_STRLEN(HTTP_CRLF));
414  } else {
415  status += evbuffer_add_printf(http->response.outbuf,
416  "0" HTTP_CRLF HTTP_CRLF);
417  }
418  if (status != 0) {
419  WARN("Failed to add data to out-buffer. (size:%jd)", size);
420  return 0;
421  }
422 
423  size_t bytesout = evbuffer_get_length(http->response.outbuf) - beforesize;
424  http->response.bodyout += bytesout;
425  return bytesout;
426 }
427 
428 const char *ad_http_get_reason(int code) {
429  switch (code) {
430  case HTTP_CODE_CONTINUE:
431  return "Continue";
432  case HTTP_CODE_OK:
433  return "OK";
434  case HTTP_CODE_CREATED:
435  return "Created";
437  return "No content";
439  return "Partial Content";
441  return "Multi Status";
443  return "Moved Temporarily";
445  return "Not Modified";
447  return "Bad Request";
449  return "Authorization Required";
450  case HTTP_CODE_FORBIDDEN:
451  return "Forbidden";
452  case HTTP_CODE_NOT_FOUND:
453  return "Not Found";
455  return "Method Not Allowed";
457  return "Request Time Out";
458  case HTTP_CODE_GONE:
459  return "Gone";
461  return "Request URI Too Long";
462  case HTTP_CODE_LOCKED:
463  return "Locked";
465  return "Internal Server Error";
467  return "Not Implemented";
469  return "Service Unavailable";
470  }
471 
472  WARN("Undefined code found. %d", code);
473  return "-";
474 }
475 
476 /******************************************************************************
477  * Private internal functions.
478  *****************************************************************************/
479 #ifndef _DOXYGEN_SKIP
480 
481 static ad_http_t *http_new(struct evbuffer *out) {
482  // Create a new connection container.
483  ad_http_t *http = NEW_OBJECT(ad_http_t);
484  if (http == NULL)
485  return NULL;
486 
487  // Allocate additional resources.
488  http->request.inbuf = evbuffer_new();
489  http->request.headers = qlisttbl(
490  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
491  http->response.headers = qlisttbl(
492  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
493  if (http->request.inbuf == NULL || http->request.headers == NULL
494  || http->response.headers == NULL) {
495  http_free(http);
496  return NULL;
497  }
498 
499  // Initialize structure.
501  http->request.contentlength = -1;
502  http->response.contentlength = -1;
503  http->response.outbuf = out;
504 
505  return http;
506 }
507 
508 static void http_free(ad_http_t *http) {
509  if (http) {
510  if (http->request.inbuf)
511  evbuffer_free(http->request.inbuf);
512  if (http->request.method)
513  free(http->request.method);
514  if (http->request.uri)
515  free(http->request.uri);
516  if (http->request.httpver)
517  free(http->request.httpver);
518  if (http->request.path)
519  free(http->request.path);
520  if (http->request.query)
521  free(http->request.query);
522 
523  if (http->request.headers)
524  http->request.headers->free(http->request.headers);
525  if (http->request.host)
526  free(http->request.host);
527  if (http->request.domain)
528  free(http->request.domain);
529 
530  if (http->response.headers)
531  http->response.headers->free(http->response.headers);
532  if (http->response.reason)
533  free(http->response.reason);
534 
535  free(http);
536  }
537 }
538 
539 static void http_free_cb(ad_conn_t *conn, void *userdata) {
540  http_free((ad_http_t *) userdata);
541 }
542 
543 static size_t http_add_inbuf(struct evbuffer *buffer, ad_http_t *http,
544  size_t maxsize) {
545  if (maxsize == 0 || evbuffer_get_length(buffer) == 0) {
546  return 0;
547  }
548 
549  return evbuffer_remove_buffer(buffer, http->request.inbuf, maxsize);
550 }
551 
552 static int http_parser(ad_http_t *http, struct evbuffer *in) {
553  ASSERT(http != NULL && in != NULL);
554 
555  if (http->request.status == AD_HTTP_REQ_INIT) {
556  char *line = evbuffer_readln(in, NULL, EVBUFFER_EOL_CRLF);
557  if (line == NULL)
558  return http->request.status;
559  http->request.status = parse_requestline(http, line);
560  free(line);
561  // Do not call user callbacks until I reach the next state.
562  if (http->request.status == AD_HTTP_REQ_INIT) {
563  return AD_TAKEOVER;
564  }
565  }
566 
568  http->request.status = parse_headers(http, in);
569  // Do not call user callbacks until I reach the next state.
571  return AD_TAKEOVER;
572  }
573  }
574 
575  if (http->request.status == AD_HTTP_REQ_HEADER_DONE) {
576  http->request.status = parse_body(http, in);
577  // Do not call user callbacks until I reach the next state.
578  if (http->request.status == AD_HTTP_REQ_HEADER_DONE) {
579  return AD_TAKEOVER;
580  }
581  }
582 
583  if (http->request.status == AD_HTTP_REQ_DONE) {
584  return AD_OK;
585  }
586 
587  if (http->request.status == AD_HTTP_ERROR) {
588  return AD_CLOSE;
589  }
590 
591  BUG_EXIT();
592  return AD_CLOSE;
593 }
594 
595 static int parse_requestline(ad_http_t *http, char *line) {
596  // Parse request line.
597  char *saveptr;
598  char *method = strtok_r(line, " ", &saveptr);
599  char *uri = strtok_r(NULL, " ", &saveptr);
600  char *httpver = strtok_r(NULL, " ", &saveptr);
601  char *tmp = strtok_r(NULL, " ", &saveptr);
602 
603  if (method == NULL || uri == NULL || httpver == NULL || tmp != NULL) {
604  DEBUG("Invalid request line. %s", line);
605  return AD_HTTP_ERROR;
606  }
607 
608  // Set request method
609  http->request.method = qstrupper(strdup(method));
610 
611  // Set HTTP version
612  http->request.httpver = qstrupper(strdup(httpver));
613  if (strcmp(http->request.httpver, HTTP_PROTOCOL_09)
614  && strcmp(http->request.httpver, HTTP_PROTOCOL_10)
615  && strcmp(http->request.httpver, HTTP_PROTOCOL_11)) {
616  DEBUG("Unknown protocol: %s", http->request.httpver);
617  return AD_HTTP_ERROR;
618  }
619 
620  // Set URI
621  if (uri[0] == '/') {
622  http->request.uri = strdup(uri);
623  } else if ((tmp = strstr(uri, "://"))) {
624  // divide URI into host and path
625  char *path = strstr(tmp + CONST_STRLEN("://"), "/");
626  if (path == NULL) { // URI has no path ex) http://domain.com:80
627  http->request.headers->putstr(http->request.headers, "Host",
628  tmp + CONST_STRLEN("://"));
629  http->request.uri = strdup("/");
630  } else { // URI has path, ex) http://domain.com:80/path
631  *path = '\0';
632  http->request.headers->putstr(http->request.headers, "Host",
633  tmp + CONST_STRLEN("://"));
634  *path = '/';
635  http->request.uri = strdup(path);
636  }
637  } else {
638  DEBUG("Invalid URI format. %s", uri);
639  return AD_HTTP_ERROR;
640  }
641 
642  // Set request path. Only path part from URI.
643  http->request.path = strdup(http->request.uri);
644  tmp = strstr(http->request.path, "?");
645  if (tmp) {
646  *tmp = '\0';
647  http->request.query = strdup(tmp + 1);
648  } else {
649  http->request.query = strdup("");
650  }
651  qurl_decode(http->request.path);
652 
653  // check path
654  if (isValidPathname(http->request.path) == false) {
655  DEBUG("Invalid URI format : %s", http->request.uri);
656  return AD_HTTP_ERROR;
657  }
658  correctPathname(http->request.path);
659 
660  DEBUG("Method=%s, URI=%s, VER=%s", http->request.method, http->request.uri, http->request.httpver);
661 
663 }
664 
665 static int parse_headers(ad_http_t *http, struct evbuffer *in) {
666  char *line;
667  while ((line = evbuffer_readln(in, NULL, EVBUFFER_EOL_CRLF))) {
668  if (IS_EMPTY_STR(line)) {
669  const char *clen = http->request.headers->getstr(
670  http->request.headers, "Content-Length", false);
671  http->request.contentlength = (clen) ? atol(clen) : -1;
672  free(line);
674  }
675  // Parse
676  char *name, *value;
677  char *tmp = strstr(line, ":");
678  if (tmp) {
679  *tmp = '\0';
680  name = qstrtrim(line);
681  value = qstrtrim(tmp + 1);
682  } else {
683  name = qstrtrim(line);
684  value = "";
685  }
686  // Add
687  http->request.headers->putstr(http->request.headers, name, value);
688 
689  free(line);
690  }
691 
692  return http->request.status;
693 }
694 
695 static int parse_body(ad_http_t *http, struct evbuffer *in) {
696  // Handle static data case.
697  if (http->request.contentlength == 0) {
698  return AD_HTTP_REQ_DONE;
699  } else if (http->request.contentlength > 0) {
700  if (http->request.contentlength > http->request.bodyin) {
701  size_t maxread = http->request.contentlength - http->request.bodyin;
702  if (maxread > 0 && evbuffer_get_length(in) > 0) {
703  http->request.bodyin += http_add_inbuf(in, http, maxread);
704  }
705  }
706  if (http->request.contentlength == http->request.bodyin) {
707  return AD_HTTP_REQ_DONE;
708  }
709  } else {
710  // Check if Transfer-Encoding is chunked.
711  const char *tranenc = http->request.headers->getstr(
712  http->request.headers, "Transfer-Encoding", false);
713  if (tranenc != NULL && !strcmp(tranenc, "chunked")) {
714  // TODO: handle chunked encoding
715  for (;;) {
716  ssize_t chunksize = parse_chunked_body(http, in);
717  if (chunksize > 0) {
718  continue;
719  } else if (chunksize == 0) {
720  return AD_HTTP_REQ_DONE;
721  } else if (chunksize == -1) {
722  return http->request.status;
723  } else {
724  return AD_HTTP_ERROR;
725  }
726  }
727  } else {
728  return AD_HTTP_REQ_DONE;
729  }
730  }
731 
732  return http->request.status;
733 }
734 
735 /**
736  * Parse chunked body and append it to inbuf.
737  *
738  * @return number of bytes in a chunk. so 0 for the ending chunk. -1 for not enough data, -2 format error.
739  */
740 static ssize_t parse_chunked_body(ad_http_t *http, struct evbuffer *in) {
741  // Peek chunk size.
742  size_t crlf_len = 0;
743  char *line = evbuffer_peekln(in, &crlf_len, EVBUFFER_EOL_CRLF);
744  if (line == NULL)
745  return -1; // not enough data.
746  size_t linelen = strlen(line);
747 
748  // Parse chunk size
749  int chunksize = -1;
750  sscanf(line, "%x", &chunksize);
751  free(line);
752  if (chunksize < 0)
753  return -2; // format error
754 
755  // Check if we've received whole data of this chunk.
756  size_t datalen = linelen + crlf_len + chunksize + crlf_len;
757  size_t inbuflen = evbuffer_get_length(in);
758  if (inbuflen < datalen) {
759  return -1; // not enough data.
760  }
761 
762  // Copy chunk body
763  evbuffer_drainln(in, NULL, EVBUFFER_EOL_CRLF);
764  http_add_inbuf(in, http, chunksize);
765  evbuffer_drainln(in, NULL, EVBUFFER_EOL_CRLF);
766 
767  return chunksize;
768 }
769 
770 /**
771  * validate file path
772  */
773 static bool isValidPathname(const char *path) {
774  if (path == NULL)
775  return false;
776 
777  int len = strlen(path);
778  if (len == 0 || len >= PATH_MAX)
779  return false;
780  else if (path[0] != '/')
781  return false;
782  else if (strpbrk(path, "\\:*?\"<>|") != NULL)
783  return false;
784 
785  // check folder name length
786  int n;
787  char *t;
788  for (n = 0, t = (char *) path; *t != '\0'; t++) {
789  if (*t == '/') {
790  n = 0;
791  continue;
792  }
793  if (n >= FILENAME_MAX) {
794  DEBUG("Filename too long.");
795  return false;
796  }
797  n++;
798  }
799 
800  return true;
801 }
802 
803 /**
804  * Correct pathname.
805  *
806  * @note
807  * remove : heading & tailing white spaces, double slashes, tailing slash
808  */
809 static void correctPathname(char *path) {
810  // Take care of head & tail white spaces.
811  qstrtrim(path);
812 
813  // Take care of double slashes.
814  while (strstr(path, "//") != NULL)
815  qstrreplace("sr", path, "//", "/");
816 
817  // Take care of tailing slash.
818  int len = strlen(path);
819  if (len <= 1)
820  return;
821  if (path[len - 1] == '/')
822  path[len - 1] = '\0';
823 }
824 
825 static char *evbuffer_peekln(struct evbuffer *buffer, size_t *n_read_out,
826  enum evbuffer_eol_style eol_style) {
827  // Check if first line has arrived.
828  struct evbuffer_ptr ptr = evbuffer_search_eol(buffer, NULL, n_read_out,
829  eol_style);
830  if (ptr.pos == -1)
831  return NULL;
832 
833  char *line = (char *) malloc(ptr.pos + 1);
834  if (line == NULL)
835  return NULL;
836 
837  // Linearizes buffer
838  if (ptr.pos > 0) {
839  char *bufferptr = (char *) evbuffer_pullup(buffer, ptr.pos);
840  ASSERT(bufferptr != NULL);
841  strncpy(line, bufferptr, ptr.pos);
842  }
843  line[ptr.pos] = '\0';
844 
845  return line;
846 }
847 
848 static ssize_t evbuffer_drainln(struct evbuffer *buffer, size_t *n_read_out,
849  enum evbuffer_eol_style eol_style) {
850  char *line = evbuffer_readln(buffer, n_read_out, eol_style);
851  if (line == NULL)
852  return -1;
853 
854  size_t linelen = strlen(line);
855  free(line);
856  return linelen;
857 }
858 
859 #endif // _DOXYGEN_SKIP
struct evbuffer * outbuf
void ad_conn_set_method(ad_conn_t *conn, char *method)
Set method name on this connection.
Definition: ad_server.c:532
#define HTTP_CODE_BAD_REQUEST
#define HTTP_PROTOCOL_10
#define HTTP_CODE_UNAUTHORIZED
#define HTTP_CODE_MULTI_STATUS
#define HTTP_CODE_CREATED
#define HTTP_CODE_REQUEST_URI_TOO_LONG
size_t ad_http_response(ad_conn_t *conn, int code, const char *contenttype, const void *data, off_t size)
#define HTTP_CODE_SERVICE_UNAVAILABLE
#define HTTP_CODE_REQUEST_TIME_OUT
void * ad_http_get_content(ad_conn_t *conn, size_t maxsize, size_t *storedsize)
Remove content from the in-buffer.
bool frozen_header
size_t ad_http_get_content_length_stored(ad_conn_t *conn)
Return the actual size of data stored in in-buffer.
const char * ad_http_get_response_header(ad_conn_t *conn, const char *name)
Get response header.
int ad_http_is_keepalive_request(ad_conn_t *conn)
Return whether the request is keep-alive request or not.
#define AD_EVENT_WRITE
Definition: ad_server.h:125
#define HTTP_CODE_NO_CONTENT
#define AD_EVENT_TIMEOUT
Definition: ad_server.h:127
#define AD_EVENT_SHUTDOWN
Definition: ad_server.h:128
#define AD_CLOSE
Definition: ad_server.h:60
struct evbuffer * out
Definition: ad_server.h:163
struct ad_http_s::@1 response
#define HTTP_CODE_GONE
char * httpver
#define AD_EVENT_CLOSE
Definition: ad_server.h:126
const char * ad_http_get_request_header(ad_conn_t *conn, const char *name)
Get request header.
struct evbuffer * in
Definition: ad_server.h:162
int ad_http_handler(short event, ad_conn_t *conn, void *userdata)
HTTP protocol handler hook.
char * method
Definition: ad_server.h:168
#define HTTP_PROTOCOL_11
#define HTTP_CRLF
int ad_http_set_response_content(ad_conn_t *conn, const char *contenttype, off_t size)
#define HTTP_CODE_OK
size_t bodyout
enum ad_http_request_status_e status
#define HTTP_CODE_FORBIDDEN
Connection structure.
Definition: ad_server.h:159
const char * ad_http_get_reason(int code)
#define HTTP_CODE_PARTIAL_CONTENT
#define AD_EVENT_INIT
Event types.
Definition: ad_server.h:123
#define HTTP_CODE_NOT_MODIFIED
size_t ad_http_send_chunk(ad_conn_t *conn, const void *data, size_t size)
#define AD_EVENT_READ
Definition: ad_server.h:124
#define HTTP_PROTOCOL_09
#define AD_TAKEOVER
Definition: ad_server.h:58
struct evbuffer * ad_http_get_outbuf(ad_conn_t *conn)
#define HTTP_DEF_CONTENTTYPE
ad_http_handler header file
ad_server header file
#define HTTP_CODE_NOT_FOUND
void * ad_conn_set_extra(ad_conn_t *conn, const void *extra, ad_userdata_free_cb free_cb)
Set extra userdata into the connection.
Definition: ad_server.c:513
qlisttbl_t * headers
#define HTTP_CODE_METHOD_NOT_ALLOWED
int ad_http_set_response_code(ad_conn_t *conn, int code, const char *reason)
ad_http_request_status_e
#define HTTP_CODE_LOCKED
enum ad_http_request_status_e ad_http_get_status(ad_conn_t *conn)
Return the request status.
struct evbuffer * ad_http_get_inbuf(ad_conn_t *conn)
size_t ad_http_send_header(ad_conn_t *conn)
off_t contentlength
#define HTTP_CODE_CONTINUE
int ad_http_set_response_header(ad_conn_t *conn, const char *name, const char *value)
Set response header.
#define HTTP_CODE_INTERNAL_SERVER_ERROR
size_t ad_http_send_data(ad_conn_t *conn, const void *data, size_t size)
#define AD_OK
Definition: ad_server.h:57
#define HTTP_CODE_NOT_IMPLEMENTED
void * ad_conn_get_extra(ad_conn_t *conn)
Get extra userdata attached in this connection.
Definition: ad_server.c:520
off_t ad_http_get_content_length(ad_conn_t *conn)
Return the size of content from the request.
struct evbuffer * inbuf
#define HTTP_CODE_MOVED_TEMPORARILY
struct ad_http_s::@0 request