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
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)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
ahc_echo(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** unused)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
testExternalGet()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, &copyBuffer);
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
main(int argc,char * const * argv)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