tests: add cache-test
[platform/upstream/libsoup.git] / tests / coding-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007 Red Hat, Inc.
4  * Copyright (C) 2011 Igalia, S.L.
5  */
6
7 #include "test-utils.h"
8
9 SoupServer *server;
10 SoupURI *base_uri;
11
12 static void
13 server_callback (SoupServer *server, SoupMessage *msg,
14                  const char *path, GHashTable *query,
15                  SoupClientContext *context, gpointer data)
16 {
17         const char *accept_encoding, *options;
18         GSList *codings;
19         char *file = NULL, *contents;
20         gsize length;
21
22         options = soup_message_headers_get_one (msg->request_headers,
23                                                 "X-Test-Options");
24         if (!options)
25                 options = "";
26
27         accept_encoding = soup_message_headers_get_list (msg->request_headers,
28                                                          "Accept-Encoding");
29         if (accept_encoding && !soup_header_contains (options, "force-encode"))
30                 codings = soup_header_parse_quality_list (accept_encoding, NULL);
31         else
32                 codings = NULL;
33
34         if (codings) {
35                 gboolean claim_deflate, claim_gzip;
36                 const char *file_path = NULL, *encoding = NULL;
37
38                 claim_deflate = g_slist_find_custom (codings, "deflate", (GCompareFunc)g_ascii_strcasecmp) != NULL;
39                 claim_gzip = g_slist_find_custom (codings, "gzip", (GCompareFunc)g_ascii_strcasecmp) != NULL;
40
41                 if (claim_gzip && (!claim_deflate ||
42                                    (!soup_header_contains (options, "prefer-deflate-zlib") &&
43                                     !soup_header_contains (options, "prefer-deflate-raw")))) {
44                         file_path = SRCDIR "/resources%s.gz";
45                         encoding = "gzip";
46                 } else if (claim_deflate) {
47                         if (soup_header_contains (options, "prefer-deflate-raw")) {
48                                 file_path = SRCDIR "/resources%s.raw";
49                                 encoding = "deflate";
50                         } else {
51                                 file_path = SRCDIR "/resources%s.zlib";
52                                 encoding = "deflate";
53                         }
54                 }
55                 if (file_path && encoding) {
56                         file = g_strdup_printf (file_path, path);
57                         if (g_file_test (file, G_FILE_TEST_EXISTS)) {
58                                 soup_message_headers_append (msg->response_headers,
59                                                              "Content-Encoding",
60                                                              encoding);
61                         } else {
62                                 g_free (file);
63                                 file = NULL;
64                         }
65                 }
66         }
67
68         soup_header_free_list (codings);
69
70         if (!file)
71                 file = g_strdup_printf (SRCDIR "/resources%s", path);
72         if (!g_file_get_contents (file, &contents, &length, NULL)) {
73                 /* If path.gz exists but can't be read, we'll send back
74                  * the error with "Content-Encoding: gzip" but there's
75                  * no body, so, eh.
76                  */
77                 g_free (file);
78                 soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
79                 return;
80         }
81         g_free (file);
82
83         if (soup_header_contains (options, "force-encode")) {
84                 const gchar *encoding = "gzip";
85
86                 if (soup_header_contains (options, "prefer-deflate-zlib") ||
87                     soup_header_contains (options, "prefer-deflate-raw"))
88                         encoding = "deflate";
89
90                 soup_message_headers_replace (msg->response_headers,
91                                               "Content-Encoding",
92                                               encoding);
93         }
94
95         /* Content-Type matches the "real" format, not the sent format */
96         if (g_str_has_suffix (path, ".gz")) {
97                 soup_message_headers_append (msg->response_headers,
98                                              "Content-Type",
99                                              "application/gzip");
100         } else {
101                 soup_message_headers_append (msg->response_headers,
102                                              "Content-Type",
103                                              "text/plain");
104         }
105
106         soup_message_set_status (msg, SOUP_STATUS_OK);
107         soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CHUNKED);
108
109         if (!soup_header_contains (options, "empty")) {
110                 soup_message_body_append (msg->response_body,
111                                           SOUP_MEMORY_TAKE, contents, length);
112         } else
113                 g_free (contents);
114
115         if (soup_header_contains (options, "trailing-junk")) {
116                 soup_message_body_append (msg->response_body, SOUP_MEMORY_COPY,
117                                           options, strlen (options));
118         }
119         soup_message_body_complete (msg->response_body);
120 }
121
122 typedef enum {
123         NO_CHECK,
124         EXPECT_DECODED,
125         EXPECT_NOT_DECODED
126 } MessageContentStatus;
127
128 static void
129 check_response (SoupMessage *msg,
130                 const char *expected_encoding,
131                 const char *expected_content_type,
132                 MessageContentStatus status)
133 {
134         const char *coding, *type;
135
136         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
137                 debug_printf (1, "    Unexpected status %d %s\n",
138                               msg->status_code, msg->reason_phrase);
139                 errors++;
140         }
141
142         coding = soup_message_headers_get_one (msg->response_headers, "Content-Encoding");
143         if (expected_encoding) {
144                 if (!coding || g_ascii_strcasecmp (coding, expected_encoding) != 0) {
145                         debug_printf (1, "    Unexpected Content-Encoding: %s\n",
146                                       coding ? coding : "(none)");
147                         errors++;
148                 }
149         } else {
150                 if (coding) {
151                         debug_printf (1, "    Unexpected Content-Encoding: %s\n",
152                                       coding);
153                         errors++;
154                 }
155         }
156
157         if (status != NO_CHECK) {
158                 if (status == EXPECT_DECODED) {
159                         if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_CONTENT_DECODED)) {
160                                 debug_printf (1, "    SOUP_MESSAGE_CONTENT_DECODED not set!\n");
161                                 errors++;
162                         }
163                 } else {
164                         if (soup_message_get_flags (msg) & SOUP_MESSAGE_CONTENT_DECODED) {
165                                 debug_printf (1, "    SOUP_MESSAGE_CONTENT_DECODED set!\n");
166                                 errors++;
167                         }
168                 }
169         }
170
171         type = soup_message_headers_get_one (msg->response_headers, "Content-Type");
172         if (!type || g_ascii_strcasecmp (type, expected_content_type) != 0) {
173                 debug_printf (1, "    Unexpected Content-Type: %s\n",
174                               type ? type : "(none)");
175                 errors++;
176         }
177 }
178
179 static void
180 check_msg_bodies (SoupMessage *msg1,
181                   SoupMessage *msg2,
182                   const char *msg1_type,
183                   const char *msg2_type)
184 {
185         if (msg1->response_body->length != msg2->response_body->length) {
186                 debug_printf (1, "    Message length mismatch: %lu (%s) vs %lu (%s)\n",
187                               (gulong)msg1->response_body->length,
188                               msg1_type,
189                               (gulong)msg2->response_body->length,
190                               msg2_type);
191                 errors++;
192         } else if (memcmp (msg1->response_body->data,
193                            msg2->response_body->data,
194                            msg1->response_body->length) != 0) {
195                 debug_printf (1, "    Message data mismatch (%s/%s)\n",
196                               msg1_type, msg2_type);
197                 errors++;
198         }
199 }
200
201 static void
202 do_coding_test (void)
203 {
204         SoupSession *session;
205         SoupMessage *msg, *msgz, *msgj, *msge, *msgzl, *msgzlj, *msgzle, *msgzlr, *msgzlre;
206         SoupURI *uri;
207
208         debug_printf (1, "SoupMessage tests\n");
209
210         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
211         uri = soup_uri_new_with_base (base_uri, "/mbox");
212
213         /* Plain text data, no claim */
214         debug_printf (1, "  GET /mbox, plain\n");
215         msg = soup_message_new_from_uri ("GET", uri);
216         soup_session_send_message (session, msg);
217         check_response (msg, NULL, "text/plain", EXPECT_NOT_DECODED);
218
219         /* Plain text data, claim gzip */
220         debug_printf (1, "  GET /mbox, Accept-Encoding: gzip\n");
221         soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
222         msgz = soup_message_new_from_uri ("GET", uri);
223         soup_session_send_message (session, msgz);
224         check_response (msgz, "gzip", "text/plain", EXPECT_DECODED);
225         check_msg_bodies (msg, msgz, "plain", "compressed");
226
227         /* Plain text data, claim gzip w/ junk */
228         debug_printf (1, "  GET /mbox, Accept-Encoding: gzip, plus trailing junk\n");
229         msgj = soup_message_new_from_uri ("GET", uri);
230         soup_message_headers_append (msgj->request_headers,
231                                      "X-Test-Options", "trailing-junk");
232         soup_session_send_message (session, msgj);
233         check_response (msgj, "gzip", "text/plain", EXPECT_DECODED);
234         check_msg_bodies (msg, msgj, "plain", "compressed w/ junk");
235
236         /* Plain text data, claim gzip with server error */
237         debug_printf (1, "  GET /mbox, Accept-Encoding: gzip, with server error\n");
238         msge = soup_message_new_from_uri ("GET", uri);
239         soup_message_headers_append (msge->request_headers,
240                                      "X-Test-Options", "force-encode");
241         soup_session_send_message (session, msge);
242         check_response (msge, "gzip", "text/plain", EXPECT_NOT_DECODED);
243
244         /* Failed content-decoding should have left the body untouched
245          * from what the server sent... which happens to be the
246          * uncompressed data.
247          */
248         check_msg_bodies (msg, msge, "plain", "mis-encoded");
249
250         /* Plain text data, claim deflate */
251         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate\n");
252         msgzl = soup_message_new_from_uri ("GET", uri);
253         soup_message_headers_append (msgzl->request_headers,
254                                      "X-Test-Options", "prefer-deflate-zlib");
255         soup_session_send_message (session, msgzl);
256         check_response (msgzl, "deflate", "text/plain", EXPECT_DECODED);
257         check_msg_bodies (msg, msgzl, "plain", "compressed");
258
259         /* Plain text data, claim deflate w/ junk */
260         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate, plus trailing junk\n");
261         msgzlj = soup_message_new_from_uri ("GET", uri);
262         soup_message_headers_append (msgzlj->request_headers,
263                                      "X-Test-Options", "prefer-deflate-zlib, trailing-junk");
264         soup_session_send_message (session, msgzlj);
265         check_response (msgzlj, "deflate", "text/plain", EXPECT_DECODED);
266         check_msg_bodies (msg, msgzlj, "plain", "compressed w/ junk");
267
268         /* Plain text data, claim deflate with server error */
269         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate, with server error\n");
270         msgzle = soup_message_new_from_uri ("GET", uri);
271         soup_message_headers_append (msgzle->request_headers,
272                                      "X-Test-Options", "force-encode, prefer-deflate-zlib");
273         soup_session_send_message (session, msgzle);
274         check_response (msgzle, "deflate", "text/plain", EXPECT_NOT_DECODED);
275         check_msg_bodies (msg, msgzle, "plain", "mis-encoded");
276
277         /* Plain text data, claim deflate (no zlib headers)*/
278         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate (raw data)\n");
279         msgzlr = soup_message_new_from_uri ("GET", uri);
280         soup_message_headers_append (msgzlr->request_headers,
281                                      "X-Test-Options", "prefer-deflate-raw");
282         soup_session_send_message (session, msgzlr);
283         check_response (msgzlr, "deflate", "text/plain", EXPECT_DECODED);
284         check_msg_bodies (msg, msgzlr, "plain", "compressed");
285
286         /* Plain text data, claim deflate with server error */
287         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate (raw data), with server error\n");
288         msgzlre = soup_message_new_from_uri ("GET", uri);
289         soup_message_headers_append (msgzlre->request_headers,
290                                      "X-Test-Options", "force-encode, prefer-deflate-raw");
291         soup_session_send_message (session, msgzlre);
292         check_response (msgzlre, "deflate", "text/plain", EXPECT_NOT_DECODED);
293         check_msg_bodies (msg, msgzlre, "plain", "mis-encoded");
294
295         g_object_unref (msg);
296         g_object_unref (msgzlre);
297         g_object_unref (msgzlr);
298         g_object_unref (msgzlj);
299         g_object_unref (msgzle);
300         g_object_unref (msgzl);
301         g_object_unref (msgz);
302         g_object_unref (msgj);
303         g_object_unref (msge);
304         soup_uri_free (uri);
305
306         soup_test_session_abort_unref (session);
307 }
308
309 static void
310 read_finished (GObject *stream, GAsyncResult *result, gpointer user_data)
311 {
312         gssize *nread = user_data;
313         GError *error = NULL;
314
315         *nread = g_input_stream_read_finish (G_INPUT_STREAM (stream),
316                                              result, &error);
317         if (error) {
318                 debug_printf (1, "    Error reading: %s\n",
319                               error->message);
320                 g_error_free (error);
321                 errors++;
322         }
323 }
324
325 static GByteArray *
326 do_single_coding_req_test (SoupRequestHTTP *reqh,
327                            const char *expected_encoding,
328                            const char *expected_content_type,
329                            MessageContentStatus status)
330 {
331         GInputStream *stream;
332         SoupMessage *msg;
333         GByteArray *data;
334         guchar buf[1024];
335         gssize nread;
336         GError *error = NULL;
337
338         data = g_byte_array_new ();
339
340         stream = soup_test_request_send (SOUP_REQUEST (reqh), NULL, &error);
341         if (error) {
342                 debug_printf (1, "    Error sending request: %s\n",
343                               error->message);
344                 g_error_free (error);
345                 errors++;
346                 return data;
347         }
348
349         do {
350                 nread = -2;
351                 g_input_stream_read_async (stream, buf, sizeof (buf),
352                                            G_PRIORITY_DEFAULT,
353                                            NULL, read_finished, &nread);
354                 while (nread == -2)
355                         g_main_context_iteration (NULL, TRUE);
356
357                 if (nread > 0)
358                         g_byte_array_append (data, buf, nread);
359         } while (nread > 0);
360
361         soup_test_request_close_stream (SOUP_REQUEST (reqh), stream, NULL, &error);
362         if (error) {
363                 debug_printf (1, "    error closing stream: %s\n",
364                               error->message);
365                 g_error_free (error);
366                 errors++;
367         }
368         g_object_unref (stream);
369
370         msg = soup_request_http_get_message (reqh);
371         check_response (msg, expected_encoding, expected_content_type, status);
372         g_object_unref (msg);
373
374         return data;
375 }
376
377 static void
378 check_req_bodies (GByteArray *body1,
379                   GByteArray *body2,
380                   const char *msg1_type,
381                   const char *msg2_type)
382 {
383         if (body1->len != body2->len) {
384                 debug_printf (1, "    Message length mismatch: %lu (%s) vs %lu (%s)\n",
385                               (gulong)body1->len, msg1_type,
386                               (gulong)body2->len, msg2_type);
387                 errors++;
388         } else if (memcmp (body1->data, body2->data, body1->len) != 0) {
389                 debug_printf (1, "    Message data mismatch (%s/%s)\n",
390                               msg1_type, msg2_type);
391                 errors++;
392         }
393 }
394
395 static void
396 do_coding_req_test (void)
397 {
398         SoupSession *session;
399         SoupRequestHTTP *reqh;
400         SoupURI *uri;
401         GByteArray *plain, *cmp;
402
403         debug_printf (1, "\nSoupRequest tests\n");
404
405         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
406                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
407                                          NULL);
408         uri = soup_uri_new_with_base (base_uri, "/mbox");
409
410         /* Plain text data, no claim */
411         debug_printf (1, "  GET /mbox, plain\n");
412         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
413         plain = do_single_coding_req_test (reqh, NULL, "text/plain", EXPECT_NOT_DECODED);
414         g_object_unref (reqh);
415
416         /* Plain text data, claim gzip */
417         debug_printf (1, "  GET /mbox, Accept-Encoding: gzip\n");
418         soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
419         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
420         cmp = do_single_coding_req_test (reqh, "gzip", "text/plain", EXPECT_DECODED);
421         check_req_bodies (plain, cmp, "plain", "compressed");
422         g_byte_array_free (cmp, TRUE);
423         g_object_unref (reqh);
424
425         /* Plain text data, claim gzip w/ junk */
426         debug_printf (1, "  GET /mbox, Accept-Encoding: gzip, plus trailing junk\n");
427         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
428         soup_message_headers_append (reqh->request_headers,
429                                      "X-Test-Options", "trailing-junk");
430         cmp = do_single_coding_req_test (reqh, "gzip", "text/plain", EXPECT_DECODED);
431         check_req_bodies (plain, cmp, "plain", "compressed w/ junk");
432         g_byte_array_free (cmp, TRUE);
433         g_object_unref (reqh);
434
435         /* Plain text data, claim gzip with server error */
436         debug_printf (1, "  GET /mbox, Accept-Encoding: gzip, with server error\n");
437         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
438         soup_message_headers_append (reqh->request_headers,
439                                      "X-Test-Options", "force-encode");
440         cmp = do_single_coding_req_test (reqh, "gzip", "text/plain", EXPECT_NOT_DECODED);
441
442         /* Failed content-decoding should have left the body untouched
443          * from what the server sent... which happens to be the
444          * uncompressed data.
445          */
446         check_req_bodies (plain, cmp, "plain", "mis-encoded");
447         g_byte_array_free (cmp, TRUE);
448         g_object_unref (reqh);
449
450         /* Plain text data, claim deflate */
451         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate\n");
452         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
453         soup_message_headers_append (reqh->request_headers,
454                                      "X-Test-Options", "prefer-deflate-zlib");
455         cmp = do_single_coding_req_test (reqh, "deflate", "text/plain", EXPECT_DECODED);
456         check_req_bodies (plain, cmp, "plain", "compressed");
457         g_byte_array_free (cmp, TRUE);
458         g_object_unref (reqh);
459
460         /* Plain text data, claim deflate w/ junk */
461         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate, plus trailing junk\n");
462         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
463         soup_message_headers_append (reqh->request_headers,
464                                      "X-Test-Options", "prefer-deflate-zlib, trailing-junk");
465         cmp = do_single_coding_req_test (reqh, "deflate", "text/plain", EXPECT_DECODED);
466         check_req_bodies (plain, cmp, "plain", "compressed w/ junk");
467         g_byte_array_free (cmp, TRUE);
468         g_object_unref (reqh);
469
470         /* Plain text data, claim deflate with server error */
471         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate, with server error\n");
472         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
473         soup_message_headers_append (reqh->request_headers,
474                                      "X-Test-Options", "force-encode, prefer-deflate-zlib");
475         cmp = do_single_coding_req_test (reqh, "deflate", "text/plain", EXPECT_NOT_DECODED);
476         check_req_bodies (plain, cmp, "plain", "mis-encoded");
477         g_byte_array_free (cmp, TRUE);
478         g_object_unref (reqh);
479
480         /* Plain text data, claim deflate (no zlib headers)*/
481         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate (raw data)\n");
482         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
483         soup_message_headers_append (reqh->request_headers,
484                                      "X-Test-Options", "prefer-deflate-raw");
485         cmp = do_single_coding_req_test (reqh, "deflate", "text/plain", EXPECT_DECODED);
486         check_req_bodies (plain, cmp, "plain", "compressed");
487         g_byte_array_free (cmp, TRUE);
488         g_object_unref (reqh);
489
490         /* Plain text data, claim deflate with server error */
491         debug_printf (1, "  GET /mbox, Accept-Encoding: deflate (raw data), with server error\n");
492         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
493         soup_message_headers_append (reqh->request_headers,
494                                      "X-Test-Options", "force-encode, prefer-deflate-raw");
495         cmp = do_single_coding_req_test (reqh, "deflate", "text/plain", EXPECT_NOT_DECODED);
496         check_req_bodies (plain, cmp, "plain", "mis-encoded");
497         g_byte_array_free (cmp, TRUE);
498         g_object_unref (reqh);
499
500         g_byte_array_free (plain, TRUE);
501         soup_uri_free (uri);
502
503         soup_test_session_abort_unref (session);
504 }
505
506 static void
507 do_coding_empty_test (void)
508 {
509         SoupSession *session;
510         SoupMessage *msg;
511         SoupURI *uri;
512         SoupRequestHTTP *reqh;
513         GByteArray *body;
514
515         debug_printf (1, "\nEmpty allegedly-encoded body test\n");
516
517         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
518                                          SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
519                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
520                                          NULL);
521         uri = soup_uri_new_with_base (base_uri, "/mbox");
522
523         debug_printf (1, "  SoupMessage\n");
524         msg = soup_message_new_from_uri ("GET", uri);
525         soup_message_headers_append (msg->request_headers,
526                                      "X-Test-Options", "empty");
527         soup_session_send_message (session, msg);
528         check_response (msg, "gzip", "text/plain", EXPECT_NOT_DECODED);
529         g_object_unref (msg);
530
531         debug_printf (1, "  SoupRequest\n");
532         reqh = soup_session_request_http_uri (session, "GET", uri, NULL);
533         soup_message_headers_append (reqh->request_headers,
534                                      "X-Test-Options", "empty");
535         body = do_single_coding_req_test (reqh, "gzip", "text/plain", EXPECT_NOT_DECODED);
536         g_byte_array_free (body, TRUE);
537         g_object_unref (reqh);
538
539         soup_uri_free (uri);
540         soup_test_session_abort_unref (session);
541 }
542
543
544 int
545 main (int argc, char **argv)
546 {
547         test_init (argc, argv, NULL);
548
549         server = soup_test_server_new (TRUE);
550         soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
551         base_uri = soup_uri_new ("http://127.0.0.1/");
552         soup_uri_set_port (base_uri, soup_server_get_port (server));
553
554         do_coding_test ();
555         do_coding_req_test ();
556         do_coding_empty_test ();
557
558         soup_uri_free (base_uri);
559         soup_test_server_quit_unref (server);
560
561         test_cleanup ();
562         return errors != 0;
563 }