1 /*
2      This file is part of libmicrohttpd
3      Copyright (C) 2007, 2008 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 2, 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_long_header.c
23  * @brief  Testcase for libmicrohttpd handling of very long headers
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 #include "socat.c"
40 
41 /**
42  * We will set the memory available per connection to
43  * half of this value, so the actual value does not have
44  * to be big at all...
45  */
46 #define VERY_LONG (1024*10)
47 
48 static int oneone;
49 
50 static int
apc_all(void * cls,const struct sockaddr * addr,socklen_t addrlen)51 apc_all (void *cls, const struct sockaddr *addr, socklen_t addrlen)
52 {
53   return MHD_YES;
54 }
55 
56 struct CBC
57 {
58   char *buf;
59   size_t pos;
60   size_t size;
61 };
62 
63 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)64 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
65 {
66   return size * nmemb;
67 }
68 
69 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)70 ahc_echo (void *cls,
71           struct MHD_Connection *connection,
72           const char *url,
73           const char *method,
74           const char *version,
75           const char *upload_data, size_t *upload_data_size,
76           void **unused)
77 {
78   const char *me = cls;
79   struct MHD_Response *response;
80   int ret;
81 
82   if (0 != strcmp (me, method))
83     return MHD_NO;              /* unexpected method */
84   response = MHD_create_response_from_buffer (strlen (url),
85 					      (void *) url,
86 					      MHD_RESPMEM_MUST_COPY);
87   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
88   MHD_destroy_response (response);
89   return ret;
90 }
91 
92 
93 static int
testLongUrlGet()94 testLongUrlGet ()
95 {
96   struct MHD_Daemon *d;
97   CURL *c;
98   char buf[2048];
99   struct CBC cbc;
100   char *url;
101   int i;
102 
103   cbc.buf = buf;
104   cbc.size = 2048;
105   cbc.pos = 0;
106   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
107                         11080,
108                         &apc_all,
109                         NULL,
110                         &ahc_echo,
111                         "GET",
112                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
113                         (size_t) (VERY_LONG / 2), MHD_OPTION_END);
114 
115   if (d == NULL)
116     return 1;
117   zzuf_socat_start ();
118   for (i = 0; i < LOOP_COUNT; i++)
119     {
120       fprintf (stderr, ".");
121 
122       c = curl_easy_init ();
123       url = malloc (VERY_LONG);
124       memset (url, 'a', VERY_LONG);
125       url[VERY_LONG - 1] = '\0';
126       memcpy (url, "http://localhost:11081/",
127               strlen ("http://localhost:11081/"));
128       curl_easy_setopt (c, CURLOPT_URL, url);
129       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
130       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
131       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
132       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
133       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
134       if (oneone)
135         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
136       else
137         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
138       // NOTE: use of CONNECTTIMEOUT without also
139       //   setting NOSIGNAL results in really weird
140       //   crashes on my system!
141       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
142       curl_easy_perform (c);
143       curl_easy_cleanup (c);
144     }
145   fprintf (stderr, "\n");
146   zzuf_socat_stop ();
147 
148   MHD_stop_daemon (d);
149   free (url);
150   return 0;
151 }
152 
153 
154 static int
testLongHeaderGet()155 testLongHeaderGet ()
156 {
157   struct MHD_Daemon *d;
158   CURL *c;
159   char buf[2048];
160   struct CBC cbc;
161   char *url;
162   struct curl_slist *header = NULL;
163   int i;
164 
165   cbc.buf = buf;
166   cbc.size = 2048;
167   cbc.pos = 0;
168   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
169                         11080,
170                         &apc_all,
171                         NULL,
172                         &ahc_echo,
173                         "GET",
174                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
175                         (size_t) (VERY_LONG / 2), MHD_OPTION_END);
176   if (d == NULL)
177     return 16;
178   zzuf_socat_start ();
179   for (i = 0; i < LOOP_COUNT; i++)
180     {
181       fprintf (stderr, ".");
182       c = curl_easy_init ();
183       url = malloc (VERY_LONG);
184       memset (url, 'a', VERY_LONG);
185       url[VERY_LONG - 1] = '\0';
186       url[VERY_LONG / 2] = ':';
187       url[VERY_LONG / 2 + 1] = ' ';
188       header = curl_slist_append (header, url);
189 
190       curl_easy_setopt (c, CURLOPT_HTTPHEADER, header);
191       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
192       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
193       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
194       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
195       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
196       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
197       if (oneone)
198         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
199       else
200         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
201       // NOTE: use of CONNECTTIMEOUT without also
202       //   setting NOSIGNAL results in really weird
203       //   crashes on my system!
204       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
205       curl_easy_perform (c);
206       curl_slist_free_all (header);
207       header = NULL;
208       curl_easy_cleanup (c);
209     }
210   fprintf (stderr, "\n");
211   zzuf_socat_stop ();
212 
213   MHD_stop_daemon (d);
214   free (url);
215   return 0;
216 }
217 
218 
219 int
main(int argc,char * const * argv)220 main (int argc, char *const *argv)
221 {
222   unsigned int errorCount = 0;
223 
224   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
225     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
226   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
227     return 2;
228   errorCount += testLongUrlGet ();
229   errorCount += testLongHeaderGet ();
230   if (errorCount != 0)
231     fprintf (stderr, "Error (code: %u)\n", errorCount);
232   curl_global_cleanup ();
233   return errorCount != 0;       /* 0 == pass */
234 }
235