You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

388 lines
10 KiB

  1. /*
  2. * weighttp - a lightweight and simple webserver benchmarking tool
  3. *
  4. * Author:
  5. * Copyright (c) 2009 Thomas Porzelt
  6. *
  7. * License:
  8. * MIT, see COPYING file
  9. */
  10. #include "weighttp.h"
  11. static uint8_t client_parse(Client *client);
  12. static void client_io_cb(struct ev_loop *loop, ev_io *w, int revents);
  13. static void client_set_events(Client *client, int events);
  14. /*
  15. static void client_add_events(Client *client, int events);
  16. static void client_rem_events(Client *client, int events);
  17. static void client_add_events(Client *client, int events) {
  18. struct ev_loop *loop = client->worker->loop;
  19. ev_io *watcher = &client->sock_watcher;
  20. if ((watcher->events & events) == events)
  21. return;
  22. ev_io_stop(loop, watcher);
  23. ev_io_set(watcher, watcher->fd, watcher->events | events);
  24. ev_io_start(loop, watcher);
  25. }
  26. static void client_rem_events(Client *client, int events) {
  27. struct ev_loop *loop = client->worker->loop;
  28. ev_io *watcher = &client->sock_watcher;
  29. if (0 == (watcher->events & events))
  30. return;
  31. ev_io_stop(loop, watcher);
  32. ev_io_set(watcher, watcher->fd, watcher->events & ~events);
  33. ev_io_start(loop, watcher);
  34. }
  35. */
  36. static void client_set_events(Client *client, int events) {
  37. struct ev_loop *loop = client->worker->loop;
  38. ev_io *watcher = &client->sock_watcher;
  39. if (events == (watcher->events & (EV_READ | EV_WRITE)))
  40. return;
  41. ev_io_stop(loop, watcher);
  42. ev_io_set(watcher, watcher->fd, (watcher->events & ~(EV_READ | EV_WRITE)) | events);
  43. ev_io_start(loop, watcher);
  44. }
  45. Client *client_new(Worker *worker) {
  46. Client *client;
  47. client = W_MALLOC(Client, 1);
  48. client->state = CLIENT_START;
  49. client->worker = worker;
  50. client->sock_watcher.fd = -1;
  51. client->sock_watcher.data = client;
  52. client->content_length = -1;
  53. client->buffer_offset = 0;
  54. client->request_offset = 0;
  55. client->keepalive = client->worker->config->keep_alive;
  56. return client;
  57. }
  58. void client_free(Client *client) {
  59. if (client->sock_watcher.fd != -1) {
  60. ev_io_stop(client->worker->loop, &client->sock_watcher);
  61. shutdown(client->sock_watcher.fd, SHUT_WR);
  62. close(client->sock_watcher.fd);
  63. }
  64. free(client);
  65. }
  66. static void client_reset(Client *client) {
  67. //printf("keep alive: %d\n", client->keepalive);
  68. if (!client->keepalive) {
  69. ev_io_stop(client->worker->loop, &client->sock_watcher);
  70. if (client->sock_watcher.fd != -1) {
  71. shutdown(client->sock_watcher.fd, SHUT_WR);
  72. close(client->sock_watcher.fd);
  73. }
  74. client->sock_watcher.fd = -1;
  75. client->state = CLIENT_START;
  76. } else {
  77. client_set_events(client, EV_WRITE);
  78. client->worker->stats.req_started++;
  79. client->state = CLIENT_WRITING;
  80. }
  81. client->parser_state = PARSER_START;
  82. client->buffer_offset = 0;
  83. client->parser_offset = 0;
  84. client->request_offset = 0;
  85. client->ts_start = 0;
  86. client->ts_end = 0;
  87. client->status_200 = 0;
  88. client->success = 0;
  89. client->content_length = -1;
  90. client->bytes_received = 0;
  91. client->header_size = 0;
  92. client->keepalive = client->worker->config->keep_alive;
  93. }
  94. static uint8_t client_connect(Client *client) {
  95. //printf("connecting...\n");
  96. start:
  97. if (-1 == connect(client->sock_watcher.fd, client->worker->config->saddr->ai_addr, client->worker->config->saddr->ai_addrlen)) {
  98. switch (errno) {
  99. case EINPROGRESS:
  100. case EALREADY:
  101. /* async connect now in progress */
  102. client->state = CLIENT_CONNECTING;
  103. return 1;
  104. case EISCONN:
  105. break;
  106. case EINTR:
  107. goto start;
  108. default:
  109. {
  110. strerror_r(errno, client->buffer, sizeof(client->buffer));
  111. W_ERROR("connect() failed: %s (%d)", client->buffer, errno);
  112. return 0;
  113. }
  114. }
  115. }
  116. /* successfully connected */
  117. client->state = CLIENT_WRITING;
  118. return 1;
  119. }
  120. static void client_io_cb(struct ev_loop *loop, ev_io *w, int revents) {
  121. Client *client = w->data;
  122. UNUSED(loop);
  123. UNUSED(revents);
  124. client_state_machine(client);
  125. }
  126. void client_state_machine(Client *client) {
  127. int r;
  128. Config *config = client->worker->config;
  129. start:
  130. //printf("state: %d\n", client->state);
  131. switch (client->state) {
  132. case CLIENT_START:
  133. do {
  134. r = socket(config->saddr->ai_family, config->saddr->ai_socktype, config->saddr->ai_protocol);
  135. } while (-1 == r && errno == EINTR);
  136. if (-1 == r) {
  137. client->state = CLIENT_ERROR;
  138. goto start;
  139. }
  140. /* set non-blocking */
  141. fcntl(r, F_SETFL, O_NONBLOCK | O_RDWR);
  142. ev_init(&client->sock_watcher, client_io_cb);
  143. ev_io_set(&client->sock_watcher, r, EV_WRITE);
  144. ev_io_start(client->worker->loop, &client->sock_watcher);
  145. client->worker->stats.req_started++;
  146. if (!client_connect(client)) {
  147. client->state = CLIENT_ERROR;
  148. goto start;
  149. } else {
  150. client_set_events(client, EV_WRITE);
  151. return;
  152. }
  153. case CLIENT_CONNECTING:
  154. if (!client_connect(client)) {
  155. client->state = CLIENT_ERROR;
  156. goto start;
  157. }
  158. case CLIENT_WRITING:
  159. while (1) {
  160. r = write(client->sock_watcher.fd, &config->request[client->request_offset], config->request_size - client->request_offset);
  161. //printf("write(%d - %d = %d): %d\n", config->request_size, client->request_offset, config->request_size - client->request_offset, r);
  162. if (r == -1) {
  163. /* error */
  164. if (errno == EINTR)
  165. continue;
  166. strerror_r(errno, client->buffer, sizeof(client->buffer));
  167. W_ERROR("write() failed: %s (%d)", client->buffer, errno);
  168. client->state = CLIENT_ERROR;
  169. goto start;
  170. } else if (r != 0) {
  171. /* success */
  172. client->request_offset += r;
  173. if (client->request_offset == config->request_size) {
  174. /* whole request was sent, start reading */
  175. client->state = CLIENT_READING;
  176. client_set_events(client, EV_READ);
  177. }
  178. return;
  179. } else {
  180. /* disconnect */
  181. client->state = CLIENT_END;
  182. goto start;
  183. }
  184. }
  185. case CLIENT_READING:
  186. while (1) {
  187. r = read(client->sock_watcher.fd, &client->buffer[client->buffer_offset], sizeof(client->buffer) - client->buffer_offset);
  188. //printf("read(): %d\n", r);
  189. if (r == -1) {
  190. /* error */
  191. if (errno == EINTR)
  192. continue;
  193. strerror_r(errno, client->buffer, sizeof(client->buffer));
  194. W_ERROR("read() failed: %s (%d)", client->buffer, errno);
  195. client->state = CLIENT_ERROR;
  196. } else if (r != 0) {
  197. /* success */
  198. client->bytes_received += r;
  199. client->buffer_offset += r;
  200. client->worker->stats.bytes_total += r;
  201. if (client->buffer_offset >= sizeof(client->buffer)) {
  202. /* too big response header */
  203. client->state = CLIENT_ERROR;
  204. break;
  205. }
  206. client->buffer[client->buffer_offset] = '\0';
  207. //printf("buffer:\n==========\n%s\n==========\n", client->buffer);
  208. if (!client_parse(client)) {
  209. client->state = CLIENT_ERROR;
  210. //printf("parser failed\n");
  211. break;
  212. } else {
  213. if (client->state == CLIENT_END)
  214. goto start;
  215. else
  216. break;
  217. }
  218. } else {
  219. /* disconnect */
  220. client->state = CLIENT_ERROR;
  221. break;
  222. }
  223. }
  224. break;
  225. case CLIENT_ERROR:
  226. //printf("client error\n");
  227. client->worker->stats.req_error++;
  228. client->keepalive = 0;
  229. client->success = 0;
  230. client->state = CLIENT_END;
  231. case CLIENT_END:
  232. /* update worker stats */
  233. client->worker->stats.req_done++;
  234. if (client->success) {
  235. client->worker->stats.req_success++;
  236. client->worker->stats.bytes_body += client->bytes_received - client->header_size;
  237. } else {
  238. client->worker->stats.req_failed++;
  239. }
  240. if (client->worker->stats.req_started == client->worker->stats.req_todo) {
  241. /* this worker has started all requests */
  242. ev_io_stop(client->worker->loop, &client->sock_watcher);
  243. if (client->worker->stats.req_done == client->worker->stats.req_todo) {
  244. /* this worker has finished all requests */
  245. ev_unref(client->worker->loop);
  246. }
  247. } else {
  248. client_reset(client);
  249. goto start;
  250. }
  251. }
  252. }
  253. static uint8_t client_parse(Client *client) {
  254. char *end, *str;
  255. switch (client->parser_state) {
  256. case PARSER_START:
  257. //printf("parse (START):\n%s\n", &client->buffer[client->parser_offset]);
  258. /* look for HTTP/1.1 200 OK */
  259. if (client->buffer_offset < sizeof("HTTP/1.1 200 OK\r\n"))
  260. return 1;
  261. if (strncmp(client->buffer, "HTTP/1.1 200 OK\r\n", sizeof("HTTP/1.1 200 OK\r\n")-1) == 0) {
  262. client->status_200 = 1;
  263. client->parser_offset = sizeof("HTTP/1.1 200 ok\r\n") - 1;
  264. } else {
  265. client->status_200 = 0;
  266. end = strchr(client->buffer, '\r');
  267. if (!end || *(end+1) != '\n')
  268. return 0;
  269. client->parser_offset = end + 2 - client->buffer;
  270. }
  271. client->parser_state = PARSER_HEADER;
  272. case PARSER_HEADER:
  273. //printf("parse (HEADER)\n");
  274. /* look for Content-Length and Connection header */
  275. while (NULL != (end = strchr(&client->buffer[client->parser_offset], '\r'))) {
  276. if (*(end+1) != '\n')
  277. return 0;
  278. if (end == &client->buffer[client->parser_offset]) {
  279. /* body reached */
  280. client->parser_state = PARSER_BODY;
  281. client->header_size = end + 2 - client->buffer;
  282. //printf("body reached\n");
  283. return client_parse(client);
  284. }
  285. *end = '\0';
  286. str = &client->buffer[client->parser_offset];
  287. //printf("checking header: '%s'\n", str);
  288. if (strncmp(str, "Content-Length: ", sizeof("Content-Length: ")-1) == 0) {
  289. /* content length header */
  290. client->content_length = atoi(str + sizeof("Content-Length: ") - 1);
  291. } else if (strncmp(str, "Connection: ", sizeof("Connection: ")-1) == 0) {
  292. /* connection header */
  293. str += sizeof("Connection: ") - 1;
  294. if (strncmp(str, "close", sizeof("close")-1) == 0)
  295. client->keepalive = 0;
  296. else if (strncmp(str, "Keep-Alive", sizeof("Keep-Alive")-1) == 0)
  297. client->keepalive = client->worker->config->keep_alive;
  298. else if (strncmp(str, "keep-alive", sizeof("keep-alive")-1) == 0)
  299. client->keepalive = client->worker->config->keep_alive;
  300. else
  301. return 0;
  302. }
  303. if (*(end+2) == '\r' && *(end+3) == '\n') {
  304. /* body reached */
  305. client->parser_state = PARSER_BODY;
  306. client->header_size = end + 4 - client->buffer;
  307. //printf("body reached\n");
  308. return client_parse(client);
  309. }
  310. client->parser_offset = end - client->buffer + 2;
  311. }
  312. return 1;
  313. case PARSER_BODY:
  314. //printf("parse (BODY)\n");
  315. /* do nothing, just consume the data */
  316. /*printf("content-l: %"PRIu64", header: %d, recevied: %"PRIu64"\n",
  317. client->content_length, client->header_size, client->bytes_received);
  318. client->buffer_offset = 0;*/
  319. if (client->content_length == -1)
  320. return 0;
  321. if (client->bytes_received == (uint64_t) (client->header_size + client->content_length)) {
  322. /* full response received */
  323. client->state = CLIENT_END;
  324. client->success = client->status_200 ? 1 : 0;
  325. }
  326. return 1;
  327. }
  328. return 1;
  329. }