test_process_arguments.c (8740B)
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 #ifndef WINDOWS 40 #include <unistd.h> 41 #endif 42 43 static int oneone; 44 45 struct CBC 46 { 47 char *buf; 48 size_t pos; 49 size_t size; 50 }; 51 52 53 static size_t 54 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 55 { 56 struct CBC *cbc = ctx; 57 58 if (cbc->pos + size * nmemb > cbc->size) 59 return 0; /* overflow */ 60 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 61 cbc->pos += size * nmemb; 62 return size * nmemb; 63 } 64 65 66 static enum MHD_Result 67 ahc_echo (void *cls, 68 struct MHD_Connection *connection, 69 const char *url, 70 const char *method, 71 const char *version, 72 const char *upload_data, size_t *upload_data_size, 73 void **req_cls) 74 { 75 static int ptr; 76 struct MHD_Response *response; 77 enum MHD_Result ret; 78 const char *hdr; 79 (void) cls; 80 (void) version; (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 81 82 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 83 return MHD_NO; /* unexpected method */ 84 if (&ptr != *req_cls) 85 { 86 *req_cls = &ptr; 87 return MHD_YES; 88 } 89 *req_cls = NULL; 90 hdr = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "k"); 91 if ((hdr == NULL) || (0 != strcmp (hdr, "v x"))) 92 abort (); 93 hdr = MHD_lookup_connection_value (connection, 94 MHD_GET_ARGUMENT_KIND, "hash"); 95 if ((hdr == NULL) || (0 != strcmp (hdr, "#foo"))) 96 abort (); 97 hdr = MHD_lookup_connection_value (connection, 98 MHD_GET_ARGUMENT_KIND, "space"); 99 if ((hdr == NULL) || (0 != strcmp (hdr, "\240bar"))) 100 abort (); 101 if (3 != MHD_get_connection_values (connection, 102 MHD_GET_ARGUMENT_KIND, 103 NULL, NULL)) 104 abort (); 105 response = MHD_create_response_from_buffer_copy (strlen (url), 106 (const void *) url); 107 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 108 MHD_destroy_response (response); 109 if (ret == MHD_NO) 110 abort (); 111 return ret; 112 } 113 114 115 static unsigned int 116 testExternalGet (void) 117 { 118 struct MHD_Daemon *d; 119 CURL *c; 120 char buf[2048]; 121 struct CBC cbc; 122 CURLM *multi; 123 CURLMcode mret; 124 fd_set rs; 125 fd_set ws; 126 fd_set es; 127 MHD_socket maxsock; 128 #ifdef MHD_WINSOCK_SOCKETS 129 int maxposixs; /* Max socket number unused on W32 */ 130 #else /* MHD_POSIX_SOCKETS */ 131 #define maxposixs maxsock 132 #endif /* MHD_POSIX_SOCKETS */ 133 int running; 134 struct CURLMsg *msg; 135 time_t start; 136 struct timeval tv; 137 uint16_t port; 138 139 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 140 port = 0; 141 else 142 { 143 port = 1410; 144 if (oneone) 145 port += 5; 146 } 147 148 multi = NULL; 149 cbc.buf = buf; 150 cbc.size = 2048; 151 cbc.pos = 0; 152 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 153 port, NULL, NULL, &ahc_echo, NULL, 154 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 155 MHD_OPTION_END); 156 if (d == NULL) 157 return 256; 158 if (0 == port) 159 { 160 const union MHD_DaemonInfo *dinfo; 161 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 162 if ((NULL == dinfo) || (0 == dinfo->port) ) 163 { 164 MHD_stop_daemon (d); return 32; 165 } 166 port = dinfo->port; 167 } 168 c = curl_easy_init (); 169 curl_easy_setopt (c, CURLOPT_URL, 170 "http://127.0.0.1/hello+world?k=v+x&hash=%23foo&space=%A0bar"); 171 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 172 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 173 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 174 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 175 if (oneone) 176 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 177 else 178 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 179 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 180 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 181 /* NOTE: use of CONNECTTIMEOUT without also 182 setting NOSIGNAL results in really weird 183 crashes on my system! */ 184 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 185 186 187 multi = curl_multi_init (); 188 if (multi == NULL) 189 { 190 curl_easy_cleanup (c); 191 MHD_stop_daemon (d); 192 return 512; 193 } 194 mret = curl_multi_add_handle (multi, c); 195 if (mret != CURLM_OK) 196 { 197 curl_multi_cleanup (multi); 198 curl_easy_cleanup (c); 199 MHD_stop_daemon (d); 200 return 1024; 201 } 202 start = time (NULL); 203 while ((time (NULL) - start < 5) && (multi != NULL)) 204 { 205 maxsock = MHD_INVALID_SOCKET; 206 maxposixs = -1; 207 FD_ZERO (&rs); 208 FD_ZERO (&ws); 209 FD_ZERO (&es); 210 curl_multi_perform (multi, &running); 211 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 212 if (mret != CURLM_OK) 213 { 214 curl_multi_remove_handle (multi, c); 215 curl_multi_cleanup (multi); 216 curl_easy_cleanup (c); 217 MHD_stop_daemon (d); 218 return 2048; 219 } 220 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 221 { 222 curl_multi_remove_handle (multi, c); 223 curl_multi_cleanup (multi); 224 curl_easy_cleanup (c); 225 MHD_stop_daemon (d); 226 return 4096; 227 } 228 tv.tv_sec = 0; 229 tv.tv_usec = 1000; 230 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 231 { 232 #ifdef MHD_POSIX_SOCKETS 233 if (EINTR != errno) 234 { 235 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 236 (int) errno, __LINE__); 237 fflush (stderr); 238 exit (99); 239 } 240 #else 241 if ((WSAEINVAL != WSAGetLastError ()) || 242 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 243 { 244 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 245 (int) WSAGetLastError (), __LINE__); 246 fflush (stderr); 247 exit (99); 248 } 249 Sleep (1); 250 #endif 251 } 252 curl_multi_perform (multi, &running); 253 if (0 == running) 254 { 255 int pending; 256 int curl_fine = 0; 257 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 258 { 259 if (msg->msg == CURLMSG_DONE) 260 { 261 if (msg->data.result == CURLE_OK) 262 curl_fine = 1; 263 else 264 { 265 fprintf (stderr, 266 "%s failed at %s:%d: `%s'\n", 267 "curl_multi_perform", 268 __FILE__, 269 __LINE__, curl_easy_strerror (msg->data.result)); 270 abort (); 271 } 272 } 273 } 274 if (! curl_fine) 275 { 276 fprintf (stderr, "libcurl haven't returned OK code\n"); 277 abort (); 278 } 279 curl_multi_remove_handle (multi, c); 280 curl_multi_cleanup (multi); 281 curl_easy_cleanup (c); 282 c = NULL; 283 multi = NULL; 284 } 285 MHD_run (d); 286 } 287 if (multi != NULL) 288 { 289 curl_multi_remove_handle (multi, c); 290 curl_easy_cleanup (c); 291 curl_multi_cleanup (multi); 292 } 293 MHD_stop_daemon (d); 294 if (cbc.pos != strlen ("/hello+world")) 295 return 8192; 296 if (0 != strncmp ("/hello+world", cbc.buf, strlen ("/hello+world"))) 297 return 16384; 298 return 0; 299 } 300 301 302 int 303 main (int argc, char *const *argv) 304 { 305 unsigned int errorCount = 0; 306 (void) argc; /* Unused. Silent compiler warning. */ 307 308 if ((NULL == argv) || (0 == argv[0])) 309 return 99; 310 oneone = has_in_name (argv[0], "11"); 311 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 312 return 2; 313 errorCount += testExternalGet (); 314 if (errorCount != 0) 315 fprintf (stderr, "Error (code: %u)\n", errorCount); 316 curl_global_cleanup (); 317 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 318 }