soup-multipart-input-stream: belatedly add .h file to soup.h
[platform/upstream/libsoup.git] / tests / proxy-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 #include "test-utils.h"
4
5 typedef struct {
6         const char *explanation;
7         const char *url;
8         const guint final_status;
9 } SoupProxyTest;
10
11 static SoupProxyTest tests[] = {
12         { "GET -> 200", "", SOUP_STATUS_OK },
13         { "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND },
14         { "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK },
15         { "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED },
16         { "GET -> 403", "http://no-such-hostname.xx/", SOUP_STATUS_FORBIDDEN },
17 };
18 static const int ntests = sizeof (tests) / sizeof (tests[0]);
19
20 #define HTTP_SERVER    "http://127.0.0.1:47524"
21 #define HTTPS_SERVER   "https://127.0.0.1:47525"
22
23 enum {
24         SIMPLE_PROXY,
25         AUTH_PROXY,
26         UNAUTH_PROXY
27 };
28 static const char *proxies[] = {
29         "http://127.0.0.1:47526",
30         "http://127.0.0.1:47527",
31         "http://127.0.0.1:47528"
32 };
33 static const char *proxy_names[] = {
34         "simple proxy",
35         "authenticated proxy",
36         "unauthenticatable-to proxy"
37 };
38
39 static void
40 authenticate (SoupSession *session, SoupMessage *msg,
41               SoupAuth *auth, gboolean retrying, gpointer data)
42 {
43         if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
44                 if (soup_auth_is_for_proxy (auth)) {
45                         debug_printf (1, "  got proxy auth object for 401!\n");
46                         errors++;
47                 }
48         } else if (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
49                 if (!soup_auth_is_for_proxy (auth)) {
50                         debug_printf (1, "  got regular auth object for 407!\n");
51                         errors++;
52                 }
53         } else {
54                 debug_printf (1, "  got authenticate signal with status %d\n",
55                               msg->status_code);
56                 errors++;
57         }
58
59         if (!retrying)
60                 soup_auth_authenticate (auth, "user1", "realm1");
61 }
62
63 static void
64 set_close_on_connect (SoupSession *session, SoupMessage *msg,
65                       SoupSocket *sock, gpointer user_data)
66 {
67         /* This is used to test that we can handle the server closing
68          * the connection when returning a 407 in response to a
69          * CONNECT. (Rude!)
70          */
71         if (msg->method == SOUP_METHOD_CONNECT) {
72                 soup_message_headers_append (msg->request_headers,
73                                              "Connection", "close");
74         }
75 }
76
77
78 static void
79 test_url (const char *url, int proxy, guint expected,
80           gboolean sync, gboolean close)
81 {
82         SoupSession *session;
83         SoupURI *proxy_uri;
84         SoupMessage *msg;
85
86         if (!tls_available && g_str_has_prefix (url, "https:"))
87                 return;
88
89         debug_printf (1, "  GET %s via %s%s\n", url, proxy_names[proxy],
90                       close ? " (with Connection: close)" : "");
91         if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN)
92                 expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
93
94         /* We create a new session for each request to ensure that
95          * connections/auth aren't cached between tests.
96          */
97         proxy_uri = soup_uri_new (proxies[proxy]);
98         session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
99                                          SOUP_SESSION_PROXY_URI, proxy_uri,
100                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
101                                          NULL);
102         soup_uri_free (proxy_uri);
103         g_signal_connect (session, "authenticate",
104                           G_CALLBACK (authenticate), NULL);
105         if (close) {
106                 g_signal_connect (session, "request-started",
107                                   G_CALLBACK (set_close_on_connect), NULL);
108         }
109
110         msg = soup_message_new (SOUP_METHOD_GET, url);
111         if (!msg) {
112                 g_printerr ("proxy-test: Could not parse URI\n");
113                 exit (1);
114         }
115
116         soup_session_send_message (session, msg);
117
118         debug_printf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
119         if (msg->status_code != expected) {
120                 debug_printf (1, "  EXPECTED %d!\n", expected);
121                 errors++;
122         }
123
124         g_object_unref (msg);
125         soup_test_session_abort_unref (session);
126 }
127
128 static void
129 test_url_new_api (const char *url, int proxy, guint expected,
130                   gboolean sync, gboolean close)
131 {
132         SoupSession *session;
133         SoupURI *proxy_uri;
134         SoupMessage *msg;
135         SoupRequester *requester;
136         SoupRequest *request;
137         GInputStream *stream;
138         GError *error = NULL;
139
140         if (!tls_available && g_str_has_prefix (url, "https:"))
141                 return;
142
143         debug_printf (1, "  GET (requester API) %s via %s%s\n", url, proxy_names[proxy],
144                       close ? " (with Connection: close)" : "");
145         if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN)
146                 expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
147
148         /* We create a new session for each request to ensure that
149          * connections/auth aren't cached between tests.
150          */
151         proxy_uri = soup_uri_new (proxies[proxy]);
152         session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
153                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
154                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
155                                          SOUP_SESSION_PROXY_URI, proxy_uri,
156                                          NULL);
157         soup_uri_free (proxy_uri);
158
159         g_signal_connect (session, "authenticate",
160                           G_CALLBACK (authenticate), NULL);
161         if (close) {
162                 g_signal_connect (session, "request-started",
163                                   G_CALLBACK (set_close_on_connect), NULL);
164         }
165
166         requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
167         request = soup_requester_request (requester, url, NULL);
168         msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
169
170         stream = soup_test_request_send (request, NULL, &error);
171         if (!stream) {
172                 debug_printf (1, "  Unexpected error on Request: %s\n",
173                               error->message);
174                 errors++;
175                 g_clear_error (&error);
176         }
177
178         if (stream) {
179                 soup_test_request_close_stream (request, stream, NULL, NULL);
180                 if (error) {
181                         debug_printf (1, "  Unexpected error on close: %s\n",
182                                       error->message);
183                         errors++;
184                         g_clear_error (&error);
185                 }
186                 g_object_unref (stream);
187         }
188
189         debug_printf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
190         if (msg->status_code != expected) {
191                 debug_printf (1, "  EXPECTED %d!\n", expected);
192                 errors++;
193         }
194
195         g_object_unref (msg);
196         g_object_unref (request);
197
198         soup_test_session_abort_unref (session);
199 }
200
201 static void
202 run_test (int i, gboolean sync)
203 {
204         char *http_url, *https_url;
205
206         debug_printf (1, "Test %d: %s (%s)\n", i + 1, tests[i].explanation,
207                       sync ? "sync" : "async");
208
209         if (!strncmp (tests[i].url, "http", 4)) {
210                 http_url = g_strdup (tests[i].url);
211                 https_url = g_strdup_printf ("https%s", tests[i].url + 4);
212         } else {
213                 http_url = g_strconcat (HTTP_SERVER, tests[i].url, NULL);
214                 https_url = g_strconcat (HTTPS_SERVER, tests[i].url, NULL);
215         }
216
217         test_url (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
218         test_url_new_api (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
219         test_url (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
220         test_url_new_api (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
221
222         test_url (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
223         test_url_new_api (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
224         test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
225         test_url_new_api (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
226         test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE);
227         test_url_new_api (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE);
228
229         test_url (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
230         test_url_new_api (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
231         test_url (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
232         test_url_new_api (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
233
234         g_free (http_url);
235         g_free (https_url);
236
237         debug_printf (1, "\n");
238 }
239
240 static gpointer
241 async_proxy_test_thread (gpointer num)
242 {
243         GMainContext *context = g_main_context_new ();
244
245         g_main_context_push_thread_default (context);
246         run_test (GPOINTER_TO_INT (num), FALSE);
247         g_main_context_pop_thread_default (context);
248         g_main_context_unref (context);
249
250         return NULL;
251 }
252
253 static gpointer
254 sync_proxy_test_thread (gpointer num)
255 {
256         run_test (GPOINTER_TO_INT (num), TRUE);
257         return NULL;
258 }
259
260 static void
261 do_proxy_tests (void)
262 {
263         int i;
264
265         debug_printf (1, "Basic proxy tests\n");
266
267         if (parallelize) {
268                 GThread *threads[ntests];
269
270                 /* Doing the sync and async tests separately is faster
271                  * than doing them both at the same time (hitting
272                  * apache's connection limit maybe?)
273                  */
274                 for (i = 0; i < ntests; i++) {
275                         threads[i] = g_thread_new ("async_proxy_test",
276                                                    async_proxy_test_thread,
277                                                    GINT_TO_POINTER (i));
278                 }
279                 for (i = 0; i < ntests; i++)
280                         g_thread_join (threads[i]);
281
282                 for (i = 0; i < ntests; i++) {
283                         threads[i] = g_thread_new ("sync_proxy_test",
284                                                    sync_proxy_test_thread,
285                                                    GINT_TO_POINTER (i));
286                 }
287                 for (i = 0; i < ntests; i++)
288                         g_thread_join (threads[i]);
289         } else {
290                 for (i = 0; i < ntests; i++) {
291                         run_test (i, FALSE);
292                         run_test (i, TRUE);
293                 }
294         }
295 }
296
297 static void
298 server_callback (SoupServer *server, SoupMessage *msg,
299                  const char *path, GHashTable *query,
300                  SoupClientContext *context, gpointer data)
301 {
302         SoupURI *uri = soup_message_get_uri (msg);
303
304         soup_message_set_status (msg, uri->fragment ? SOUP_STATUS_BAD_REQUEST : SOUP_STATUS_OK);
305 }
306
307 static void
308 do_proxy_fragment_test (SoupURI *base_uri)
309 {
310         SoupSession *session;
311         SoupURI *proxy_uri, *req_uri;
312         SoupMessage *msg;
313
314         debug_printf (1, "\nTesting request with fragment via proxy\n");
315
316         proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
317         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
318                                          SOUP_SESSION_PROXY_URI, proxy_uri,
319                                          NULL);
320         soup_uri_free (proxy_uri);
321
322         req_uri = soup_uri_new_with_base (base_uri, "/#foo");
323         msg = soup_message_new_from_uri (SOUP_METHOD_GET, req_uri);
324         soup_uri_free (req_uri);
325         soup_session_send_message (session, msg);
326
327         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
328                 debug_printf (1, "  unexpected status %d %s!\n",
329                               msg->status_code, msg->reason_phrase);
330                 errors++;
331         }
332
333         g_object_unref (msg);
334         soup_test_session_abort_unref (session);
335 }
336
337 static void
338 do_proxy_redirect_test (void)
339 {
340         SoupSession *session;
341         SoupURI *proxy_uri, *req_uri, *new_uri;
342         SoupMessage *msg;
343
344         debug_printf (1, "\nTesting redirection through proxy\n");
345
346         proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
347         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
348                                          SOUP_SESSION_PROXY_URI, proxy_uri,
349                                          NULL);
350         soup_uri_free (proxy_uri);
351
352         req_uri = soup_uri_new (HTTPS_SERVER);
353         soup_uri_set_path (req_uri, "/redirected");
354         msg = soup_message_new_from_uri (SOUP_METHOD_GET, req_uri);
355         soup_message_headers_append (msg->request_headers,
356                                      "Connection", "close");
357         soup_session_send_message (session, msg);
358
359         new_uri = soup_message_get_uri (msg);
360         if (!strcmp (req_uri->path, new_uri->path)) {
361                 debug_printf (1, "  message was not redirected!\n");
362                 errors++;
363         }
364         soup_uri_free (req_uri);
365
366         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
367                 debug_printf (1, "  unexpected status %d %s!\n",
368                               msg->status_code, msg->reason_phrase);
369                 errors++;
370         }
371
372         g_object_unref (msg);
373         soup_test_session_abort_unref (session);
374 }
375
376 int
377 main (int argc, char **argv)
378 {
379         SoupServer *server;
380         SoupURI *base_uri;
381
382         test_init (argc, argv, NULL);
383         apache_init ();
384
385         server = soup_test_server_new (TRUE);
386         soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
387         base_uri = soup_uri_new ("http://127.0.0.1/");
388         soup_uri_set_port (base_uri, soup_server_get_port (server));
389
390         do_proxy_tests ();
391         do_proxy_fragment_test (base_uri);
392         do_proxy_redirect_test ();
393
394         soup_uri_free (base_uri);
395         soup_test_server_quit_unref (server);
396
397         test_cleanup ();
398         return errors != 0;
399 }