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  * Remove content from the in-buffer.
155  *
156  * @param maxsize maximum length of data to pull up. 0 to pull up everything.
157  */
158 void *ad_http_get_content(ad_conn_t *conn, size_t maxsize, size_t *storedsize) {
159  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
160 
161  size_t inbuflen = evbuffer_get_length(http->request.inbuf);
162  size_t readlen =
163  (maxsize == 0) ?
164  inbuflen : ((inbuflen < maxsize) ? inbuflen : maxsize);
165  if (readlen == 0)
166  return NULL;
167 
168  void *data = malloc(readlen);
169  if (data == NULL)
170  return NULL;
171 
172  size_t removedlen = evbuffer_remove(http->request.inbuf, data, readlen);
173  if (storedsize)
174  *storedsize = removedlen;
175 
176  return data;
177 }
178 
179 /**
180  * Return whether the request is keep-alive request or not.
181  *
182  * @return 1 if keep-alive request, otherwise 0.
183  */
185  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
186  if (http->request.httpver == NULL) {
187  return 0;
188  }
189 
190  const char *connection = ad_http_get_request_header(conn, "Connection");
191  if (!strcmp(http->request.httpver, HTTP_PROTOCOL_11)) {
192  // In HTTP/1.1, Keep-Alive is on by default unless explicitly specified.
193  if (connection != NULL && !strcmp(connection, "close")) {
194  return 0;
195  }
196  return 1;
197  } else {
198  // In older version, Keep-Alive is off by default unless requested.
199  if (connection != NULL
200  && (!strcmp(connection, "Keep-Alive")
201  || !strcmp(connection, "TE"))) {
202  return 1;
203  }
204  return 0;
205  }
206 }
207 
208 /**
209  * Set response header.
210  *
211  * @param name name of header.
212  * @param value value string to set. NULL to remove the header.
213  *
214  * @return 0 on success, -1 if we already sent it out.
215  */
216 int ad_http_set_response_header(ad_conn_t *conn, const char *name,
217  const char *value) {
218  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
219  if (http->response.frozen_header) {
220  return -1;
221  }
222 
223  if (value != NULL) {
224  http->response.headers->putstr(http->response.headers, name, value);
225  } else {
226  http->response.headers->remove(http->response.headers, name);
227  }
228 
229  return 0;
230 }
231 
232 /**
233  * Get response header.
234  *
235  * @param name name of header.
236  *
237  * @return value of string if found, otherwise NULL.
238  */
239 const char *ad_http_get_response_header(ad_conn_t *conn, const char *name) {
240  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
241  return http->response.headers->getstr(http->response.headers, name, false);
242 }
243 
244 /**
245  *
246  * @return 0 on success, -1 if we already sent it out.
247  */
248 int ad_http_set_response_code(ad_conn_t *conn, int code, const char *reason) {
249  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
250  if (http->response.frozen_header) {
251  return -1;
252  }
253 
254  http->response.code = code;
255  if (reason)
256  http->response.reason = strdup(reason);
257 
258  return 0;
259 }
260 
261 /**
262  *
263  * @param size content size. -1 for chunked transfer encoding.
264  * @return 0 on success, -1 if we already sent it out.
265  */
266 int ad_http_set_response_content(ad_conn_t *conn, const char *contenttype,
267  off_t size) {
268  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
269  if (http->response.frozen_header) {
270  return -1;
271  }
272 
273  // Set Content-Type header.
275  conn, "Content-Type",
276  (contenttype) ? contenttype : HTTP_DEF_CONTENTTYPE);
277  if (size >= 0) {
278  char clenval[20 + 1];
279  sprintf(clenval, "%jd", size);
280  ad_http_set_response_header(conn, "Content-Length", clenval);
281  http->response.contentlength = size;
282  } else {
283  ad_http_set_response_header(conn, "Transfer-Encoding", "chunked");
284  http->response.contentlength = -1;
285  }
286 
287  return 0;
288 }
289 
290 /**
291  * @return total bytes sent, 0 on error.
292  */
293 size_t ad_http_response(ad_conn_t *conn, int code, const char *contenttype,
294  const void *data, off_t size) {
295  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
296  if (http->response.frozen_header) {
297  return 0;
298  }
299 
300  // Set response headers.
301  if (ad_http_get_response_header(conn, "Connection") == NULL) {
303  conn, "Connection",
304  (ad_http_is_keepalive_request(conn)) ? "Keep-Alive" : "close");
305  }
306 
308  ad_http_set_response_content(conn, contenttype, size);
309  return ad_http_send_data(conn, data, size);
310 }
311 
312 /**
313  *
314  * @return 0 total bytes put in out buffer, -1 if we already sent it out.
315  */
317  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
318  if (http->response.frozen_header) {
319  return 0;
320  }
321  http->response.frozen_header = true;
322 
323  // Send status line.
324  const char *reason =
325  (http->response.reason) ?
326  http->response.reason :
328  evbuffer_add_printf(http->response.outbuf, "%s %d %s" HTTP_CRLF,
329  http->request.httpver, http->response.code, reason);
330 
331  // Send headers.
332  qdlnobj_t obj;
333  bzero((void*) &obj, sizeof(obj));
334  qlisttbl_t *tbl = http->response.headers;
335  tbl->lock(tbl);
336  while (tbl->getnext(tbl, &obj, NULL, false)) {
337  evbuffer_add_printf(http->response.outbuf, "%s: %s" HTTP_CRLF,
338  (char*) obj.name, (char*) obj.data);
339  }
340  tbl->unlock(tbl);
341 
342  // Send empty line, indicator of end of header.
343  evbuffer_add(http->response.outbuf, HTTP_CRLF, CONST_STRLEN(HTTP_CRLF));
344 
345  return evbuffer_get_length(http->response.outbuf);
346 }
347 
348 /**
349  *
350  * @return 0 on success, -1 if we already sent it out.
351  */
352 size_t ad_http_send_data(ad_conn_t *conn, const void *data, size_t size) {
353  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
354 
355  if (http->response.contentlength < 0) {
356  WARN("Content-Length is not set. Invalid usage.");
357  return 0;
358  }
359 
360  if ((http->response.bodyout + size) > http->response.contentlength) {
361  WARN("Trying to send more data than supposed to");
362  return 0;
363  }
364 
365  size_t beforesize = evbuffer_get_length(http->response.outbuf);
366  if (!http->response.frozen_header) {
367  ad_http_send_header(conn);
368  }
369 
370  if (data != NULL && size > 0) {
371  if (evbuffer_add(http->response.outbuf, data, size))
372  return 0;
373  }
374 
375  http->response.bodyout += size;
376  return (evbuffer_get_length(http->response.outbuf) - beforesize);
377 }
378 
379 size_t ad_http_send_chunk(ad_conn_t *conn, const void *data, size_t size) {
380  ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
381 
382  if (http->response.contentlength >= 0) {
383  WARN("Content-Length is set. Invalid usage.");
384  return 0;
385  }
386 
387  if (!http->response.frozen_header) {
388  ad_http_send_header(conn);
389  }
390 
391  size_t beforesize = evbuffer_get_length(http->response.outbuf);
392  int status = 0;
393  if (size > 0) {
394  status += evbuffer_add_printf(http->response.outbuf, "%zu" HTTP_CRLF,
395  size);
396  status += evbuffer_add(http->response.outbuf, data, size);
397  status += evbuffer_add(http->response.outbuf, HTTP_CRLF,
398  CONST_STRLEN(HTTP_CRLF));
399  } else {
400  status += evbuffer_add_printf(http->response.outbuf,
401  "0" HTTP_CRLF HTTP_CRLF);
402  }
403  if (status != 0) {
404  WARN("Failed to add data to out-buffer. (size:%jd)", size);
405  return 0;
406  }
407 
408  size_t bytesout = evbuffer_get_length(http->response.outbuf) - beforesize;
409  http->response.bodyout += bytesout;
410  return bytesout;
411 }
412 
413 const char *ad_http_get_reason(int code) {
414  switch (code) {
415  case HTTP_CODE_CONTINUE:
416  return "Continue";
417  case HTTP_CODE_OK:
418  return "OK";
419  case HTTP_CODE_CREATED:
420  return "Created";
422  return "No content";
424  return "Partial Content";
426  return "Multi Status";
428  return "Moved Temporarily";
430  return "Not Modified";
432  return "Bad Request";
434  return "Authorization Required";
435  case HTTP_CODE_FORBIDDEN:
436  return "Forbidden";
437  case HTTP_CODE_NOT_FOUND:
438  return "Not Found";
440  return "Method Not Allowed";
442  return "Request Time Out";
443  case HTTP_CODE_GONE:
444  return "Gone";
446  return "Request URI Too Long";
447  case HTTP_CODE_LOCKED:
448  return "Locked";
450  return "Internal Server Error";
452  return "Not Implemented";
454  return "Service Unavailable";
455  }
456 
457  WARN("Undefined code found. %d", code);
458  return "-";
459 }
460 
461 /******************************************************************************
462  * Private internal functions.
463  *****************************************************************************/
464 #ifndef _DOXYGEN_SKIP
465 
466 static ad_http_t *http_new(struct evbuffer *out) {
467  // Create a new connection container.
468  ad_http_t *http = NEW_OBJECT(ad_http_t);
469  if (http == NULL)
470  return NULL;
471 
472  // Allocate additional resources.
473  http->request.inbuf = evbuffer_new();
474  http->request.headers = qlisttbl(
475  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
476  http->response.headers = qlisttbl(
477  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
478  if (http->request.inbuf == NULL || http->request.headers == NULL
479  || http->response.headers == NULL) {
480  http_free(http);
481  return NULL;
482  }
483 
484  // Initialize structure.
486  http->request.contentlength = -1;
487  http->response.contentlength = -1;
488  http->response.outbuf = out;
489 
490  return http;
491 }
492 
493 static void http_free(ad_http_t *http) {
494  if (http) {
495  if (http->request.inbuf)
496  evbuffer_free(http->request.inbuf);
497  if (http->request.method)
498  free(http->request.method);
499  if (http->request.uri)
500  free(http->request.uri);
501  if (http->request.httpver)
502  free(http->request.httpver);
503  if (http->request.path)
504  free(http->request.path);
505  if (http->request.query)
506  free(http->request.query);
507 
508  if (http->request.headers)
509  http->request.headers->free(http->request.headers);
510  if (http->request.host)
511  free(http->request.host);
512  if (http->request.domain)
513  free(http->request.domain);
514 
515  if (http->response.headers)
516  http->response.headers->free(http->response.headers);
517  if (http->response.reason)
518  free(http->response.reason);
519 
520  free(http);
521  }
522 }
523 
524 static void http_free_cb(ad_conn_t *conn, void *userdata) {
525  http_free((ad_http_t *) userdata);
526 }
527 
528 static size_t http_add_inbuf(struct evbuffer *buffer, ad_http_t *http,
529  size_t maxsize) {
530  if (maxsize == 0 || evbuffer_get_length(buffer) == 0) {
531  return 0;
532  }
533 
534  return evbuffer_remove_buffer(buffer, http->request.inbuf, maxsize);
535 }
536 
537 static int http_parser(ad_http_t *http, struct evbuffer *in) {
538  ASSERT(http != NULL && in != NULL);
539 
540  if (http->request.status == AD_HTTP_REQ_INIT) {
541  char *line = evbuffer_readln(in, NULL, EVBUFFER_EOL_CRLF);
542  if (line == NULL)
543  return http->request.status;
544  http->request.status = parse_requestline(http, line);
545  free(line);
546  // Do not call user callbacks until I reach the next state.
547  if (http->request.status == AD_HTTP_REQ_INIT) {
548  return AD_TAKEOVER;
549  }
550  }
551 
553  http->request.status = parse_headers(http, in);
554  // Do not call user callbacks until I reach the next state.
556  return AD_TAKEOVER;
557  }
558  }
559 
560  if (http->request.status == AD_HTTP_REQ_HEADER_DONE) {
561  http->request.status = parse_body(http, in);
562  // Do not call user callbacks until I reach the next state.
563  if (http->request.status == AD_HTTP_REQ_HEADER_DONE) {
564  return AD_TAKEOVER;
565  }
566  }
567 
568  if (http->request.status == AD_HTTP_REQ_DONE) {
569  return AD_OK;
570  }
571 
572  if (http->request.status == AD_HTTP_ERROR) {
573  return AD_CLOSE;
574  }
575 
576  BUG_EXIT();
577  return AD_CLOSE;
578 }
579 
580 static int parse_requestline(ad_http_t *http, char *line) {
581  // Parse request line.
582  char *saveptr;
583  char *method = strtok_r(line, " ", &saveptr);
584  char *uri = strtok_r(NULL, " ", &saveptr);
585  char *httpver = strtok_r(NULL, " ", &saveptr);
586  char *tmp = strtok_r(NULL, " ", &saveptr);
587 
588  if (method == NULL || uri == NULL || httpver == NULL || tmp != NULL) {
589  DEBUG("Invalid request line. %s", line);
590  return AD_HTTP_ERROR;
591  }
592 
593  // Set request method
594  http->request.method = qstrupper(strdup(method));
595 
596  // Set HTTP version
597  http->request.httpver = qstrupper(strdup(httpver));
598  if (strcmp(http->request.httpver, HTTP_PROTOCOL_09)
599  && strcmp(http->request.httpver, HTTP_PROTOCOL_10)
600  && strcmp(http->request.httpver, HTTP_PROTOCOL_11)) {
601  DEBUG("Unknown protocol: %s", http->request.httpver);
602  return AD_HTTP_ERROR;
603  }
604 
605  // Set URI
606  if (uri[0] == '/') {
607  http->request.uri = strdup(uri);
608  } else if ((tmp = strstr(uri, "://"))) {
609  // divide URI into host and path
610  char *path = strstr(tmp + CONST_STRLEN("://"), "/");
611  if (path == NULL) { // URI has no path ex) http://domain.com:80
612  http->request.headers->putstr(http->request.headers, "Host",
613  tmp + CONST_STRLEN("://"));
614  http->request.uri = strdup("/");
615  } else { // URI has path, ex) http://domain.com:80/path
616  *path = '\0';
617  http->request.headers->putstr(http->request.headers, "Host",
618  tmp + CONST_STRLEN("://"));
619  *path = '/';
620  http->request.uri = strdup(path);
621  }
622  } else {
623  DEBUG("Invalid URI format. %s", uri);
624  return AD_HTTP_ERROR;
625  }
626 
627  // Set request path. Only path part from URI.
628  http->request.path = strdup(http->request.uri);
629  tmp = strstr(http->request.path, "?");
630  if (tmp) {
631  *tmp = '\0';
632  http->request.query = strdup(tmp + 1);
633  } else {
634  http->request.query = strdup("");
635  }
636  qurl_decode(http->request.path);
637 
638  // check path
639  if (isValidPathname(http->request.path) == false) {
640  DEBUG("Invalid URI format : %s", http->request.uri);
641  return AD_HTTP_ERROR;
642  }
643  correctPathname(http->request.path);
644 
645  DEBUG("Method=%s, URI=%s, VER=%s", http->request.method, http->request.uri, http->request.httpver);
646 
648 }
649 
650 static int parse_headers(ad_http_t *http, struct evbuffer *in) {
651  char *line;
652  while ((line = evbuffer_readln(in, NULL, EVBUFFER_EOL_CRLF))) {
653  if (IS_EMPTY_STR(line)) {
654  const char *clen = http->request.headers->getstr(
655  http->request.headers, "Content-Length", false);
656  http->request.contentlength = (clen) ? atol(clen) : -1;
657  free(line);
659  }
660  // Parse
661  char *name, *value;
662  char *tmp = strstr(line, ":");
663  if (tmp) {
664  *tmp = '\0';
665  name = qstrtrim(line);
666  value = qstrtrim(tmp + 1);
667  } else {
668  name = qstrtrim(line);
669  value = "";
670  }
671  // Add
672  http->request.headers->putstr(http->request.headers, name, value);
673 
674  free(line);
675  }
676 
677  return http->request.status;
678 }
679 
680 static int parse_body(ad_http_t *http, struct evbuffer *in) {
681  // Handle static data case.
682  if (http->request.contentlength == 0) {
683  return AD_HTTP_REQ_DONE;
684  } else if (http->request.contentlength > 0) {
685  if (http->request.contentlength > http->request.bodyin) {
686  size_t maxread = http->request.contentlength - http->request.bodyin;
687  if (maxread > 0 && evbuffer_get_length(in) > 0) {
688  http->request.bodyin += http_add_inbuf(in, http, maxread);
689  }
690  }
691  if (http->request.contentlength == http->request.bodyin) {
692  return AD_HTTP_REQ_DONE;
693  }
694  } else {
695  // Check if Transfer-Encoding is chunked.
696  const char *tranenc = http->request.headers->getstr(
697  http->request.headers, "Transfer-Encoding", false);
698  if (tranenc != NULL && !strcmp(tranenc, "chunked")) {
699  // TODO: handle chunked encoding
700  for (;;) {
701  ssize_t chunksize = parse_chunked_body(http, in);
702  if (chunksize > 0) {
703  continue;
704  } else if (chunksize == 0) {
705  return AD_HTTP_REQ_DONE;
706  } else if (chunksize == -1) {
707  return http->request.status;
708  } else {
709  return AD_HTTP_ERROR;
710  }
711  }
712  } else {
713  return AD_HTTP_REQ_DONE;
714  }
715  }
716 
717  return http->request.status;
718 }
719 
720 /**
721  * Parse chunked body and append it to inbuf.
722  *
723  * @return number of bytes in a chunk. so 0 for the ending chunk. -1 for not enough data, -2 format error.
724  */
725 static ssize_t parse_chunked_body(ad_http_t *http, struct evbuffer *in) {
726  // Peek chunk size.
727  size_t crlf_len = 0;
728  char *line = evbuffer_peekln(in, &crlf_len, EVBUFFER_EOL_CRLF);
729  if (line == NULL)
730  return -1; // not enough data.
731  size_t linelen = strlen(line);
732 
733  // Parse chunk size
734  int chunksize = -1;
735  sscanf(line, "%x", &chunksize);
736  free(line);
737  if (chunksize < 0)
738  return -2; // format error
739 
740  // Check if we've received whole data of this chunk.
741  size_t datalen = linelen + crlf_len + chunksize + crlf_len;
742  size_t inbuflen = evbuffer_get_length(in);
743  if (inbuflen < datalen) {
744  return -1; // not enough data.
745  }
746 
747  // Copy chunk body
748  evbuffer_drainln(in, NULL, EVBUFFER_EOL_CRLF);
749  http_add_inbuf(in, http, chunksize);
750  evbuffer_drainln(in, NULL, EVBUFFER_EOL_CRLF);
751 
752  return chunksize;
753 }
754 
755 /**
756  * validate file path
757  */
758 static bool isValidPathname(const char *path) {
759  if (path == NULL)
760  return false;
761 
762  int len = strlen(path);
763  if (len == 0 || len >= PATH_MAX)
764  return false;
765  else if (path[0] != '/')
766  return false;
767  else if (strpbrk(path, "\\:*?\"<>|") != NULL)
768  return false;
769 
770  // check folder name length
771  int n;
772  char *t;
773  for (n = 0, t = (char *) path; *t != '\0'; t++) {
774  if (*t == '/') {
775  n = 0;
776  continue;
777  }
778  if (n >= FILENAME_MAX) {
779  DEBUG("Filename too long.");
780  return false;
781  }
782  n++;
783  }
784 
785  return true;
786 }
787 
788 /**
789  * Correct pathname.
790  *
791  * @note
792  * remove : heading & tailing white spaces, double slashes, tailing slash
793  */
794 static void correctPathname(char *path) {
795  // Take care of head & tail white spaces.
796  qstrtrim(path);
797 
798  // Take care of double slashes.
799  while (strstr(path, "//") != NULL)
800  qstrreplace("sr", path, "//", "/");
801 
802  // Take care of tailing slash.
803  int len = strlen(path);
804  if (len <= 1)
805  return;
806  if (path[len - 1] == '/')
807  path[len - 1] = '\0';
808 }
809 
810 static char *evbuffer_peekln(struct evbuffer *buffer, size_t *n_read_out,
811  enum evbuffer_eol_style eol_style) {
812  // Check if first line has arrived.
813  struct evbuffer_ptr ptr = evbuffer_search_eol(buffer, NULL, n_read_out,
814  eol_style);
815  if (ptr.pos == -1)
816  return NULL;
817 
818  char *line = (char *) malloc(ptr.pos + 1);
819  if (line == NULL)
820  return NULL;
821 
822  // Linearizes buffer
823  if (ptr.pos > 0) {
824  char *bufferptr = (char *) evbuffer_pullup(buffer, ptr.pos);
825  ASSERT(bufferptr != NULL);
826  strncpy(line, bufferptr, ptr.pos);
827  }
828  line[ptr.pos] = '\0';
829 
830  return line;
831 }
832 
833 static ssize_t evbuffer_drainln(struct evbuffer *buffer, size_t *n_read_out,
834  enum evbuffer_eol_style eol_style) {
835  char *line = evbuffer_readln(buffer, n_read_out, eol_style);
836  if (line == NULL)
837  return -1;
838 
839  size_t linelen = strlen(line);
840  free(line);
841  return linelen;
842 }
843 
844 #endif // _DOXYGEN_SKIP
struct evbuffer * outbuf
#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
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
char * ad_conn_set_method(ad_conn_t *conn, char *method)
Set method name on this connection.
Definition: ad_server.c:523
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:504
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:511
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