Fix the retry-on-broken-connection codepath for SoupRequest
[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 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                                          NULL);
101         soup_uri_free (proxy_uri);
102         g_signal_connect (session, "authenticate",
103                           G_CALLBACK (authenticate), NULL);
104         if (close) {
105                 g_signal_connect (session, "request-started",
106                                   G_CALLBACK (set_close_on_connect), NULL);
107         }
108
109         msg = soup_message_new (SOUP_METHOD_GET, url);
110         if (!msg) {
111                 g_printerr ("proxy-test: Could not parse URI\n");
112                 exit (1);
113         }
114
115         soup_session_send_message (session, msg);
116
117         debug_printf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
118         if (msg->status_code != expected) {
119                 debug_printf (1, "  EXPECTED %d!\n", expected);
120                 errors++;
121         }
122
123         g_object_unref (msg);
124         soup_test_session_abort_unref (session);
125 }
126
127 static void
128 test_url_new_api (const char *url, int proxy, guint expected,
129                   gboolean sync, gboolean close)
130 {
131         SoupSession *session;
132         SoupURI *proxy_uri;
133         SoupMessage *msg;
134         SoupRequester *requester;
135         SoupRequest *request;
136         GInputStream *stream;
137         GError *error = NULL;
138
139         if (!tls_available && g_str_has_prefix (url, "https:"))
140                 return;
141
142         debug_printf (1, "  GET (requester API) %s via %s%s\n", url, proxy_names[proxy],
143                       close ? " (with Connection: close)" : "");
144         if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN)
145                 expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
146
147         /* We create a new session for each request to ensure that
148          * connections/auth aren't cached between tests.
149          */
150         proxy_uri = soup_uri_new (proxies[proxy]);
151         session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
152                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
153                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
154                                          SOUP_SESSION_PROXY_URI, proxy_uri,
155                                          NULL);
156         soup_uri_free (proxy_uri);
157
158         g_signal_connect (session, "authenticate",
159                           G_CALLBACK (authenticate), NULL);
160         if (close) {
161                 g_signal_connect (session, "request-started",
162                                   G_CALLBACK (set_close_on_connect), NULL);
163         }
164
165         requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
166         request = soup_requester_request (requester, url, NULL);
167         msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
168
169         if (sync)
170                 stream = soup_request_send (request, NULL, &error);
171         else
172                 stream = soup_test_request_send_async_as_sync (request, NULL, &error);
173
174         if (!stream) {
175                 debug_printf (1, "  Unexpected error on Request: %s\n",
176                               error->message);
177                 errors++;
178                 g_clear_error (&error);
179         }
180
181         if (stream) {
182                 if (sync)
183                         g_input_stream_close (stream, NULL, NULL);
184                 else
185                         soup_test_stream_close_async_as_sync (stream, NULL, NULL);
186                 if (error) {
187                         debug_printf (1, "  Unexpected error on close: %s\n",
188                                       error->message);
189                         errors++;
190                         g_clear_error (&error);
191                 }
192         }
193
194         debug_printf (1, "  %d %s\n", msg->status_code, msg->reason_phrase);
195         if (msg->status_code != expected) {
196                 debug_printf (1, "  EXPECTED %d!\n", expected);
197                 errors++;
198         }
199
200         g_object_unref (msg);
201         g_object_unref (request);
202
203         soup_test_session_abort_unref (session);
204 }
205
206 static void
207 run_test (int i, gboolean sync)
208 {
209         char *http_url, *https_url;
210
211         debug_printf (1, "Test %d: %s (%s)\n", i + 1, tests[i].explanation,
212                       sync ? "sync" : "async");
213
214         if (!strncmp (tests[i].url, "http", 4)) {
215                 http_url = g_strdup (tests[i].url);
216                 https_url = g_strdup_printf ("https%s", tests[i].url + 4);
217         } else {
218                 http_url = g_strconcat (HTTP_SERVER, tests[i].url, NULL);
219                 https_url = g_strconcat (HTTPS_SERVER, tests[i].url, NULL);
220         }
221
222         test_url (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
223         test_url_new_api (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
224         test_url (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
225         test_url_new_api (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
226
227         test_url (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
228         test_url_new_api (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
229         test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
230         test_url_new_api (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
231         test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE);
232         test_url_new_api (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE);
233
234         test_url (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
235         test_url_new_api (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
236         test_url (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
237         test_url_new_api (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
238
239         g_free (http_url);
240         g_free (https_url);
241
242         debug_printf (1, "\n");
243 }
244
245 static void
246 server_callback (SoupServer *server, SoupMessage *msg,
247                  const char *path, GHashTable *query,
248                  SoupClientContext *context, gpointer data)
249 {
250         SoupURI *uri = soup_message_get_uri (msg);
251
252         soup_message_set_status (msg, uri->fragment ? SOUP_STATUS_BAD_REQUEST : SOUP_STATUS_OK);
253 }
254
255 static void
256 do_proxy_fragment_test (SoupURI *base_uri)
257 {
258         SoupSession *session;
259         SoupURI *proxy_uri, *req_uri;
260         SoupMessage *msg;
261
262         debug_printf (1, "\nTesting request with fragment via proxy\n");
263
264         proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
265         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
266                                          SOUP_SESSION_PROXY_URI, proxy_uri,
267                                          NULL);
268         soup_uri_free (proxy_uri);
269
270         req_uri = soup_uri_new_with_base (base_uri, "/#foo");
271         msg = soup_message_new_from_uri (SOUP_METHOD_GET, req_uri);
272         soup_uri_free (req_uri);
273         soup_session_send_message (session, msg);
274
275         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
276                 debug_printf (1, "  unexpected status %d %s!\n",
277                               msg->status_code, msg->reason_phrase);
278                 errors++;
279         }
280
281         g_object_unref (msg);
282         soup_test_session_abort_unref (session);
283 }
284
285 static void
286 do_proxy_redirect_test (void)
287 {
288         SoupSession *session;
289         SoupURI *proxy_uri, *req_uri, *new_uri;
290         SoupMessage *msg;
291
292         debug_printf (1, "\nTesting redirection through proxy\n");
293
294         proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
295         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
296                                          SOUP_SESSION_PROXY_URI, proxy_uri,
297                                          NULL);
298         soup_uri_free (proxy_uri);
299
300         req_uri = soup_uri_new (HTTPS_SERVER);
301         soup_uri_set_path (req_uri, "/redirected");
302         msg = soup_message_new_from_uri (SOUP_METHOD_GET, req_uri);
303         soup_message_headers_append (msg->request_headers,
304                                      "Connection", "close");
305         soup_session_send_message (session, msg);
306
307         new_uri = soup_message_get_uri (msg);
308         if (!strcmp (req_uri->path, new_uri->path)) {
309                 debug_printf (1, "  message was not redirected!\n");
310                 errors++;
311         }
312         soup_uri_free (req_uri);
313
314         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
315                 debug_printf (1, "  unexpected status %d %s!\n",
316                               msg->status_code, msg->reason_phrase);
317                 errors++;
318         }
319
320         g_object_unref (msg);
321         soup_test_session_abort_unref (session);
322 }
323
324 int
325 main (int argc, char **argv)
326 {
327         SoupServer *server;
328         SoupURI *base_uri;
329         int i;
330
331         test_init (argc, argv, NULL);
332         apache_init ();
333
334         for (i = 0; i < ntests; i++) {
335                 run_test (i, FALSE);
336                 run_test (i, TRUE);
337         }
338
339         server = soup_test_server_new (TRUE);
340         soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
341         base_uri = soup_uri_new ("http://127.0.0.1/");
342         soup_uri_set_port (base_uri, soup_server_get_port (server));
343
344         do_proxy_fragment_test (base_uri);
345         do_proxy_redirect_test ();
346
347         soup_uri_free (base_uri);
348         soup_test_server_quit_unref (server);
349
350         test_cleanup ();
351         return errors != 0;
352 }