/http.c

http://github.com/nicolasff/webdis · C · 330 lines · 214 code · 75 blank · 41 comment · 34 complexity · 87cae5e94adf28f24042b797bd778352 MD5 · raw file

  1. #include "http.h"
  2. #include "server.h"
  3. #include "worker.h"
  4. #include "client.h"
  5. #include <string.h>
  6. #include <stdlib.h>
  7. #include <unistd.h>
  8. #include <stdio.h>
  9. /* HTTP Response */
  10. struct http_response *
  11. http_response_init(struct worker *w, int code, const char *msg) {
  12. /* create object */
  13. struct http_response *r = calloc(1, sizeof(struct http_response));
  14. r->code = code;
  15. r->msg = msg;
  16. r->w = w;
  17. r->keep_alive = 0; /* default */
  18. http_response_set_header(r, "Server", "Webdis");
  19. /* Cross-Origin Resource Sharing, CORS. */
  20. http_response_set_header(r, "Allow", "GET,POST,PUT,OPTIONS");
  21. /*
  22. Chrome doesn't support Allow and requires
  23. Access-Control-Allow-Methods
  24. */
  25. http_response_set_header(r, "Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS");
  26. http_response_set_header(r, "Access-Control-Allow-Origin", "*");
  27. /*
  28. According to
  29. http://www.w3.org/TR/cors/#access-control-allow-headers-response-header
  30. Access-Control-Allow-Headers cannot be a wildcard and must be set
  31. with explicit names
  32. */
  33. http_response_set_header(r, "Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization");
  34. return r;
  35. }
  36. void
  37. http_response_set_header(struct http_response *r, const char *k, const char *v) {
  38. int i, pos = r->header_count;
  39. size_t key_sz = strlen(k);
  40. size_t val_sz = strlen(v);
  41. for(i = 0; i < r->header_count; ++i) {
  42. if(strncmp(r->headers[i].key, k, key_sz) == 0) {
  43. pos = i;
  44. /* free old value before replacing it. */
  45. free(r->headers[i].key);
  46. free(r->headers[i].val);
  47. break;
  48. }
  49. }
  50. /* extend array */
  51. if(pos == r->header_count) {
  52. r->headers = realloc(r->headers,
  53. sizeof(struct http_header)*(r->header_count + 1));
  54. r->header_count++;
  55. }
  56. /* copy key */
  57. r->headers[pos].key = calloc(key_sz + 1, 1);
  58. memcpy(r->headers[pos].key, k, key_sz);
  59. r->headers[pos].key_sz = key_sz;
  60. /* copy val */
  61. r->headers[pos].val = calloc(val_sz + 1, 1);
  62. memcpy(r->headers[pos].val, v, val_sz);
  63. r->headers[pos].val_sz = val_sz;
  64. if(!r->chunked && !strcmp(k, "Transfer-Encoding") && !strcmp(v, "chunked")) {
  65. r->chunked = 1;
  66. }
  67. }
  68. void
  69. http_response_set_body(struct http_response *r, const char *body, size_t body_len) {
  70. r->body = body;
  71. r->body_len = body_len;
  72. }
  73. static void
  74. http_response_cleanup(struct http_response *r, int fd, int success) {
  75. int i;
  76. /* cleanup buffer */
  77. free(r->out);
  78. if(!r->keep_alive || !success) {
  79. /* Close fd is client doesn't support Keep-Alive. */
  80. close(fd);
  81. }
  82. /* cleanup response object */
  83. for(i = 0; i < r->header_count; ++i) {
  84. free(r->headers[i].key);
  85. free(r->headers[i].val);
  86. }
  87. free(r->headers);
  88. free(r);
  89. }
  90. static void
  91. http_can_write(int fd, short event, void *p) {
  92. int ret;
  93. struct http_response *r = p;
  94. (void)event;
  95. ret = write(fd, r->out + r->sent, r->out_sz - r->sent);
  96. if(ret > 0)
  97. r->sent += ret;
  98. if(ret <= 0 || r->out_sz - r->sent == 0) { /* error or done */
  99. http_response_cleanup(r, fd, (int)r->out_sz == r->sent ? 1 : 0);
  100. } else { /* reschedule write */
  101. http_schedule_write(fd, r);
  102. }
  103. }
  104. void
  105. http_schedule_write(int fd, struct http_response *r) {
  106. if(r->w) { /* async */
  107. event_set(&r->ev, fd, EV_WRITE, http_can_write, r);
  108. event_base_set(r->w->base, &r->ev);
  109. event_add(&r->ev, NULL);
  110. } else { /* blocking */
  111. http_can_write(fd, 0, r);
  112. }
  113. }
  114. static char *
  115. format_chunk(const char *p, size_t sz, size_t *out_sz) {
  116. char *out, tmp[64];
  117. int chunk_size;
  118. /* calculate format size */
  119. chunk_size = sprintf(tmp, "%x\r\n", (int)sz);
  120. *out_sz = chunk_size + sz + 2;
  121. out = malloc(*out_sz);
  122. memcpy(out, tmp, chunk_size);
  123. memcpy(out + chunk_size, p, sz);
  124. memcpy(out + chunk_size + sz, "\r\n", 2);
  125. return out;
  126. }
  127. void
  128. http_response_write(struct http_response *r, int fd) {
  129. char *p;
  130. int i, ret;
  131. /*r->keep_alive = 0;*/
  132. r->out_sz = sizeof("HTTP/1.x xxx ")-1 + strlen(r->msg) + 2;
  133. r->out = calloc(r->out_sz + 1, 1);
  134. ret = sprintf(r->out, "HTTP/1.%d %d %s\r\n", (r->http_version?1:0), r->code, r->msg);
  135. (void)ret;
  136. p = r->out;
  137. if(!r->chunked) {
  138. if(r->code == 200 && r->body) {
  139. char content_length[10];
  140. sprintf(content_length, "%zd", r->body_len);
  141. http_response_set_header(r, "Content-Length", content_length);
  142. } else {
  143. http_response_set_header(r, "Content-Length", "0");
  144. }
  145. }
  146. for(i = 0; i < r->header_count; ++i) {
  147. /* "Key: Value\r\n" */
  148. size_t header_sz = r->headers[i].key_sz + 2 + r->headers[i].val_sz + 2;
  149. r->out = realloc(r->out, r->out_sz + header_sz);
  150. p = r->out + r->out_sz;
  151. /* add key */
  152. memcpy(p, r->headers[i].key, r->headers[i].key_sz);
  153. p += r->headers[i].key_sz;
  154. /* add ": " */
  155. *(p++) = ':';
  156. *(p++) = ' ';
  157. /* add value */
  158. memcpy(p, r->headers[i].val, r->headers[i].val_sz);
  159. p += r->headers[i].val_sz;
  160. /* add "\r\n" */
  161. *(p++) = '\r';
  162. *(p++) = '\n';
  163. r->out_sz += header_sz;
  164. if(strncasecmp("Connection", r->headers[i].key, r->headers[i].key_sz) == 0 &&
  165. strncasecmp("Keep-Alive", r->headers[i].val, r->headers[i].val_sz) == 0) {
  166. r->keep_alive = 1;
  167. }
  168. }
  169. /* end of headers */
  170. r->out = realloc(r->out, r->out_sz + 2);
  171. memcpy(r->out + r->out_sz, "\r\n", 2);
  172. r->out_sz += 2;
  173. /* append body if there is one. */
  174. if(r->body && r->body_len) {
  175. char *tmp = (char*)r->body;
  176. size_t tmp_len = r->body_len;
  177. if(r->chunked) { /* replace body with formatted chunk */
  178. tmp = format_chunk(r->body, r->body_len, &tmp_len);
  179. }
  180. r->out = realloc(r->out, r->out_sz + tmp_len);
  181. memcpy(r->out + r->out_sz, tmp, tmp_len);
  182. r->out_sz += tmp_len;
  183. if(r->chunked) { /* need to free the chunk */
  184. free(tmp);
  185. }
  186. }
  187. /* send buffer to client */
  188. r->sent = 0;
  189. http_schedule_write(fd, r);
  190. }
  191. static void
  192. http_response_set_connection_header(struct http_client *c, struct http_response *r) {
  193. http_response_set_keep_alive(r, c->keep_alive);
  194. }
  195. /* Adobe flash cross-domain request */
  196. void
  197. http_crossdomain(struct http_client *c) {
  198. struct http_response *resp = http_response_init(NULL, 200, "OK");
  199. char out[] = "<?xml version=\"1.0\"?>\n"
  200. "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n"
  201. "<cross-domain-policy>\n"
  202. "<allow-access-from domain=\"*\" />\n"
  203. "</cross-domain-policy>\n";
  204. resp->http_version = c->http_version;
  205. http_response_set_connection_header(c, resp);
  206. http_response_set_header(resp, "Content-Type", "application/xml");
  207. http_response_set_body(resp, out, sizeof(out)-1);
  208. http_response_write(resp, c->fd);
  209. http_client_reset(c);
  210. }
  211. /* Simple error response */
  212. void
  213. http_send_error(struct http_client *c, short code, const char *msg) {
  214. struct http_response *resp = http_response_init(NULL, code, msg);
  215. resp->http_version = c->http_version;
  216. http_response_set_connection_header(c, resp);
  217. http_response_set_body(resp, NULL, 0);
  218. http_response_write(resp, c->fd);
  219. http_client_reset(c);
  220. }
  221. /**
  222. * Set Connection field, either Keep-Alive or Close.
  223. */
  224. void
  225. http_response_set_keep_alive(struct http_response *r, int enabled) {
  226. r->keep_alive = enabled;
  227. if(enabled) {
  228. http_response_set_header(r, "Connection", "Keep-Alive");
  229. } else {
  230. http_response_set_header(r, "Connection", "Close");
  231. }
  232. }
  233. /* Response to HTTP OPTIONS */
  234. void
  235. http_send_options(struct http_client *c) {
  236. struct http_response *resp = http_response_init(NULL, 200, "OK");
  237. resp->http_version = c->http_version;
  238. http_response_set_connection_header(c, resp);
  239. http_response_set_header(resp, "Content-Type", "text/html");
  240. http_response_set_header(resp, "Content-Length", "0");
  241. http_response_write(resp, c->fd);
  242. http_client_reset(c);
  243. }
  244. /**
  245. * Write HTTP chunk.
  246. */
  247. void
  248. http_response_write_chunk(int fd, struct worker *w, const char *p, size_t sz) {
  249. struct http_response *r = http_response_init(w, 0, NULL);
  250. r->keep_alive = 1; /* chunks are always keep-alive */
  251. /* format packet */
  252. r->out = format_chunk(p, sz, &r->out_sz);
  253. /* send async write */
  254. http_schedule_write(fd, r);
  255. }