/worker.c

http://github.com/nicolasff/webdis · C · 237 lines · 164 code · 46 blank · 27 comment · 32 complexity · ef10ba6f022dddf4543f15f36b59ec48 MD5 · raw file

  1. #include "worker.h"
  2. #include "client.h"
  3. #include "http.h"
  4. #include "cmd.h"
  5. #include "pool.h"
  6. #include "slog.h"
  7. #include "websocket.h"
  8. #include "conf.h"
  9. #include "server.h"
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <unistd.h>
  13. #include <event.h>
  14. #include <string.h>
  15. struct worker *
  16. worker_new(struct server *s) {
  17. int ret;
  18. struct worker *w = calloc(1, sizeof(struct worker));
  19. w->s = s;
  20. /* setup communication link */
  21. ret = pipe(w->link);
  22. (void)ret;
  23. /* Redis connection pool */
  24. w->pool = pool_new(w, s->cfg->pool_size_per_thread);
  25. return w;
  26. }
  27. void
  28. worker_can_read(int fd, short event, void *p) {
  29. struct http_client *c = p;
  30. int ret, nparsed;
  31. (void)fd;
  32. (void)event;
  33. ret = http_client_read(c);
  34. if(ret <= 0) {
  35. if((client_error_t)ret == CLIENT_DISCONNECTED) {
  36. return;
  37. } else if (c->failed_alloc || (client_error_t)ret == CLIENT_OOM) {
  38. slog(c->w->s, WEBDIS_DEBUG, "503", 3);
  39. http_send_error(c, 503, "Service Unavailable");
  40. return;
  41. }
  42. }
  43. if(c->is_websocket) {
  44. /* Got websocket data */
  45. ws_add_data(c);
  46. } else {
  47. /* run parser */
  48. nparsed = http_client_execute(c);
  49. if(c->failed_alloc) {
  50. slog(c->w->s, WEBDIS_DEBUG, "503", 3);
  51. http_send_error(c, 503, "Service Unavailable");
  52. } else if (c->parser.flags & F_CONNECTION_CLOSE) {
  53. c->broken = 1;
  54. } else if(c->is_websocket) {
  55. /* we need to use the remaining (unparsed) data as the body. */
  56. if(nparsed < ret) {
  57. http_client_add_to_body(c, c->buffer + nparsed + 1, c->sz - nparsed - 1);
  58. ws_handshake_reply(c);
  59. } else {
  60. c->broken = 1;
  61. }
  62. free(c->buffer);
  63. c->buffer = NULL;
  64. c->sz = 0;
  65. } else if(nparsed != ret) {
  66. slog(c->w->s, WEBDIS_DEBUG, "400", 3);
  67. http_send_error(c, 400, "Bad Request");
  68. } else if(c->request_sz > c->s->cfg->http_max_request_size) {
  69. slog(c->w->s, WEBDIS_DEBUG, "413", 3);
  70. http_send_error(c, 413, "Request Entity Too Large");
  71. }
  72. }
  73. if(c->broken) { /* terminate client */
  74. http_client_free(c);
  75. } else {
  76. /* start monitoring input again */
  77. worker_monitor_input(c);
  78. }
  79. }
  80. /**
  81. * Monitor client FD for possible reads.
  82. */
  83. void
  84. worker_monitor_input(struct http_client *c) {
  85. event_set(&c->ev, c->fd, EV_READ, worker_can_read, c);
  86. event_base_set(c->w->base, &c->ev);
  87. event_add(&c->ev, NULL);
  88. }
  89. /**
  90. * Called when a client is sent to this worker.
  91. */
  92. static void
  93. worker_on_new_client(int pipefd, short event, void *ptr) {
  94. struct http_client *c;
  95. unsigned long addr;
  96. (void)event;
  97. (void)ptr;
  98. /* Get client from messaging pipe */
  99. int ret = read(pipefd, &addr, sizeof(addr));
  100. if(ret == sizeof(addr)) {
  101. c = (struct http_client*)addr;
  102. /* monitor client for input */
  103. worker_monitor_input(c);
  104. }
  105. }
  106. static void
  107. worker_pool_connect(struct worker *w) {
  108. int i;
  109. /* create connections */
  110. for(i = 0; i < w->pool->count; ++i) {
  111. pool_connect(w->pool, w->s->cfg->database, 1);
  112. }
  113. }
  114. static void*
  115. worker_main(void *p) {
  116. struct worker *w = p;
  117. struct event ev;
  118. /* setup libevent */
  119. w->base = event_base_new();
  120. /* monitor pipe link */
  121. event_set(&ev, w->link[0], EV_READ | EV_PERSIST, worker_on_new_client, w);
  122. event_base_set(w->base, &ev);
  123. event_add(&ev, NULL);
  124. /* connect to Redis */
  125. worker_pool_connect(w);
  126. /* loop */
  127. event_base_dispatch(w->base);
  128. return NULL;
  129. }
  130. void
  131. worker_start(struct worker *w) {
  132. pthread_create(&w->thread, NULL, worker_main, w);
  133. }
  134. /**
  135. * Queue new client to process
  136. */
  137. void
  138. worker_add_client(struct worker *w, struct http_client *c) {
  139. /* write into pipe link */
  140. unsigned long addr = (unsigned long)c;
  141. int ret = write(w->link[1], &addr, sizeof(addr));
  142. (void)ret;
  143. }
  144. /**
  145. * Called when a client has finished reading input and can create a cmd
  146. */
  147. void
  148. worker_process_client(struct http_client *c) {
  149. /* check that the command can be executed */
  150. struct worker *w = c->w;
  151. cmd_response_t ret = CMD_PARAM_ERROR;
  152. switch(c->parser.method) {
  153. case HTTP_GET:
  154. if(c->path_sz == 16 && memcmp(c->path, "/crossdomain.xml", 16) == 0) {
  155. http_crossdomain(c);
  156. return;
  157. }
  158. slog(w->s, WEBDIS_DEBUG, c->path, c->path_sz);
  159. ret = cmd_run(c->w, c, 1+c->path, c->path_sz-1, NULL, 0);
  160. break;
  161. case HTTP_POST:
  162. slog(w->s, WEBDIS_DEBUG, c->path, c->path_sz);
  163. ret = cmd_run(c->w, c, c->body, c->body_sz, NULL, 0);
  164. break;
  165. case HTTP_PUT:
  166. slog(w->s, WEBDIS_DEBUG, c->path, c->path_sz);
  167. ret = cmd_run(c->w, c, 1+c->path, c->path_sz-1,
  168. c->body, c->body_sz);
  169. break;
  170. case HTTP_OPTIONS:
  171. http_send_options(c);
  172. return;
  173. default:
  174. slog(w->s, WEBDIS_DEBUG, "405", 3);
  175. http_send_error(c, 405, "Method Not Allowed");
  176. return;
  177. }
  178. switch(ret) {
  179. case CMD_ACL_FAIL:
  180. case CMD_PARAM_ERROR:
  181. slog(w->s, WEBDIS_DEBUG, "403", 3);
  182. http_send_error(c, 403, "Forbidden");
  183. break;
  184. case CMD_REDIS_UNAVAIL:
  185. slog(w->s, WEBDIS_DEBUG, "503", 3);
  186. http_send_error(c, 503, "Service Unavailable");
  187. break;
  188. default:
  189. break;
  190. }
  191. }