soup-multipart-input-stream: belatedly add .h file to soup.h
[platform/upstream/libsoup.git] / tests / range-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 #include "test-utils.h"
4
5 SoupBuffer *full_response;
6 int total_length;
7 char *test_response;
8
9 static void
10 get_full_response (void)
11 {
12         char *contents;
13         gsize length;
14         GError *error = NULL;
15
16         if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
17                 g_printerr ("Could not read index.txt: %s\n",
18                             error->message);
19                 exit (1);
20         }
21
22         full_response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
23         debug_printf (1, "Total response length is %d\n\n", (int)length);
24 }
25
26 static void
27 check_part (SoupMessageHeaders *headers, const char *body, gsize body_len,
28             gboolean check_start_end, int expected_start, int expected_end)
29 {
30         goffset start, end, total_length;
31
32         debug_printf (1, "    Content-Range: %s\n",
33                       soup_message_headers_get_one (headers, "Content-Range"));
34
35         if (!soup_message_headers_get_content_range (headers, &start, &end, &total_length)) {
36                 debug_printf (1, "    Could not find/parse Content-Range\n");
37                 errors++;
38                 return;
39         }
40
41         if (total_length != full_response->length && total_length != -1) {
42                 debug_printf (1, "    Unexpected total length %" G_GINT64_FORMAT " in response\n",
43                               total_length);
44                 errors++;
45                 return;
46         }
47
48         if (check_start_end) {
49                 if ((expected_start >= 0 && start != expected_start) ||
50                     (expected_start < 0 && start != full_response->length + expected_start)) {
51                         debug_printf (1, "    Unexpected range start %" G_GINT64_FORMAT " in response\n",
52                                       start);
53                         errors++;
54                         return;
55                 }
56
57                 if ((expected_end >= 0 && end != expected_end) ||
58                     (expected_end < 0 && end != full_response->length - 1)) {
59                         debug_printf (1, "    Unexpected range end %" G_GINT64_FORMAT " in response\n",
60                                       end);
61                         errors++;
62                         return;
63                 }
64         }
65
66         if (end - start + 1 != body_len) {
67                 debug_printf (1, "    Range length (%d) does not match body length (%d)\n",
68                               (int)(end - start) + 1,
69                               (int)body_len);
70                 errors++;
71                 return;
72         }
73
74         memcpy (test_response + start, body, body_len);
75 }
76
77 static void
78 do_single_range (SoupSession *session, SoupMessage *msg,
79                  int start, int end)
80 {
81         const char *content_type;
82
83         debug_printf (1, "    Range: %s\n",
84                       soup_message_headers_get_one (msg->request_headers, "Range"));
85
86         soup_session_send_message (session, msg);
87
88         if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
89                 debug_printf (1, "    Unexpected status %d %s\n",
90                               msg->status_code, msg->reason_phrase);
91                 g_object_unref (msg);
92                 errors++;
93                 return;
94         }
95
96         content_type = soup_message_headers_get_content_type (
97                 msg->response_headers, NULL);
98         if (content_type && !strcmp (content_type, "multipart/byteranges")) {
99                 debug_printf (1, "    Response body should not have been multipart/byteranges\n");
100                 g_object_unref (msg);
101                 errors++;
102                 return;
103         }
104
105         check_part (msg->response_headers, msg->response_body->data,
106                     msg->response_body->length, TRUE, start, end);
107         g_object_unref (msg);
108 }
109
110 static void
111 request_single_range (SoupSession *session, const char *uri,
112                       int start, int end)
113 {
114         SoupMessage *msg;
115
116         msg = soup_message_new ("GET", uri);
117         soup_message_headers_set_range (msg->request_headers, start, end);
118         do_single_range (session, msg, start, end);
119 }
120
121 static void
122 do_multi_range (SoupSession *session, SoupMessage *msg,
123                 int expected_return_ranges)
124 {
125         SoupMultipart *multipart;
126         const char *content_type;
127         int i, length;
128
129         debug_printf (1, "    Range: %s\n",
130                       soup_message_headers_get_one (msg->request_headers, "Range"));
131
132         soup_session_send_message (session, msg);
133
134         if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
135                 debug_printf (1, "    Unexpected status %d %s\n",
136                               msg->status_code, msg->reason_phrase);
137                 g_object_unref (msg);
138                 errors++;
139                 return;
140         }
141
142         content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
143         if (!content_type || strcmp (content_type, "multipart/byteranges") != 0) {
144                 debug_printf (1, "    Response Content-Type (%s) was not multipart/byteranges\n",
145                               content_type);
146                 g_object_unref (msg);
147                 errors++;
148                 return;
149         }
150
151         multipart = soup_multipart_new_from_message (msg->response_headers,
152                                                      msg->response_body);
153         if (!multipart) {
154                 debug_printf (1, "    Could not parse multipart\n");
155                 g_object_unref (msg);
156                 errors++;
157                 return;
158         }
159
160         length = soup_multipart_get_length (multipart);
161         if (length != expected_return_ranges) {
162                 debug_printf (1, "    Expected %d ranges, got %d\n",
163                               expected_return_ranges, length);
164                 errors++;
165         }
166
167         for (i = 0; i < length; i++) {
168                 SoupMessageHeaders *headers;
169                 SoupBuffer *body;
170
171                 debug_printf (1, "  Part %d\n", i + 1);
172                 soup_multipart_get_part (multipart, i, &headers, &body);
173                 check_part (headers, body->data, body->length, FALSE, 0, 0);
174         }
175
176         soup_multipart_free (multipart);
177         g_object_unref (msg);
178 }
179
180 static void
181 request_double_range (SoupSession *session, const char *uri,
182                       int first_start, int first_end,
183                       int second_start, int second_end,
184                       int expected_return_ranges)
185 {
186         SoupMessage *msg;
187         SoupRange ranges[2];
188
189         msg = soup_message_new ("GET", uri);
190         ranges[0].start = first_start;
191         ranges[0].end = first_end;
192         ranges[1].start = second_start;
193         ranges[1].end = second_end;
194         soup_message_headers_set_ranges (msg->request_headers, ranges, 2);
195
196         if (expected_return_ranges == 1) {
197                 do_single_range (session, msg,
198                                  MIN (first_start, second_start),
199                                  MAX (first_end, second_end));
200         } else
201                 do_multi_range (session, msg, expected_return_ranges);
202 }
203
204 static void
205 request_triple_range (SoupSession *session, const char *uri,
206                       int first_start, int first_end,
207                       int second_start, int second_end,
208                       int third_start, int third_end,
209                       int expected_return_ranges)
210 {
211         SoupMessage *msg;
212         SoupRange ranges[3];
213
214         msg = soup_message_new ("GET", uri);
215         ranges[0].start = first_start;
216         ranges[0].end = first_end;
217         ranges[1].start = second_start;
218         ranges[1].end = second_end;
219         ranges[2].start = third_start;
220         ranges[2].end = third_end;
221         soup_message_headers_set_ranges (msg->request_headers, ranges, 3);
222
223         if (expected_return_ranges == 1) {
224                 do_single_range (session, msg,
225                                  MIN (first_start, MIN (second_start, third_start)),
226                                  MAX (first_end, MAX (second_end, third_end)));
227         } else
228                 do_multi_range (session, msg, expected_return_ranges);
229 }
230
231 static void
232 do_range_test (SoupSession *session, const char *uri,
233                gboolean expect_coalesce, gboolean expect_partial_coalesce)
234 {
235         int twelfths = full_response->length / 12;
236
237         memset (test_response, 0, full_response->length);
238
239         /* We divide the response into 12 ranges and request them
240          * as follows:
241          *
242          *  0: A (first single request)
243          *  1: D (2nd part of triple request)
244          *  2: C (1st part of double request)
245          *  3: D (1st part of triple request)
246          *  4: F (trickier overlapping request)
247          *  5: C (2nd part of double request)
248          *  6: D (3rd part of triple request)
249          *  7: E (overlapping request)
250          *  8: E (overlapping request)
251          *  9: F (trickier overlapping request)
252          * 10: F (trickier overlapping request)
253          * 11: B (second and third single requests)
254          */
255
256         /* A: 0, simple request */
257         debug_printf (1, "Requesting %d-%d\n", 0 * twelfths, 1 * twelfths);
258         request_single_range (session, uri,
259                               0 * twelfths, 1 * twelfths);
260
261         /* B: 11, end-relative request. These two are mostly redundant
262          * in terms of data coverage, but they may still catch
263          * Range-header-generating bugs.
264          */
265         debug_printf (1, "Requesting %d-\n", 11 * twelfths);
266         request_single_range (session, uri,
267                               11 * twelfths, -1);
268         debug_printf (1, "Requesting -%d\n", 1 * twelfths);
269         request_single_range (session, uri,
270                               -1 * twelfths, -1);
271
272         /* C: 2 and 5 */
273         debug_printf (1, "Requesting %d-%d,%d-%d\n",
274                       2 * twelfths, 3 * twelfths,
275                       5 * twelfths, 6 * twelfths);
276         request_double_range (session, uri,
277                               2 * twelfths, 3 * twelfths,
278                               5 * twelfths, 6 * twelfths,
279                               2);
280
281         /* D: 1, 3, 6 */
282         debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d\n",
283                       3 * twelfths, 4 * twelfths,
284                       1 * twelfths, 2 * twelfths,
285                       6 * twelfths, 7 * twelfths);
286         request_triple_range (session, uri,
287                               3 * twelfths, 4 * twelfths,
288                               1 * twelfths, 2 * twelfths,
289                               6 * twelfths, 7 * twelfths,
290                               3);
291
292         /* E: 7 and 8: should coalesce into a single response */
293         debug_printf (1, "Requesting %d-%d,%d-%d (can coalesce)\n",
294                       7 * twelfths, 8 * twelfths,
295                       8 * twelfths, 9 * twelfths);
296         request_double_range (session, uri,
297                               7 * twelfths, 8 * twelfths,
298                               8 * twelfths, 9 * twelfths,
299                               expect_coalesce ? 1 : 2);
300
301         /* F: 4, 9, 10: 9 and 10 should coalesce even though 4 was
302          * requested between them. (Also, they actually overlap in
303          * this case, as opposed to just touching.)
304          */
305         debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d (can partially coalesce)\n",
306                       9 * twelfths, 10 * twelfths + 5,
307                       4 * twelfths, 5 * twelfths,
308                       10 * twelfths - 5, 11 * twelfths);
309         request_triple_range (session, uri,
310                               9 * twelfths, 10 * twelfths + 5,
311                               4 * twelfths, 5 * twelfths,
312                               10 * twelfths - 5, 11 * twelfths,
313                               expect_partial_coalesce ? 2 : 3);
314
315         if (memcmp (full_response->data, test_response, full_response->length) != 0) {
316                 debug_printf (1, "\nfull_response and test_response don't match\n");
317                 errors++;
318         }
319 }
320
321 static void
322 server_handler (SoupServer        *server,
323                 SoupMessage       *msg, 
324                 const char        *path,
325                 GHashTable        *query,
326                 SoupClientContext *client,
327                 gpointer           user_data)
328 {
329         soup_message_set_status (msg, SOUP_STATUS_OK);
330         soup_message_body_append_buffer (msg->response_body,
331                                          full_response);
332 }
333
334 int
335 main (int argc, char **argv)
336 {
337         SoupSession *session;
338         SoupServer *server;
339         char *base_uri;
340
341         test_init (argc, argv, NULL);
342         apache_init ();
343
344         get_full_response ();
345         test_response = g_malloc0 (full_response->length);
346
347         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
348
349         debug_printf (1, "1. Testing against apache\n");
350 #if HAVE_APACHE_2_2
351         do_range_test (session, "http://127.0.0.1:47524/", FALSE, FALSE);
352 #else
353         do_range_test (session, "http://127.0.0.1:47524/", TRUE, FALSE);
354 #endif
355
356         debug_printf (1, "\n2. Testing against SoupServer\n");
357         server = soup_test_server_new (FALSE);
358         soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
359         base_uri = g_strdup_printf ("http://127.0.0.1:%u/",
360                                     soup_server_get_port (server));
361         do_range_test (session, base_uri, TRUE, TRUE);
362         g_free (base_uri);
363         soup_test_server_quit_unref (server);
364
365         soup_test_session_abort_unref (session);
366
367         soup_buffer_free (full_response);
368         g_free (test_response);
369
370         test_cleanup ();
371         return errors != 0;
372 }