• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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