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 const char *bugref;
10 } SoupProxyTest;
11
12 static SoupProxyTest tests[] = {
13 { "GET -> 200", "", SOUP_STATUS_OK, NULL },
14 { "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND, NULL },
15 { "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK, NULL },
16 { "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED, NULL },
17 { "GET -> 403", "http://no-such-hostname.xx/", SOUP_STATUS_FORBIDDEN, "577532" },
18 { "GET -> 200 (unproxied)", "http://localhost:47524/", SOUP_STATUS_OK, "700472" },
19 };
20 static const int ntests = sizeof (tests) / sizeof (tests[0]);
21
22 #define HTTP_SERVER "http://127.0.0.1:47524"
23 #define HTTPS_SERVER "https://127.0.0.1:47525"
24
25 enum {
26 SIMPLE_PROXY,
27 AUTH_PROXY,
28 UNAUTH_PROXY
29 };
30 static const char *proxies[] = {
31 "http://127.0.0.1:47526",
32 "http://127.0.0.1:47527",
33 "http://127.0.0.1:47528"
34 };
35 static const char *proxy_names[] = {
36 "simple proxy",
37 "authenticated proxy",
38 "unauthenticatable-to proxy"
39 };
40 static GProxyResolver *proxy_resolvers[3];
41 static const char *ignore_hosts[] = { "localhost", NULL };
42
43 static void
authenticate(SoupSession * session,SoupMessage * msg,SoupAuth * auth,gboolean retrying,gpointer data)44 authenticate (SoupSession *session, SoupMessage *msg,
45 SoupAuth *auth, gboolean retrying, gpointer data)
46 {
47 if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
48 soup_test_assert (!soup_auth_is_for_proxy (auth),
49 "got proxy auth object for 401");
50 } else if (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
51 soup_test_assert (soup_auth_is_for_proxy (auth),
52 "got regular auth object for 407");
53 } else {
54 soup_test_assert (FALSE,
55 "got authenticate signal with status %d\n",
56 msg->status_code);
57 }
58
59 if (!retrying)
60 soup_auth_authenticate (auth, "user1", "realm1");
61 }
62
63 static void
set_close_on_connect(SoupSession * session,SoupMessage * msg,SoupSocket * sock,gpointer user_data)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
test_url(const char * url,int proxy,guint expected,gboolean sync,gboolean close)79 test_url (const char *url, int proxy, guint expected,
80 gboolean sync, gboolean close)
81 {
82 SoupSession *session;
83 SoupMessage *msg;
84 gboolean noproxy = !!strstr (url, "localhost");
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 && !noproxy)
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 session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
98 SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[proxy],
99 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
100 SOUP_SESSION_SSL_STRICT, FALSE,
101 NULL);
102 g_signal_connect (session, "authenticate",
103 G_CALLBACK (authenticate), NULL);
104 if (close) {
105 /* FIXME g_test_bug ("611663") */
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 soup_test_assert_message_status (msg, expected);
120
121 g_object_unref (msg);
122 soup_test_session_abort_unref (session);
123 }
124
125 static void
test_url_new_api(const char * url,int proxy,guint expected,gboolean sync,gboolean close)126 test_url_new_api (const char *url, int proxy, guint expected,
127 gboolean sync, gboolean close)
128 {
129 SoupSession *session;
130 SoupMessage *msg;
131 SoupRequest *request;
132 GInputStream *stream;
133 GError *error = NULL;
134 gboolean noproxy = !!strstr (url, "localhost");
135
136 /* FIXME g_test_skip() FIXME g_test_bug ("675865") */
137 if (!tls_available && g_str_has_prefix (url, "https:"))
138 return;
139
140 debug_printf (1, " GET (request API) %s via %s%s\n", url, proxy_names[proxy],
141 close ? " (with Connection: close)" : "");
142 if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN && !noproxy)
143 expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
144
145 /* We create a new session for each request to ensure that
146 * connections/auth aren't cached between tests.
147 */
148 session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
149 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
150 SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[proxy],
151 SOUP_SESSION_SSL_STRICT, FALSE,
152 NULL);
153
154 g_signal_connect (session, "authenticate",
155 G_CALLBACK (authenticate), NULL);
156 if (close) {
157 /* FIXME g_test_bug ("611663") */
158 g_signal_connect (session, "request-started",
159 G_CALLBACK (set_close_on_connect), NULL);
160 }
161
162 request = soup_session_request (session, url, NULL);
163 msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
164
165 stream = soup_test_request_send (request, NULL, 0, &error);
166 g_assert_no_error (error);
167 g_clear_error (&error);
168
169 if (stream) {
170 soup_test_request_close_stream (request, stream, NULL, &error);
171 g_assert_no_error (error);
172 g_clear_error (&error);
173 g_object_unref (stream);
174 }
175
176 debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase);
177 soup_test_assert_message_status (msg, expected);
178
179 g_object_unref (msg);
180 g_object_unref (request);
181
182 soup_test_session_abort_unref (session);
183 }
184
185 static void
do_proxy_test(SoupProxyTest * test,gboolean sync)186 do_proxy_test (SoupProxyTest *test, gboolean sync)
187 {
188 char *http_url, *https_url;
189
190 if (test->bugref)
191 g_test_bug (test->bugref);
192
193 if (!strncmp (test->url, "http", 4)) {
194 SoupURI *uri;
195 guint port;
196
197 http_url = g_strdup (test->url);
198
199 uri = soup_uri_new (test->url);
200 port = uri->port;
201 soup_uri_set_scheme (uri, "https");
202 if (port)
203 soup_uri_set_port (uri, port + 1);
204 https_url = soup_uri_to_string (uri, FALSE);
205 soup_uri_free (uri);
206 } else {
207 http_url = g_strconcat (HTTP_SERVER, test->url, NULL);
208 https_url = g_strconcat (HTTPS_SERVER, test->url, NULL);
209 }
210
211 test_url (http_url, SIMPLE_PROXY, test->final_status, sync, FALSE);
212 test_url_new_api (http_url, SIMPLE_PROXY, test->final_status, sync, FALSE);
213 test_url (https_url, SIMPLE_PROXY, test->final_status, sync, FALSE);
214 test_url_new_api (https_url, SIMPLE_PROXY, test->final_status, sync, FALSE);
215
216 test_url (http_url, AUTH_PROXY, test->final_status, sync, FALSE);
217 test_url_new_api (http_url, AUTH_PROXY, test->final_status, sync, FALSE);
218 test_url (https_url, AUTH_PROXY, test->final_status, sync, FALSE);
219 test_url_new_api (https_url, AUTH_PROXY, test->final_status, sync, FALSE);
220 test_url (https_url, AUTH_PROXY, test->final_status, sync, TRUE);
221 test_url_new_api (https_url, AUTH_PROXY, test->final_status, sync, TRUE);
222
223 test_url (http_url, UNAUTH_PROXY, test->final_status, sync, FALSE);
224 test_url_new_api (http_url, UNAUTH_PROXY, test->final_status, sync, FALSE);
225 test_url (https_url, UNAUTH_PROXY, test->final_status, sync, FALSE);
226 test_url_new_api (https_url, UNAUTH_PROXY, test->final_status, sync, FALSE);
227
228 g_free (http_url);
229 g_free (https_url);
230 }
231
232 static void
do_async_proxy_test(gconstpointer data)233 do_async_proxy_test (gconstpointer data)
234 {
235 SoupProxyTest *test = (SoupProxyTest *)data;
236
237 SOUP_TEST_SKIP_IF_NO_APACHE;
238
239 do_proxy_test (test, FALSE);
240 }
241
242 static void
do_sync_proxy_test(gconstpointer data)243 do_sync_proxy_test (gconstpointer data)
244 {
245 SoupProxyTest *test = (SoupProxyTest *)data;
246
247 SOUP_TEST_SKIP_IF_NO_APACHE;
248
249 do_proxy_test (test, TRUE);
250 }
251
252 static void
server_callback(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)253 server_callback (SoupServer *server, SoupMessage *msg,
254 const char *path, GHashTable *query,
255 SoupClientContext *context, gpointer data)
256 {
257 SoupURI *uri = soup_message_get_uri (msg);
258
259 soup_message_set_status (msg, uri->fragment ? SOUP_STATUS_BAD_REQUEST : SOUP_STATUS_OK);
260 }
261
262 static void
do_proxy_fragment_test(gconstpointer data)263 do_proxy_fragment_test (gconstpointer data)
264 {
265 SoupURI *base_uri = (SoupURI *)data;
266 SoupSession *session;
267 SoupURI *proxy_uri, *req_uri;
268 SoupMessage *msg;
269
270 SOUP_TEST_SKIP_IF_NO_APACHE;
271
272 proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
273 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
274 SOUP_SESSION_PROXY_URI, proxy_uri,
275 NULL);
276 soup_uri_free (proxy_uri);
277
278 req_uri = soup_uri_new_with_base (base_uri, "/#foo");
279 msg = soup_message_new_from_uri (SOUP_METHOD_GET, req_uri);
280 soup_uri_free (req_uri);
281 soup_session_send_message (session, msg);
282
283 soup_test_assert_message_status (msg, SOUP_STATUS_OK);
284
285 g_object_unref (msg);
286 soup_test_session_abort_unref (session);
287 }
288
289 static void
do_proxy_redirect_test(void)290 do_proxy_redirect_test (void)
291 {
292 SoupSession *session;
293 SoupURI *proxy_uri, *req_uri, *new_uri;
294 SoupMessage *msg;
295
296 g_test_bug ("631368");
297
298 SOUP_TEST_SKIP_IF_NO_APACHE;
299 SOUP_TEST_SKIP_IF_NO_TLS;
300
301 proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
302 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
303 SOUP_SESSION_PROXY_URI, proxy_uri,
304 NULL);
305 soup_uri_free (proxy_uri);
306
307 req_uri = soup_uri_new (HTTPS_SERVER);
308 soup_uri_set_path (req_uri, "/redirected");
309 msg = soup_message_new_from_uri (SOUP_METHOD_GET, req_uri);
310 soup_message_headers_append (msg->request_headers,
311 "Connection", "close");
312 soup_session_send_message (session, msg);
313
314 new_uri = soup_message_get_uri (msg);
315 soup_test_assert (strcmp (req_uri->path, new_uri->path) != 0,
316 "message was not redirected");
317 soup_uri_free (req_uri);
318
319 soup_test_assert_message_status (msg, SOUP_STATUS_OK);
320
321 g_object_unref (msg);
322 soup_test_session_abort_unref (session);
323 }
324
325 static void
do_proxy_auth_request(const char * url,SoupSession * session,gboolean do_read)326 do_proxy_auth_request (const char *url, SoupSession *session, gboolean do_read)
327 {
328 SoupRequest *request;
329 SoupMessage *msg;
330 GInputStream *stream;
331 GError *error = NULL;
332
333 request = soup_session_request (session, url, NULL);
334 msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
335
336 stream = soup_test_request_send (request, NULL, 0, &error);
337 g_assert_no_error (error);
338 g_clear_error (&error);
339
340 if (do_read) {
341 char buffer[256];
342 gsize nread;
343
344 do {
345 g_input_stream_read_all (stream, buffer, sizeof (buffer), &nread,
346 NULL, &error);
347 g_assert_no_error (error);
348 g_clear_error (&error);
349 } while (nread > 0);
350 }
351
352 soup_test_request_close_stream (request, stream, NULL, &error);
353 g_assert_no_error (error);
354 g_clear_error (&error);
355 g_object_unref (stream);
356
357 debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase);
358 soup_test_assert_message_status (msg, SOUP_STATUS_OK);
359
360 g_object_unref (msg);
361 g_object_unref (request);
362 }
363
364 static void
do_proxy_auth_cache_test(void)365 do_proxy_auth_cache_test (void)
366 {
367 SoupSession *session;
368 char *cache_dir;
369 SoupCache *cache;
370 char *url;
371
372 g_test_bug ("756076");
373
374 SOUP_TEST_SKIP_IF_NO_APACHE;
375
376 cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL);
377 debug_printf (2, " Caching to %s\n", cache_dir);
378 cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
379 g_free (cache_dir);
380
381 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
382 SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[AUTH_PROXY],
383 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
384 SOUP_SESSION_ADD_FEATURE, cache,
385 NULL);
386 g_signal_connect (session, "authenticate",
387 G_CALLBACK (authenticate), NULL);
388
389 url = g_strconcat (HTTP_SERVER, "/Basic/realm1/", NULL);
390
391 debug_printf (1, " GET %s via %s (from network)\n", url, proxy_names[AUTH_PROXY]);
392 do_proxy_auth_request (url, session, TRUE);
393 soup_cache_flush (cache);
394
395 debug_printf (1, " GET %s via %s (from cache)\n", url, proxy_names[AUTH_PROXY]);
396 do_proxy_auth_request (url, session, FALSE);
397
398 g_free (url);
399 soup_test_session_abort_unref (session);
400 g_object_unref (cache);
401 }
402
403 int
main(int argc,char ** argv)404 main (int argc, char **argv)
405 {
406 SoupServer *server;
407 SoupURI *base_uri;
408 char *path;
409 int i, ret;
410
411 test_init (argc, argv, NULL);
412 apache_init ();
413
414 for (i = 0; i < 3; i++) {
415 proxy_resolvers[i] =
416 g_simple_proxy_resolver_new (proxies[i], (char **) ignore_hosts);
417 }
418
419 server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
420 soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
421 base_uri = soup_test_server_get_uri (server, "http", NULL);
422
423 for (i = 0; i < ntests; i++) {
424 path = g_strdup_printf ("/proxy/async/%s", tests[i].explanation);
425 g_test_add_data_func (path, &tests[i], do_async_proxy_test);
426 g_free (path);
427 }
428 for (i = 0; i < ntests; i++) {
429 path = g_strdup_printf ("/proxy/sync/%s", tests[i].explanation);
430 g_test_add_data_func (path, &tests[i], do_sync_proxy_test);
431 g_free (path);
432 }
433
434 g_test_add_data_func ("/proxy/fragment", base_uri, do_proxy_fragment_test);
435 g_test_add_func ("/proxy/redirect", do_proxy_redirect_test);
436 g_test_add_func ("/proxy/auth-cache", do_proxy_auth_cache_test);
437
438 ret = g_test_run ();
439
440 soup_uri_free (base_uri);
441 soup_test_server_quit_unref (server);
442 for (i = 0; i < 3; i++)
443 g_object_unref (proxy_resolvers[i]);
444
445 test_cleanup ();
446 return ret;
447 }
448