1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright 2012 Red Hat, Inc.
4  */
5 
6 #include "test-utils.h"
7 
8 static void
server_callback(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)9 server_callback (SoupServer *server, SoupMessage *msg,
10 		 const char *path, GHashTable *query,
11 		 SoupClientContext *context, gpointer data)
12 {
13 	const char *last_modified, *etag;
14 	const char *header;
15 	guint status = SOUP_STATUS_OK;
16 
17 	if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
18 		soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
19 		return;
20 	}
21 
22 	header = soup_message_headers_get_one (msg->request_headers,
23 					       "Test-Set-Expires");
24 	if (header) {
25 		soup_message_headers_append (msg->response_headers,
26 					     "Expires",
27 					     header);
28 	}
29 
30 	header = soup_message_headers_get_one (msg->request_headers,
31 					       "Test-Set-Cache-Control");
32 	if (header) {
33 		soup_message_headers_append (msg->response_headers,
34 					     "Cache-Control",
35 					     header);
36 	}
37 
38 	last_modified = soup_message_headers_get_one (msg->request_headers,
39 						      "Test-Set-Last-Modified");
40 	if (last_modified) {
41 		soup_message_headers_append (msg->response_headers,
42 					     "Last-Modified",
43 					     last_modified);
44 	}
45 
46 	etag = soup_message_headers_get_one (msg->request_headers,
47 					     "Test-Set-ETag");
48 	if (etag) {
49 		soup_message_headers_append (msg->response_headers,
50 					     "ETag",
51 					     etag);
52 	}
53 
54 
55 	header = soup_message_headers_get_one (msg->request_headers,
56 					       "If-Modified-Since");
57 	if (header && last_modified) {
58 		SoupDate *date;
59 		time_t lastmod, check;
60 
61 		date = soup_date_new_from_string (last_modified);
62 		lastmod = soup_date_to_time_t (date);
63 		soup_date_free (date);
64 
65 		date = soup_date_new_from_string (header);
66 		check = soup_date_to_time_t (date);
67 		soup_date_free (date);
68 
69 		if (lastmod <= check)
70 			status = SOUP_STATUS_NOT_MODIFIED;
71 	}
72 
73 	header = soup_message_headers_get_one (msg->request_headers,
74 					       "If-None-Match");
75 	if (header && etag) {
76 		if (!strcmp (header, etag))
77 			status = SOUP_STATUS_NOT_MODIFIED;
78 	}
79 
80 	header = soup_message_headers_get_one (msg->request_headers,
81 					       "Test-Set-My-Header");
82 	if (header) {
83 		soup_message_headers_append (msg->response_headers,
84 					     "My-Header",
85 					     header);
86 	}
87 
88 	if (status == SOUP_STATUS_OK) {
89 		GChecksum *sum;
90 		const char *body;
91 
92 		sum = g_checksum_new (G_CHECKSUM_SHA256);
93 		g_checksum_update (sum, (guchar *)path, strlen (path));
94 		if (last_modified)
95 			g_checksum_update (sum, (guchar *)last_modified, strlen (last_modified));
96 		if (etag)
97 			g_checksum_update (sum, (guchar *)etag, strlen (etag));
98 		body = g_checksum_get_string (sum);
99 		soup_message_set_response (msg, "text/plain",
100 					   SOUP_MEMORY_COPY,
101 					   body, strlen (body) + 1);
102 		g_checksum_free (sum);
103 	}
104 	soup_message_set_status (msg, status);
105 }
106 
107 static gboolean
is_network_stream(GInputStream * stream)108 is_network_stream (GInputStream *stream)
109 {
110 	while (G_IS_FILTER_INPUT_STREAM (stream))
111 		stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
112 
113 	return !G_IS_FILE_INPUT_STREAM (stream);
114 }
115 
116 static char *do_request (SoupSession        *session,
117 			 SoupURI            *base_uri,
118 			 const char         *method,
119 			 const char         *path,
120 			 SoupMessageHeaders *response_headers,
121 			 ...) G_GNUC_NULL_TERMINATED;
122 
123 static gboolean last_request_hit_network;
124 static gboolean last_request_validated;
125 static gboolean last_request_unqueued;
126 static guint cancelled_requests;
127 
128 static void
copy_headers(const char * name,const char * value,gpointer user_data)129 copy_headers (const char         *name,
130 	      const char         *value,
131 	      gpointer            user_data)
132 {
133 	SoupMessageHeaders *headers = (SoupMessageHeaders *) user_data;
134 	soup_message_headers_append (headers, name, value);
135 }
136 
137 static char *
do_request(SoupSession * session,SoupURI * base_uri,const char * method,const char * path,SoupMessageHeaders * response_headers,...)138 do_request (SoupSession        *session,
139 	    SoupURI            *base_uri,
140 	    const char         *method,
141 	    const char         *path,
142 	    SoupMessageHeaders *response_headers,
143 	    ...)
144 {
145 	SoupRequestHTTP *req;
146 	SoupMessage *msg;
147 	GInputStream *stream;
148 	SoupURI *uri;
149 	va_list ap;
150 	const char *header, *value;
151 	char buf[256];
152 	gsize nread;
153 	GError *error = NULL;
154 
155 	last_request_validated = last_request_hit_network = FALSE;
156 	last_request_unqueued = FALSE;
157 
158 	uri = soup_uri_new_with_base (base_uri, path);
159 	req = soup_session_request_http_uri (session, method, uri, NULL);
160 	soup_uri_free (uri);
161 	msg = soup_request_http_get_message (req);
162 
163 	va_start (ap, response_headers);
164 	while ((header = va_arg (ap, const char *))) {
165 		value = va_arg (ap, const char *);
166 		soup_message_headers_append (msg->request_headers,
167 					     header, value);
168 	}
169 	va_end (ap);
170 
171 	stream = soup_test_request_send (SOUP_REQUEST (req), NULL, 0, &error);
172 	if (!stream) {
173 		debug_printf (1, "    could not send request: %s\n",
174 			      error->message);
175 		g_error_free (error);
176 		g_object_unref (req);
177 		g_object_unref (msg);
178 		return NULL;
179 	}
180 
181 	if (response_headers)
182 		soup_message_headers_foreach (msg->response_headers, copy_headers, response_headers);
183 
184 	g_object_unref (msg);
185 
186 	if (last_request_validated)
187 		last_request_unqueued = FALSE;
188 	else
189 		soup_test_assert (!last_request_unqueued,
190 				  "Request unqueued before finishing");
191 
192 	last_request_hit_network = is_network_stream (stream);
193 
194 	g_input_stream_read_all (stream, buf, sizeof (buf), &nread,
195 				 NULL, &error);
196 	if (error) {
197 		debug_printf (1, "    could not read response: %s\n",
198 			      error->message);
199 		g_clear_error (&error);
200 	}
201 	soup_test_request_close_stream (SOUP_REQUEST (req), stream,
202 					NULL, &error);
203 	if (error) {
204 		debug_printf (1, "    could not close stream: %s\n",
205 			      error->message);
206 		g_clear_error (&error);
207 	}
208 	g_object_unref (stream);
209 	g_object_unref (req);
210 
211 	/* Cache writes are G_PRIORITY_LOW, so they won't have happened yet... */
212 	soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE));
213 
214 	return nread ? g_memdup (buf, nread) : g_strdup ("");
215 }
216 
217 static void
do_request_with_cancel(SoupSession * session,SoupURI * base_uri,const char * method,const char * path,SoupTestRequestFlags flags)218 do_request_with_cancel (SoupSession          *session,
219 			SoupURI              *base_uri,
220 			const char           *method,
221 			const char           *path,
222 			SoupTestRequestFlags  flags)
223 {
224 	SoupRequestHTTP *req;
225 	GInputStream *stream;
226 	SoupURI *uri;
227 	GError *error = NULL;
228 	GCancellable *cancellable;
229 
230 	last_request_validated = last_request_hit_network = last_request_unqueued = FALSE;
231 	cancelled_requests = 0;
232 
233 	uri = soup_uri_new_with_base (base_uri, path);
234 	req = soup_session_request_http_uri (session, method, uri, NULL);
235 	soup_uri_free (uri);
236 	cancellable = flags & SOUP_TEST_REQUEST_CANCEL_CANCELLABLE ? g_cancellable_new () : NULL;
237 	stream = soup_test_request_send (SOUP_REQUEST (req), cancellable, flags, &error);
238 	if (stream) {
239 		debug_printf (1, "    could not cancel the request\n");
240 		g_object_unref (stream);
241 		g_object_unref (req);
242 		return;
243 	} else
244 		g_clear_error (&error);
245 
246 	g_clear_object (&cancellable);
247 	g_clear_object (&stream);
248 	g_clear_object (&req);
249 
250 	soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE));
251 }
252 
253 static void
message_starting(SoupMessage * msg,gpointer data)254 message_starting (SoupMessage *msg, gpointer data)
255 {
256 	if (soup_message_headers_get_one (msg->request_headers,
257 					  "If-Modified-Since") ||
258 	    soup_message_headers_get_one (msg->request_headers,
259 					  "If-None-Match")) {
260 		debug_printf (2, "    Conditional request for %s\n",
261 			      soup_message_get_uri (msg)->path);
262 		last_request_validated = TRUE;
263 	}
264 }
265 
266 static void
request_queued(SoupSession * session,SoupMessage * msg,gpointer data)267 request_queued (SoupSession *session, SoupMessage *msg,
268 		gpointer data)
269 {
270 	g_signal_connect (msg, "starting",
271 			  G_CALLBACK (message_starting),
272 			  data);
273 }
274 
275 static void
request_unqueued(SoupSession * session,SoupMessage * msg,gpointer data)276 request_unqueued (SoupSession *session, SoupMessage *msg,
277 		  gpointer data)
278 {
279 	if (msg->status_code == SOUP_STATUS_CANCELLED)
280 		cancelled_requests++;
281 	last_request_unqueued = TRUE;
282 }
283 
284 static void
do_basics_test(gconstpointer data)285 do_basics_test (gconstpointer data)
286 {
287 	SoupURI *base_uri = (SoupURI *)data;
288 	SoupSession *session;
289 	SoupCache *cache;
290 	char *cache_dir;
291 	char *body1, *body2, *body3, *body4, *body5, *cmp;
292 
293 	cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL);
294 	debug_printf (2, "  Caching to %s\n", cache_dir);
295 	cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
296 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
297 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
298 					 SOUP_SESSION_ADD_FEATURE, cache,
299 					 NULL);
300 
301 	g_signal_connect (session, "request-queued",
302 			  G_CALLBACK (request_queued), NULL);
303 	g_signal_connect (session, "request-unqueued",
304 			  G_CALLBACK (request_unqueued), NULL);
305 
306 	debug_printf (2, "  Initial requests\n");
307 	body1 = do_request (session, base_uri, "GET", "/1", NULL,
308 			    "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
309 			    NULL);
310 	body2 = do_request (session, base_uri, "GET", "/2", NULL,
311 			    "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
312 			    "Test-Set-Cache-Control", "must-revalidate",
313 			    NULL);
314 	body3 = do_request (session, base_uri, "GET", "/3", NULL,
315 			    "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
316 			    "Test-Set-Expires", "Sat, 02 Jan 2011 00:00:00 GMT",
317 			    "Test-Set-Cache-Control", "must-revalidate",
318 			    NULL);
319 	body4 = do_request (session, base_uri, "GET", "/4", NULL,
320 			    "Test-Set-ETag", "\"abcdefg\"",
321 			    "Test-Set-Cache-Control", "must-revalidate",
322 			    NULL);
323 	body5 = do_request (session, base_uri, "GET", "/5", NULL,
324 			    "Test-Set-Cache-Control", "no-cache",
325 			    NULL);
326 
327 
328 	/* Resource with future Expires should have been cached */
329 	debug_printf (1, "  Fresh cached resource\n");
330 	cmp = do_request (session, base_uri, "GET", "/1", NULL,
331 			  NULL);
332 	soup_test_assert (!last_request_hit_network,
333 			  "Request for /1 not filled from cache");
334 	soup_test_assert (last_request_unqueued,
335 			  "Cached resource /1 not unqueued");
336 	g_assert_cmpstr (body1, ==, cmp);
337 	g_free (cmp);
338 
339 
340 	/* Resource with long-ago Last-Modified should have been cached */
341 	debug_printf (1, "  Heuristically-fresh cached resource\n");
342 	cmp = do_request (session, base_uri, "GET", "/2", NULL,
343 			  NULL);
344 	/* Not validated even if it has must-revalidate, because it hasn't expired */
345 	soup_test_assert (!last_request_validated,
346 			  "Request for /2 was validated");
347 	soup_test_assert (!last_request_hit_network,
348 			  "Request for /2 not filled from cache");
349 	soup_test_assert (last_request_unqueued,
350 			  "Cached resource /2 not unqueued");
351 	g_assert_cmpstr (body2, ==, cmp);
352 	g_free (cmp);
353 
354 
355 	/* Adding a query string should bypass the cache but not invalidate it */
356 	debug_printf (1, "  Fresh cached resource with a query\n");
357 	cmp = do_request (session, base_uri, "GET", "/1?attr=value", NULL,
358 			  NULL);
359 	soup_test_assert (last_request_hit_network,
360 			  "Request for /1?attr=value filled from cache");
361 	soup_test_assert (last_request_unqueued,
362 			  "Cached resource /1?attr=value not unqueued");
363 	g_free (cmp);
364 	debug_printf (2, "  Second request\n");
365 	cmp = do_request (session, base_uri, "GET", "/1", NULL,
366 			  NULL);
367 	soup_test_assert (!last_request_hit_network,
368 			  "Second request for /1 not filled from cache");
369 	soup_test_assert (last_request_unqueued,
370 			  "Request for /1 not unqueued");
371 	g_assert_cmpstr (body1, ==, cmp);
372 	g_free (cmp);
373 
374 
375 	/* Expired + must-revalidate causes a conditional request */
376 	debug_printf (1, "  Unchanged must-revalidate resource w/ Last-Modified\n");
377 	cmp = do_request (session, base_uri, "GET", "/3", NULL,
378 			  "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
379 			  "Test-Set-Expires", "Sat, 02 Jan 2011 00:00:00 GMT",
380 			  "Test-Set-Cache-Control", "must-revalidate",
381 			  NULL);
382 	soup_test_assert (last_request_validated,
383 			  "Request for /3 not validated");
384 	soup_test_assert (!last_request_hit_network,
385 			  "Request for /3 not filled from cache");
386 	soup_test_assert (last_request_unqueued,
387 			  "Cached resource /3 not unqueued");
388 	g_assert_cmpstr (body3, ==, cmp);
389 	g_free (cmp);
390 
391 
392 	/* Validation failure should update cache */
393 	debug_printf (1, "  Changed must-revalidate resource w/ Last-Modified\n");
394 	cmp = do_request (session, base_uri, "GET", "/3", NULL,
395 			  "Test-Set-Last-Modified", "Sat, 02 Jan 2010 00:00:00 GMT",
396 			  "Test-Set-Expires", "Sat, 02 Jan 2011 00:00:00 GMT",
397 			  "Test-Set-Cache-Control", "must-revalidate",
398 			  NULL);
399 	soup_test_assert (last_request_validated,
400 			  "Request for /3 not validated");
401 	soup_test_assert (last_request_hit_network,
402 			  "Request for /3 filled from cache");
403 	soup_test_assert (last_request_unqueued,
404 			  "Request for /3 not unqueued");
405 	g_assert_cmpstr (body3, !=, cmp);
406 	g_free (cmp);
407 
408 	debug_printf (2, "  Second request\n");
409 	cmp = do_request (session, base_uri, "GET", "/3", NULL,
410 			  "Test-Set-Last-Modified", "Sat, 02 Jan 2010 00:00:00 GMT",
411 			  "Test-Set-Cache-Control", "must-revalidate",
412 			  NULL);
413 	soup_test_assert (last_request_validated,
414 			  "Second request for /3 not validated");
415 	soup_test_assert (!last_request_hit_network,
416 			  "Second request for /3 not filled from cache");
417 	soup_test_assert (last_request_unqueued,
418 			  "Cached resource /3 not unqueued");
419 	g_assert_cmpstr (body3, !=, cmp);
420 	g_free (cmp);
421 
422 	/* ETag + must-revalidate causes a conditional request */
423 	debug_printf (1, "  Unchanged must-revalidate resource w/ ETag\n");
424 	cmp = do_request (session, base_uri, "GET", "/4", NULL,
425 			  "Test-Set-ETag", "\"abcdefg\"",
426 			  NULL);
427 	soup_test_assert (last_request_validated,
428 			  "Request for /4 not validated");
429 	soup_test_assert (!last_request_hit_network,
430 			  "Request for /4 not filled from cache");
431 	soup_test_assert (last_request_unqueued,
432 			  "Cached resource /4 not unqueued");
433 	g_assert_cmpstr (body4, ==, cmp);
434 	g_free (cmp);
435 
436 
437 	/* Cache-Control: no-cache prevents caching */
438 	debug_printf (1, "  Uncacheable resource\n");
439 	cmp = do_request (session, base_uri, "GET", "/5", NULL,
440 			  "Test-Set-Cache-Control", "no-cache",
441 			  NULL);
442 	soup_test_assert (last_request_hit_network,
443 			  "Request for /5 filled from cache");
444 	soup_test_assert (last_request_unqueued,
445 			  "Request for /5 not unqueued");
446 	g_assert_cmpstr (body5, ==, cmp);
447 	g_free (cmp);
448 
449 
450 	/* PUT to a URI invalidates the cache entry */
451 	debug_printf (1, "  Invalidating and re-requesting a cached resource\n");
452 	cmp = do_request (session, base_uri, "PUT", "/1", NULL,
453 			  NULL);
454 	soup_test_assert (last_request_hit_network,
455 			  "PUT filled from cache");
456 	g_free (cmp);
457 	cmp = do_request (session, base_uri, "GET", "/1", NULL,
458 			  NULL);
459 	soup_test_assert (last_request_hit_network,
460 			  "PUT failed to invalidate cache entry");
461 	g_assert_true (last_request_hit_network);
462 	g_free (cmp);
463 
464 
465 	soup_test_session_abort_unref (session);
466 	g_object_unref (cache);
467 
468 	g_free (cache_dir);
469 	g_free (body1);
470 	g_free (body2);
471 	g_free (body3);
472 	g_free (body4);
473 	g_free (body5);
474 }
475 
476 static void
do_cancel_test(gconstpointer data)477 do_cancel_test (gconstpointer data)
478 {
479 	SoupURI *base_uri = (SoupURI *)data;
480 	SoupSession *session;
481 	SoupCache *cache;
482 	char *cache_dir;
483 	char *body1, *body2;
484 	guint flags;
485 
486 	g_test_bug ("692310");
487 
488 	cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL);
489 	debug_printf (2, "  Caching to %s\n", cache_dir);
490 	cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
491 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
492 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
493 					 SOUP_SESSION_ADD_FEATURE, cache,
494 					 NULL);
495 	g_signal_connect (session, "request-unqueued",
496 			  G_CALLBACK (request_unqueued), NULL);
497 
498 	debug_printf (2, "  Initial requests\n");
499 	body1 = do_request (session, base_uri, "GET", "/1", NULL,
500 			    "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
501 			    NULL);
502 	body2 = do_request (session, base_uri, "GET", "/2", NULL,
503 			    "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
504 			    "Test-Set-Expires", "Fri, 01 Jan 2011 00:00:00 GMT",
505 			    "Test-Set-Cache-Control", "must-revalidate",
506 			    NULL);
507 
508 	/* Check that messages are correctly processed on cancellations. */
509 	debug_printf (1, "  Cancel fresh resource with soup_session_message_cancel()\n");
510 	flags = SOUP_TEST_REQUEST_CANCEL_MESSAGE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE;
511 	do_request_with_cancel (session, base_uri, "GET", "/1", flags);
512 	g_assert_cmpint (cancelled_requests, ==, 1);
513 	soup_test_assert (last_request_unqueued,
514 			  "Cancelled request /1 not unqueued");
515 
516 	debug_printf (1, "  Cancel fresh resource with g_cancellable_cancel()\n");
517 	flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE;
518 	do_request_with_cancel (session, base_uri, "GET", "/1", flags);
519 	g_assert_cmpint (cancelled_requests, ==, 1);
520 	soup_test_assert (last_request_unqueued,
521 			  "Cancelled request /1 not unqueued");
522 
523 	soup_test_session_abort_unref (session);
524 
525 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
526 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
527 					 SOUP_SESSION_ADD_FEATURE, cache,
528 					 NULL);
529 	g_signal_connect (session, "request-unqueued",
530 			  G_CALLBACK (request_unqueued), NULL);
531 
532 	/* Check that messages are correctly processed on cancellations. */
533 	debug_printf (1, "  Cancel a revalidating resource with soup_session_message_cancel()\n");
534 	flags = SOUP_TEST_REQUEST_CANCEL_MESSAGE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE;
535 	do_request_with_cancel (session, base_uri, "GET", "/2", flags);
536 	g_assert_cmpint (cancelled_requests, ==, 2);
537 	soup_test_assert (last_request_unqueued,
538 			  "Cancelled request /2 not unqueued");
539 
540 	debug_printf (1, "  Cancel a revalidating resource with g_cancellable_cancel()\n");
541 	flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE;
542 	do_request_with_cancel (session, base_uri, "GET", "/2", flags);
543 	g_assert_cmpint (cancelled_requests, ==, 2);
544 	soup_test_assert (last_request_unqueued,
545 			  "Cancelled request /2 not unqueued");
546 
547 	soup_test_session_abort_unref (session);
548 
549 	g_object_unref (cache);
550 	g_free (cache_dir);
551 	g_free (body1);
552 	g_free (body2);
553 }
554 
555 static gboolean
unref_stream(gpointer stream)556 unref_stream (gpointer stream)
557 {
558 	g_object_unref (stream);
559 	return FALSE;
560 }
561 
562 static void
base_stream_unreffed(gpointer loop,GObject * ex_base_stream)563 base_stream_unreffed (gpointer loop, GObject *ex_base_stream)
564 {
565 	g_main_loop_quit (loop);
566 }
567 
568 static void
do_refcounting_test(gconstpointer data)569 do_refcounting_test (gconstpointer data)
570 {
571 	SoupURI *base_uri = (SoupURI *)data;
572 	SoupSession *session;
573 	SoupCache *cache;
574 	char *cache_dir;
575 	SoupRequestHTTP *req;
576 	GInputStream *stream, *base_stream;
577 	SoupURI *uri;
578 	GError *error = NULL;
579 	guint flags;
580 	GMainLoop *loop;
581 
582 	g_test_bug ("682527");
583 
584 	cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL);
585 	debug_printf (2, "  Caching to %s\n", cache_dir);
586 	cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
587 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
588 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
589 					 SOUP_SESSION_ADD_FEATURE, cache,
590 					 NULL);
591 
592 	last_request_validated = last_request_hit_network = FALSE;
593 	cancelled_requests = 0;
594 
595 	uri = soup_uri_new_with_base (base_uri, "/1");
596 	req = soup_session_request_http_uri (session, "GET", uri, NULL);
597 	soup_uri_free (uri);
598 
599 	flags = SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH | SOUP_TEST_REQUEST_CANCEL_MESSAGE;
600 	stream = soup_test_request_send (SOUP_REQUEST (req), NULL, flags, &error);
601 	if (!stream) {
602 		debug_printf (1, "    could not send request: %s\n",
603 			      error->message);
604 		g_error_free (error);
605 		g_object_unref (req);
606 		return;
607 	}
608 	g_object_unref (req);
609 
610 	base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (stream));
611 
612 	debug_printf (1, " Checking that the base stream is properly unref'ed\n");
613 	loop = g_main_loop_new (NULL, FALSE);
614 	g_object_weak_ref (G_OBJECT (base_stream), base_stream_unreffed, loop);
615 	g_idle_add (unref_stream, stream);
616 	g_main_loop_run (loop);
617 	g_main_loop_unref (loop);
618 
619 	soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE));
620 
621 	soup_test_session_abort_unref (session);
622 
623 	g_object_unref (cache);
624 	g_free (cache_dir);
625 }
626 
627 static void
do_headers_test(gconstpointer data)628 do_headers_test (gconstpointer data)
629 {
630 	SoupURI *base_uri = (SoupURI *)data;
631 	SoupSession *session;
632 	SoupMessageHeaders *headers;
633 	SoupCache *cache;
634 	char *cache_dir;
635 	char *body1, *cmp;
636 	const char *header_value;
637 
638 	cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL);
639 	debug_printf (2, "  Caching to %s\n", cache_dir);
640 	cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
641 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
642 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
643 					 SOUP_SESSION_ADD_FEATURE, cache,
644 					 NULL);
645 
646 	g_signal_connect (session, "request-queued",
647 			  G_CALLBACK (request_queued), NULL);
648 
649 	debug_printf (2, "  Initial requests\n");
650 	body1 = do_request (session, base_uri, "GET", "/1", NULL,
651 			    "Test-Set-Last-Modified", "Fri, 01 Jan 2100 00:00:00 GMT",
652 			    "Test-Set-My-Header", "My header value",
653 			    NULL);
654 
655 	/* My-Header new value should be updated in cache */
656 	debug_printf (2, "  Fresh cached resource which updates My-Header\n");
657 	cmp = do_request (session, base_uri, "GET", "/1", NULL,
658 			  "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
659 			  "Test-Set-My-Header", "My header NEW value",
660 			  NULL);
661 	soup_test_assert (last_request_validated,
662 			  "Request for /1 not validated");
663 	soup_test_assert (!last_request_hit_network,
664 			  "Request for /1 not filled from cache");
665 	g_free (cmp);
666 
667 	/* Check that cache returns the updated header */
668 	debug_printf (2, "  Fresh cached resource with new value for My-Header\n");
669 	headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
670 	cmp = do_request (session, base_uri, "GET", "/1", headers,
671 			  "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
672 			  NULL);
673 	soup_test_assert (!last_request_hit_network,
674 			  "Request for /1 not filled from cache");
675 	g_free (cmp);
676 
677 	header_value = soup_message_headers_get_list (headers, "My-Header");
678 	g_assert_cmpstr (header_value, ==, "My header NEW value");
679 	soup_message_headers_free (headers);
680 
681 	soup_test_session_abort_unref (session);
682 	g_object_unref (cache);
683 
684 	g_free (cache_dir);
685 	g_free (body1);
686 }
687 
688 static guint
count_cached_resources_in_dir(const char * cache_dir)689 count_cached_resources_in_dir (const char *cache_dir)
690 {
691 	GDir *dir;
692 	const char *name;
693 	guint retval = 0;
694 
695 	dir = g_dir_open (cache_dir, 0, NULL);
696 	while ((name = g_dir_read_name (dir))) {
697 		if (g_str_has_prefix (name, "soup."))
698 			continue;
699 
700 		retval++;
701 	}
702 	g_dir_close (dir);
703 
704 	return retval;
705 }
706 
707 static void
do_leaks_test(gconstpointer data)708 do_leaks_test (gconstpointer data)
709 {
710 	SoupURI *base_uri = (SoupURI *)data;
711 	SoupSession *session;
712 	SoupCache *cache;
713 	char *cache_dir;
714 	char *body;
715 
716 	cache_dir = g_dir_make_tmp ("cache-test-XXXXXX", NULL);
717 	debug_printf (2, "  Caching to %s\n", cache_dir);
718 	cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
719 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
720 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
721 					 SOUP_SESSION_ADD_FEATURE, cache,
722 					 NULL);
723 
724 	debug_printf (2, "  Initial requests\n");
725 	body = do_request (session, base_uri, "GET", "/1", NULL,
726 			   "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
727 			   NULL);
728 	g_free (body);
729 	body = do_request (session, base_uri, "GET", "/2", NULL,
730 			   "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
731 			   NULL);
732 	g_free (body);
733 	body = do_request (session, base_uri, "GET", "/3", NULL,
734 			   "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
735 			   NULL);
736 	g_free (body);
737 
738 	debug_printf (2, "  Dumping the cache\n");
739 	soup_cache_dump (cache);
740 
741 	g_assert_cmpuint (count_cached_resources_in_dir (cache_dir), ==, 3);
742 
743 	body = do_request (session, base_uri, "GET", "/4", NULL,
744 			   "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
745 			   NULL);
746 	g_free (body);
747 	body = do_request (session, base_uri, "GET", "/5", NULL,
748 			   "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
749 			   NULL);
750 	g_free (body);
751 
752 	/* Destroy the cache without dumping the last two resources */
753 	soup_test_session_abort_unref (session);
754 	g_object_unref (cache);
755 
756 	cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
757 
758 	debug_printf (2, "  Loading the cache\n");
759 	g_assert_cmpuint (count_cached_resources_in_dir (cache_dir), ==, 5);
760 	soup_cache_load (cache);
761 	g_assert_cmpuint (count_cached_resources_in_dir (cache_dir), ==, 3);
762 
763 	g_object_unref (cache);
764 	g_free (cache_dir);
765 }
766 
767 int
main(int argc,char ** argv)768 main (int argc, char **argv)
769 {
770 	SoupServer *server;
771 	SoupURI *base_uri;
772 	int ret;
773 
774 	test_init (argc, argv, NULL);
775 
776 	server = soup_test_server_new (TRUE);
777 	soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
778 	base_uri = soup_test_server_get_uri (server, "http", NULL);
779 
780 	g_test_add_data_func ("/cache/basics", base_uri, do_basics_test);
781 	g_test_add_data_func ("/cache/cancellation", base_uri, do_cancel_test);
782 	g_test_add_data_func ("/cache/refcounting", base_uri, do_refcounting_test);
783 	g_test_add_data_func ("/cache/headers", base_uri, do_headers_test);
784 	g_test_add_data_func ("/cache/leaks", base_uri, do_leaks_test);
785 
786 	ret = g_test_run ();
787 
788 	soup_uri_free (base_uri);
789 	soup_test_server_quit_unref (server);
790 
791 	test_cleanup ();
792 	return ret;
793 }
794