test_process_arguments.c (8907B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2013 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 3, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * @file test_process_arguments.c 24 * @brief Testcase for HTTP URI arguments 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "MHD_config.h" 30 #include "platform.h" 31 #include <curl/curl.h> 32 #include <microhttpd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <time.h> 36 #include <errno.h> 37 #include "mhd_has_in_name.h" 38 39 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 40 test into a marked, classifiable test error (TESTING.md, P5). */ 41 #include "mhd_panic_tripwire.h" 42 43 #ifndef WINDOWS 44 #include <unistd.h> 45 #endif 46 47 static int oneone; 48 49 struct CBC 50 { 51 char *buf; 52 size_t pos; 53 size_t size; 54 }; 55 56 57 static size_t 58 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 59 { 60 struct CBC *cbc = ctx; 61 62 if (cbc->pos + size * nmemb > cbc->size) 63 return 0; /* overflow */ 64 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 65 cbc->pos += size * nmemb; 66 return size * nmemb; 67 } 68 69 70 static enum MHD_Result 71 ahc_echo (void *cls, 72 struct MHD_Connection *connection, 73 const char *url, 74 const char *method, 75 const char *version, 76 const char *upload_data, size_t *upload_data_size, 77 void **req_cls) 78 { 79 static int ptr; 80 struct MHD_Response *response; 81 enum MHD_Result ret; 82 const char *hdr; 83 (void) cls; 84 (void) version; (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 85 86 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 87 return MHD_NO; /* unexpected method */ 88 if (&ptr != *req_cls) 89 { 90 *req_cls = &ptr; 91 return MHD_YES; 92 } 93 *req_cls = NULL; 94 hdr = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "k"); 95 if ((hdr == NULL) || (0 != strcmp (hdr, "v x"))) 96 abort (); 97 hdr = MHD_lookup_connection_value (connection, 98 MHD_GET_ARGUMENT_KIND, "hash"); 99 if ((hdr == NULL) || (0 != strcmp (hdr, "#foo"))) 100 abort (); 101 hdr = MHD_lookup_connection_value (connection, 102 MHD_GET_ARGUMENT_KIND, "space"); 103 if ((hdr == NULL) || (0 != strcmp (hdr, "\240bar"))) 104 abort (); 105 if (3 != MHD_get_connection_values (connection, 106 MHD_GET_ARGUMENT_KIND, 107 NULL, NULL)) 108 abort (); 109 response = MHD_create_response_from_buffer_copy (strlen (url), 110 (const void *) url); 111 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 112 MHD_destroy_response (response); 113 if (ret == MHD_NO) 114 abort (); 115 return ret; 116 } 117 118 119 static unsigned int 120 testExternalGet (void) 121 { 122 struct MHD_Daemon *d; 123 CURL *c; 124 char buf[2048]; 125 struct CBC cbc; 126 CURLM *multi; 127 CURLMcode mret; 128 fd_set rs; 129 fd_set ws; 130 fd_set es; 131 MHD_socket maxsock; 132 #ifdef MHD_WINSOCK_SOCKETS 133 int maxposixs; /* Max socket number unused on W32 */ 134 #else /* MHD_POSIX_SOCKETS */ 135 #define maxposixs maxsock 136 #endif /* MHD_POSIX_SOCKETS */ 137 int running; 138 struct CURLMsg *msg; 139 time_t start; 140 struct timeval tv; 141 uint16_t port; 142 143 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 144 port = 0; 145 else 146 { 147 port = 1410; 148 if (oneone) 149 port += 5; 150 } 151 152 multi = NULL; 153 cbc.buf = buf; 154 cbc.size = 2048; 155 cbc.pos = 0; 156 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 157 port, NULL, NULL, &ahc_echo, NULL, 158 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 159 MHD_OPTION_END); 160 if (d == NULL) 161 return 256; 162 if (0 == port) 163 { 164 const union MHD_DaemonInfo *dinfo; 165 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 166 if ((NULL == dinfo) || (0 == dinfo->port) ) 167 { 168 MHD_stop_daemon (d); return 32; 169 } 170 port = dinfo->port; 171 } 172 c = curl_easy_init (); 173 curl_easy_setopt (c, CURLOPT_URL, 174 "http://127.0.0.1/hello+world?k=v+x&hash=%23foo&space=%A0bar"); 175 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 176 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 177 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 178 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 179 if (oneone) 180 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 181 else 182 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 183 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 184 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 185 /* NOTE: use of CONNECTTIMEOUT without also 186 setting NOSIGNAL results in really weird 187 crashes on my system! */ 188 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 189 190 191 multi = curl_multi_init (); 192 if (multi == NULL) 193 { 194 curl_easy_cleanup (c); 195 MHD_stop_daemon (d); 196 return 512; 197 } 198 mret = curl_multi_add_handle (multi, c); 199 if (mret != CURLM_OK) 200 { 201 curl_multi_cleanup (multi); 202 curl_easy_cleanup (c); 203 MHD_stop_daemon (d); 204 return 1024; 205 } 206 start = time (NULL); 207 while ((time (NULL) - start < 5) && (multi != NULL)) 208 { 209 maxsock = MHD_INVALID_SOCKET; 210 maxposixs = -1; 211 FD_ZERO (&rs); 212 FD_ZERO (&ws); 213 FD_ZERO (&es); 214 curl_multi_perform (multi, &running); 215 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 216 if (mret != CURLM_OK) 217 { 218 curl_multi_remove_handle (multi, c); 219 curl_multi_cleanup (multi); 220 curl_easy_cleanup (c); 221 MHD_stop_daemon (d); 222 return 2048; 223 } 224 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 225 { 226 curl_multi_remove_handle (multi, c); 227 curl_multi_cleanup (multi); 228 curl_easy_cleanup (c); 229 MHD_stop_daemon (d); 230 return 4096; 231 } 232 tv.tv_sec = 0; 233 tv.tv_usec = 1000; 234 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 235 { 236 #ifdef MHD_POSIX_SOCKETS 237 if (EINTR != errno) 238 { 239 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 240 (int) errno, __LINE__); 241 fflush (stderr); 242 exit (99); 243 } 244 #else 245 if ((WSAEINVAL != WSAGetLastError ()) || 246 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 247 { 248 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 249 (int) WSAGetLastError (), __LINE__); 250 fflush (stderr); 251 exit (99); 252 } 253 Sleep (1); 254 #endif 255 } 256 curl_multi_perform (multi, &running); 257 if (0 == running) 258 { 259 int pending; 260 int curl_fine = 0; 261 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 262 { 263 if (msg->msg == CURLMSG_DONE) 264 { 265 if (msg->data.result == CURLE_OK) 266 curl_fine = 1; 267 else 268 { 269 fprintf (stderr, 270 "%s failed at %s:%d: `%s'\n", 271 "curl_multi_perform", 272 __FILE__, 273 __LINE__, curl_easy_strerror (msg->data.result)); 274 abort (); 275 } 276 } 277 } 278 if (! curl_fine) 279 { 280 fprintf (stderr, "libcurl haven't returned OK code\n"); 281 abort (); 282 } 283 curl_multi_remove_handle (multi, c); 284 curl_multi_cleanup (multi); 285 curl_easy_cleanup (c); 286 c = NULL; 287 multi = NULL; 288 } 289 MHD_run (d); 290 } 291 if (multi != NULL) 292 { 293 curl_multi_remove_handle (multi, c); 294 curl_easy_cleanup (c); 295 curl_multi_cleanup (multi); 296 } 297 MHD_stop_daemon (d); 298 if (cbc.pos != strlen ("/hello+world")) 299 return 8192; 300 if (0 != strncmp ("/hello+world", cbc.buf, strlen ("/hello+world"))) 301 return 16384; 302 return 0; 303 } 304 305 306 int 307 main (int argc, char *const *argv) 308 { 309 unsigned int errorCount = 0; 310 (void) argc; /* Unused. Silent compiler warning. */ 311 312 if ((NULL == argv) || (0 == argv[0])) 313 return 99; 314 oneone = has_in_name (argv[0], "11"); 315 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 316 return 2; 317 errorCount += testExternalGet (); 318 if (errorCount != 0) 319 fprintf (stderr, "Error (code: %u)\n", errorCount); 320 curl_global_cleanup (); 321 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 322 }