1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2013 Christian Grothoff 4 5 libmicrohttpd is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libmicrohttpd is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libmicrohttpd; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 Boston, MA 02111-1307, USA. 19 */ 20 21 /** 22 * @file test_process_arguments.c 23 * @brief Testcase for HTTP URI arguments 24 * @author Christian Grothoff 25 */ 26 27 #include "MHD_config.h" 28 #include "platform.h" 29 #include <curl/curl.h> 30 #include <microhttpd.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <time.h> 34 35 #ifndef WINDOWS 36 #include <unistd.h> 37 #endif 38 39 static int oneone; 40 41 struct CBC 42 { 43 char *buf; 44 size_t pos; 45 size_t size; 46 }; 47 48 49 static size_t 50 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 51 { 52 struct CBC *cbc = ctx; 53 54 if (cbc->pos + size * nmemb > cbc->size) 55 return 0; /* overflow */ 56 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 57 cbc->pos += size * nmemb; 58 return size * nmemb; 59 } 60 61 62 static int 63 ahc_echo (void *cls, 64 struct MHD_Connection *connection, 65 const char *url, 66 const char *method, 67 const char *version, 68 const char *upload_data, size_t *upload_data_size, 69 void **unused) 70 { 71 static int ptr; 72 const char *me = cls; 73 struct MHD_Response *response; 74 int ret; 75 const char *hdr; 76 77 if (0 != strcmp (me, method)) 78 return MHD_NO; /* unexpected method */ 79 if (&ptr != *unused) 80 { 81 *unused = &ptr; 82 return MHD_YES; 83 } 84 *unused = NULL; 85 hdr = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "k"); 86 if ((hdr == NULL) || (0 != strcmp (hdr, "v x"))) 87 abort (); 88 hdr = MHD_lookup_connection_value (connection, 89 MHD_GET_ARGUMENT_KIND, "hash"); 90 if ((hdr == NULL) || (0 != strcmp (hdr, "#foo"))) 91 abort (); 92 hdr = MHD_lookup_connection_value (connection, 93 MHD_GET_ARGUMENT_KIND, "space"); 94 if ((hdr == NULL) || (0 != strcmp (hdr, "\240bar"))) 95 abort (); 96 if (3 != MHD_get_connection_values (connection, 97 MHD_GET_ARGUMENT_KIND, 98 NULL, NULL)) 99 abort (); 100 response = MHD_create_response_from_buffer (strlen (url), 101 (void *) url, 102 MHD_RESPMEM_MUST_COPY); 103 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 104 MHD_destroy_response (response); 105 if (ret == MHD_NO) 106 abort (); 107 return ret; 108 } 109 110 111 static int 112 testExternalGet () 113 { 114 struct MHD_Daemon *d; 115 CURL *c; 116 char buf[2048]; 117 struct CBC cbc; 118 CURLM *multi; 119 CURLMcode mret; 120 fd_set rs; 121 fd_set ws; 122 fd_set es; 123 MHD_socket max; 124 int running; 125 struct CURLMsg *msg; 126 time_t start; 127 struct timeval tv; 128 129 multi = NULL; 130 cbc.buf = buf; 131 cbc.size = 2048; 132 cbc.pos = 0; 133 d = MHD_start_daemon (MHD_USE_DEBUG, 134 21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 135 if (d == NULL) 136 return 256; 137 c = curl_easy_init (); 138 curl_easy_setopt (c, CURLOPT_URL, 139 "http://127.0.0.1:21080/hello+world?k=v+x&hash=%23foo&space=%A0bar"); 140 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 141 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 142 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 143 if (oneone) 144 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 145 else 146 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 147 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 148 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 149 /* NOTE: use of CONNECTTIMEOUT without also 150 setting NOSIGNAL results in really weird 151 crashes on my system! */ 152 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 153 154 155 multi = curl_multi_init (); 156 if (multi == NULL) 157 { 158 curl_easy_cleanup (c); 159 MHD_stop_daemon (d); 160 return 512; 161 } 162 mret = curl_multi_add_handle (multi, c); 163 if (mret != CURLM_OK) 164 { 165 curl_multi_cleanup (multi); 166 curl_easy_cleanup (c); 167 MHD_stop_daemon (d); 168 return 1024; 169 } 170 start = time (NULL); 171 while ((time (NULL) - start < 5) && (multi != NULL)) 172 { 173 max = 0; 174 FD_ZERO (&rs); 175 FD_ZERO (&ws); 176 FD_ZERO (&es); 177 curl_multi_perform (multi, &running); 178 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); 179 if (mret != CURLM_OK) 180 { 181 curl_multi_remove_handle (multi, c); 182 curl_multi_cleanup (multi); 183 curl_easy_cleanup (c); 184 MHD_stop_daemon (d); 185 return 2048; 186 } 187 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 188 { 189 curl_multi_remove_handle (multi, c); 190 curl_multi_cleanup (multi); 191 curl_easy_cleanup (c); 192 MHD_stop_daemon (d); 193 return 4096; 194 } 195 tv.tv_sec = 0; 196 tv.tv_usec = 1000; 197 select (max + 1, &rs, &ws, &es, &tv); 198 curl_multi_perform (multi, &running); 199 if (running == 0) 200 { 201 msg = curl_multi_info_read (multi, &running); 202 if (msg == NULL) 203 break; 204 if (msg->msg == CURLMSG_DONE) 205 { 206 if (msg->data.result != CURLE_OK) 207 printf ("%s failed at %s:%d: `%s'\n", 208 "curl_multi_perform", 209 __FILE__, 210 __LINE__, curl_easy_strerror (msg->data.result)); 211 curl_multi_remove_handle (multi, c); 212 curl_multi_cleanup (multi); 213 curl_easy_cleanup (c); 214 c = NULL; 215 multi = NULL; 216 } 217 } 218 MHD_run (d); 219 } 220 if (multi != NULL) 221 { 222 curl_multi_remove_handle (multi, c); 223 curl_easy_cleanup (c); 224 curl_multi_cleanup (multi); 225 } 226 MHD_stop_daemon (d); 227 if (cbc.pos != strlen ("/hello+world")) 228 return 8192; 229 if (0 != strncmp ("/hello+world", cbc.buf, strlen ("/hello+world"))) 230 return 16384; 231 return 0; 232 } 233 234 235 int 236 main (int argc, char *const *argv) 237 { 238 unsigned int errorCount = 0; 239 240 oneone = (NULL != strrchr (argv[0], (int) '/')) ? 241 (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; 242 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 243 return 2; 244 errorCount += testExternalGet (); 245 if (errorCount != 0) 246 fprintf (stderr, "Error (code: %u)\n", errorCount); 247 curl_global_cleanup (); 248 return errorCount != 0; /* 0 == pass */ 249 } 250