HomePort
http-webserver_test.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011 Aalborg University. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this list of
8  * conditions and the following disclaimer.
9  *
10  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11  * of conditions and the following disclaimer in the documentation and/or other materials
12  * provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY Aalborg University ''AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Aalborg University OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  */
27 
28 #include "http-webserver.h"
29 #include "unit_test.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ev.h>
34 #include <curl/curl.h>
35 #include <pthread.h>
36 
37 struct data {
38  int state;
39  char *method;
40  char *url;
41  char *hdr_field;
43  char *hdr_value;
45  int hdr_count;
46  char *body;
47  int _errors;
48 };
49 
50 static char *req_data = "Hello world";
51 
52 static int started = 0;
53 static struct httpws *ws = NULL;
54 static struct ev_loop *loop;
55 static struct ev_async exit_watcher;
56 
57 static size_t data_from_curl(char *buffer, size_t buffer_size, size_t nmemb, char **userdata)
58 {
59  *userdata = realloc(*userdata, strlen(*userdata) + buffer_size*nmemb + 1);
60  char *data = &(*userdata)[strlen(*userdata)];
61  memcpy(data, buffer, buffer_size*nmemb);
62  data[buffer_size*nmemb] = '\0';
63 
64  return buffer_size*nmemb;
65 }
66 
67 static size_t data_to_curl(void *ptr, size_t size, size_t nmemb,
68  void *userdata)
69 {
70  size_t *sent = userdata;
71  char *data = &req_data[*sent];
72  char *buf = ptr;
73  size_t copied;
74 
75  printf("Data to CURL\n");
76 
77  // Copy data
78  strncpy(buf, data, size*nmemb);
79 
80  // Calculate how much was copied
81  if (strlen(data) < size*nmemb) {
82  copied = strlen(buf);
83  } else {
84  copied = size*nmemb;
85  }
86 
87  *sent += copied;
88 
89  return copied;
90 }
91 
92 // TODO This should take method as parameter as basic_get_test does
93 static char* simple_get_request(char* url)
94 {
95  CURL *handle = curl_easy_init();
96  CURLcode c;
97  char* userdata = malloc(sizeof(char));
98  userdata[0] = '\0';
99  size_t sent = 0;
100 
101  if(handle != NULL)
102  {
103  curl_easy_setopt(handle, CURLOPT_URL, url);
104  curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
105  curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, data_from_curl);
106  curl_easy_setopt(handle, CURLOPT_WRITEDATA, &userdata);
107  curl_easy_setopt(handle, CURLOPT_READFUNCTION, data_to_curl);
108  curl_easy_setopt(handle, CURLOPT_READDATA, &sent);
109  curl_easy_setopt(handle, CURLOPT_INFILESIZE, strlen(req_data));
110 #ifdef DEBUG
111  //curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
112 #endif
113  struct curl_slist *chunk = NULL;
114  chunk = curl_slist_append(chunk, "Transfer-Encoding:");
115  chunk = curl_slist_append(chunk,
116  "Cookie: cookie1=val1; cookie2=val2");
117  curl_easy_setopt(handle, CURLOPT_HTTPHEADER, chunk);
118 
119  if((c = curl_easy_perform(handle)) != CURLE_OK)
120  {
121  fprintf(stderr, "curl_easy_perform failed: %s\n", curl_easy_strerror(c));
122  return "";
123  }
124 
125  curl_slist_free_all(chunk);
126  curl_easy_cleanup(handle);
127  return userdata;
128  }
129  else
130  {
131  perror("Could not initialize curl: ");
132  return "";
133  }
134 }
135 
136 static int basic_get_test(enum http_method method, char *host, char* path)
137 {
138  // Construct URL
139  int len = strlen(host) + strlen(path) + 1;
140  char *url = malloc(len*sizeof(char));
141  strcpy(url, host);
142  strcat(url, path);
143 
144  // Perform request
145  char *res = simple_get_request(url);
146 
147  // Parse errors
148  int _errors = atoi(res);
149 
150  // Construct method + path
151  len = strlen(http_method_str(method)) + strlen(path) + 2;
152  char *first_line = malloc(len*sizeof(char));
153  strcpy(first_line, http_method_str(method));
154  strcat(first_line, " ");
155  strcat(first_line, path);
156 
157  // Construct content-length
158  len = snprintf("", 0, "Content-Length: %i\r\n", strlen(req_data));
159  char *content_length = malloc(len*sizeof(char));
160  snprintf(content_length, len, "Content-Length: %i\r\n",
161  strlen(req_data));
162 
163  // Check for method + path
164  if (strstr(res, first_line) == NULL) {
165  ASSERT(1);
166  printf("The following bad string was received: %s\n", res);
167  }
168 
169  // Check for content-length
170  if (strstr(res, content_length) == NULL) {
171  ASSERT(1);
172  printf("The following bad string was received: %s\n", res);
173  }
174 
175  // Check for body
176  if(strstr(res, req_data) == NULL) {
177  ASSERT(1);
178  printf("The following bad string was received: %s\n", res);
179  }
180 
181  free(url);
182  free(first_line);
183  free(content_length);
184  free(res);
185  return _errors;
186 }
187 
189 static int test_thread()
190 {
191  int testresult;
192  int ret = 0;
193 
194  // Init
195  curl_global_init(CURL_GLOBAL_ALL);
196 
197  // Run test
198  printf("Running webserver tests\n");
199  testresult = basic_get_test(HTTP_PUT, "http://localhost:8080", "/");
200 
201  // Check result
202  if (testresult) {
203  printf("Test failed\n");
204  ret++;
205  } else {
206  printf("Test succeeded\n");
207  }
208 
209  // Clean up
210  curl_global_cleanup();
211 
212  return ret;
213 }
214 
215 static char *ncat(char *s1, const char *s2, int s2_len)
216 {
217  int len = s2_len + 1;
218  if (s1 != NULL) len += strlen(s1);
219  char *str = realloc(s1, len);
220  if (s1 == NULL) str[0] = '\0';
221  strncat(str, s2, s2_len);
222  return str;
223 }
224 
225 static int on_req_begin(
226  struct httpws *ins, struct http_request *req,
227  void *ws_ctx, void **req_data)
228 {
229  struct data *data = malloc(sizeof(struct data));
230  *req_data = data;
231 
232  data->state = 1;
233  data->method = NULL;
234  data->url = NULL;
235  data->hdr_field = NULL;
236  data->hdr_field_l = 0;
237  data->hdr_value = NULL;
238  data->hdr_value_l = 0;
239  data->hdr_count = 0;
240  data->body = NULL;
241  data->_errors = 0;
242 
243  return 0;
244 }
245 
246 static int on_req_method(
247  struct httpws *ins, struct http_request *req,
248  void *ws_ctx, void **req_data,
249  const char *buf, size_t len)
250 {
251  struct data *data = *req_data;
252  int _errors = 0;
253 
254  ASSERT_EQUAL(data->state, 1);
255  data->state = (data->state | 2);
256  data->method = ncat(data->method, buf, len);
257 
258  data->_errors += _errors;
259  return 0;
260 }
261 
262 static int on_req_url(
263  struct httpws *ins, struct http_request *req,
264  void *ws_ctx, void **req_data,
265  const char *buf, size_t len)
266 {
267  struct data *data = *req_data;
268  int _errors = 0;
269 
270  ASSERT_EQUAL(data->state, 3);
271  data->state = (data->state | 4);
272  data->url = ncat(data->url, buf, len);
273 
274  data->_errors += _errors;
275  return 0;
276 }
277 
278 static int on_req_url_cmpl(
279  struct httpws *ins, struct http_request *req,
280  void *ws_ctx, void **req_data)
281 {
282  struct data *data = *req_data;
283  int _errors = 0;
284 
285  // Update state
286  ASSERT_EQUAL(data->state, 7);
287  data->state = (data->state | 8);
288 
289  // Check URL
290  const char *url = http_request_get_url(req);
291  ASSERT_STR_EQUAL(url, data->url);
292 
293  data->_errors += _errors;
294  return 0;
295 }
296 
297 static int on_req_hdr_field(
298  struct httpws *ins, struct http_request *req,
299  void *ws_ctx, void **req_data,
300  const char *buf, size_t len)
301 {
302  struct data *data = *req_data;
303  int _errors = 0;
304 
305  ASSERT_EQUAL(data->state, 15);
306  data->state = (data->state | 16);
307 
308  int i;
309  char *str;
310  data->hdr_field_l += len + 1;
311  data->hdr_field = realloc(data->hdr_field,
312  data->hdr_field_l*sizeof(char));
313  str = data->hdr_field;
314  for (i = 0; i < data->hdr_count; i++) {
315  str = &str[strlen(str)+1];
316  }
317  strncpy(str, buf, len);
318  str[len] = '\0';
319 
320  data->_errors += _errors;
321  return 0;
322 }
323 
324 static int on_req_hdr_value(
325  struct httpws *ins, struct http_request *req,
326  void *ws_ctx, void **req_data,
327  const char *buf, size_t len)
328 {
329  struct data *data = *req_data;
330  int _errors = 0;
331 
332  ASSERT_EQUAL(data->state, 31);
333  data->state = (data->state & 15);
334 
335  int i;
336  char *str;
337  data->hdr_value_l += len + 1;
338  data->hdr_value = realloc(data->hdr_value,
339  data->hdr_value_l*sizeof(char));
340  str = data->hdr_value;
341  for (i = 0; i < data->hdr_count; i++) {
342  str = &str[strlen(str)+1];
343  }
344  strncpy(str, buf, len);
345  str[len] = '\0';
346 
347  data->hdr_count++;
348 
349  data->_errors += _errors;
350  return 0;
351 }
352 
353 static int on_req_hdr_cmpl(
354  struct httpws *ins, struct http_request *req,
355  void *ws_ctx, void **req_data)
356 {
357  struct data *data = *req_data;
358  int _errors = 0;
359 
360  ASSERT_EQUAL(data->state, 15);
361  data->state = (data->state | 32);
362 
363  int i;
364  char *f = data->hdr_field;
365  char *v = data->hdr_value;
366  for (i = 0; i < data->hdr_count; i++) {
367  const char *got = http_request_get_header(req, f);
368  ASSERT_STR_EQUAL(v, got);
369 
370  f = &f[strlen(f)+1];
371  v = &v[strlen(v)+1];
372  }
373 
374  data->_errors += _errors;
375  return 0;
376 }
377 
378 static int on_req_body(
379  struct httpws *ins, struct http_request *req,
380  void *ws_ctx, void **req_data,
381  const char *buf, size_t len)
382 {
383  struct data *data = *req_data;
384  int _errors = 0;
385 
386  ASSERT_EQUAL(data->state, 47);
387  data->state = (data->state | 64);
388  data->body = ncat(data->body, buf, len);
389 
390  data->_errors += _errors;
391  return 0;
392 }
393 
394 static int construct_body(char *body, int len, struct data *data)
395 {
396  int l = 0, i;
397  char *field = data->hdr_field;
398  char *value = data->hdr_value;
399 
400  l += snprintf(body, len, "%d %s %s\r\n", data->_errors, data->method, data->url);
401  for (i = 0; i < data->hdr_count; i++) {
402  l += snprintf(&body[l], len, "%s: %s\r\n", field, value);
403  field = &field[strlen(field)+1];
404  value = &value[strlen(value)+1];
405  }
406  if (data->body != NULL)
407  l += snprintf(&body[l], len, "\r\n%s", data->body);
408 
409  return l+1;
410 }
411 
412 static int on_req_cmpl(
413  struct httpws *ins, struct http_request *req,
414  void *ws_ctx, void **req_data)
415 {
416  struct data *data = *req_data;
417  int _errors = 0;
418 
419  // Check state
420  ASSERT_EQUAL((data->state & 47), 47);
421  data->state = (data->state | 64);
422 
423  // Check method
424  enum http_method method = http_request_get_method(req);
425  if (strcmp(data->method, http_method_str(method)) != 0) {
426  fprintf(stderr, "Got '%s', Expected '%s'\n", data->method,
427  http_method_str(method));
428  data->_errors++;
429  }
430 
431  // Check cookie
432  const char *cookie;
433  cookie = http_request_get_cookie(req, "cookie1");
434  ASSERT_STR_EQUAL(cookie, "val1");
435  cookie = http_request_get_cookie(req, "cookie2");
436  ASSERT_STR_EQUAL(cookie, "val2");
437 
438  // Construct body
439  data->_errors += _errors;
440  char *body = "";
441  int body_len = construct_body(body, 0, data);
442  body = malloc(body_len*sizeof(char));
443  construct_body(body, body_len, data);
444 
445  // Send response
446  struct http_response *res = http_response_create(req, WS_HTTP_200);
447  http_response_sendf(res, body);
448  http_response_destroy(res);
449 
450  // Clean up
451  free(body);
452  free(data->method);
453  free(data->url);
454  free(data->hdr_field);
455  free(data->hdr_value);
456  free(data->body);
457  free(data);
458  return 0;
459 }
460 
462 static void exit_handler(int sig)
463 {
464  fprintf(stderr, "Sending stop signal\n");
465  ev_async_send(loop, &exit_watcher);
466 }
467 
469 static void exit_cb(EV_P_ ev_async *watcher, int revents)
470 {
471  fprintf(stderr, "Stopping webserver\n");
472  // Stop webserver
473  if (ws != NULL) {
474  httpws_stop(ws);
475  httpws_destroy(ws);
476  }
477 
478  ev_break(loop, EVBREAK_ALL);
479 }
480 
482 static void *webserver_thread(void *arg)
483 {
484  // The event loop for the webserver to run on
485  loop = EV_DEFAULT;
486 
487  // Add a watcher to stop it again
488  ev_async_init(&exit_watcher, exit_cb);
489  ev_async_start(loop, &exit_watcher);
490 
491  // Settings for the webserver
492  struct httpws_settings settings = HTTPWS_SETTINGS_DEFAULT;
493  settings.port = HPD_TCPD_P_HTTP_ALT;
494  settings.on_req_begin = on_req_begin;
495  settings.on_req_method = on_req_method;
496  settings.on_req_url = on_req_url;
497  settings.on_req_url_cmpl = on_req_url_cmpl;
498  settings.on_req_hdr_field = on_req_hdr_field;
499  settings.on_req_hdr_value = on_req_hdr_value;
500  settings.on_req_hdr_cmpl = on_req_hdr_cmpl;
501  settings.on_req_body = on_req_body;
502  settings.on_req_cmpl = on_req_cmpl;
503 
504  // Inform if we have been built with debug flag
505 #ifdef DEBUG
506  printf("Debugging is set\n");
507 #endif
508 
509  // Register signals for correctly exiting
510  signal(SIGINT, exit_handler);
511  signal(SIGTERM, exit_handler);
512 
513  // Create webserver
514  ws = httpws_create(&settings, loop);
515  httpws_start(ws);
516 
517  // Start the event loop and webserver
518  started = 1;
519  ev_run(loop, 0);
520 
521  return NULL;
522 }
523 
525 int main(int argc, char *argv[])
526 {
527  int stat;
528  pthread_t server_thread;
529 
530  // Start webserver and wait for it to start
531  pthread_create(&server_thread, NULL, webserver_thread, NULL);
532  // TODO Someone needs too add a timeout here...
533  while (!started);
534 
535  // Run test
536  stat = test_thread();
537 
538  // Stop webserver properly
539  exit_handler(0);
540  pthread_join(server_thread, NULL);
541 
542  // Return result of test
543  return stat;
544 }
545 
static void * webserver_thread(void *arg)
Webserver thread.
char * url
static void exit_cb(EV_P_ ev_async *watcher, int revents)
Exit callback for async watcher (Webserver)
char * hdr_value
static int test_thread()
Test thread.
data field
data value
static int on_req_body(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data, const char *buf, size_t len)
static int on_req_hdr_cmpl(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data)
data path
char * url
static int construct_body(char *body, int len, struct data *data)
free(data.url)
static char * ncat(char *s1, const char *s2, int s2_len)
ASSERT_STR_EQUAL(data.url, url)
static int on_req_hdr_field(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data, const char *buf, size_t len)
static int on_req_method(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data, const char *buf, size_t len)
char * method
static int basic_get_test(enum http_method method, char *host, char *path)
static int on_req_cmpl(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data)
static int on_req_hdr_value(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data, const char *buf, size_t len)
char * hdr_field
static struct httpws * ws
static int on_req_url(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data, const char *buf, size_t len)
static void exit_handler(int sig)
Handle correct exiting.
static struct ev_async exit_watcher
int main(int argc, char *argv[])
Main function.
struct data data
data host
static struct ev_loop * loop
static int on_req_begin(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data)
ASSERT_EQUAL(data.count, 2)
static size_t data_to_curl(void *ptr, size_t size, size_t nmemb, void *userdata)
char * body
static int on_req_url_cmpl(struct httpws *ins, struct http_request *req, void *ws_ctx, void **req_data)
static size_t data_from_curl(char *buffer, size_t buffer_size, size_t nmemb, char **userdata)
static int started
static char * simple_get_request(char *url)
static char * req_data