• 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 *name, *value;
7 } Header;
8 
9 static struct RequestTest {
10 	const char *description;
11 	const char *bugref;
12 	const char *request;
13 	int length;
14 	guint status;
15 	const char *method, *path;
16 	SoupHTTPVersion version;
17 	Header headers[10];
18 } reqtests[] = {
19 	/**********************/
20 	/*** VALID REQUESTS ***/
21 	/**********************/
22 
23 	{ "HTTP 1.0 request with no headers", NULL,
24 	  "GET / HTTP/1.0\r\n", -1,
25 	  SOUP_STATUS_OK,
26 	  "GET", "/", SOUP_HTTP_1_0,
27 	  { { NULL } }
28 	},
29 
30 	{ "Req w/ 1 header", NULL,
31 	  "GET / HTTP/1.1\r\nHost: example.com\r\n", -1,
32 	  SOUP_STATUS_OK,
33 	  "GET", "/", SOUP_HTTP_1_1,
34 	  { { "Host", "example.com" },
35 	    { NULL }
36 	  }
37 	},
38 
39 	{ "Req w/ 1 header, no leading whitespace", NULL,
40 	  "GET / HTTP/1.1\r\nHost:example.com\r\n", -1,
41 	  SOUP_STATUS_OK,
42 	  "GET", "/", SOUP_HTTP_1_1,
43 	  { { "Host", "example.com" },
44 	    { NULL }
45 	  }
46 	},
47 
48 	{ "Req w/ 1 header including trailing whitespace", NULL,
49 	  "GET / HTTP/1.1\r\nHost: example.com \r\n", -1,
50 	  SOUP_STATUS_OK,
51 	  "GET", "/", SOUP_HTTP_1_1,
52 	  { { "Host", "example.com" },
53 	    { NULL }
54 	  }
55 	},
56 
57 	{ "Req w/ 1 header, wrapped", NULL,
58 	  "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\n", -1,
59 	  SOUP_STATUS_OK,
60 	  "GET", "/", SOUP_HTTP_1_1,
61 	  { { "Foo", "bar baz" },
62 	    { NULL }
63 	  }
64 	},
65 
66 	{ "Req w/ 1 header, wrapped with additional whitespace", NULL,
67 	  "GET / HTTP/1.1\r\nFoo: bar \r\n  baz\r\n", -1,
68 	  SOUP_STATUS_OK,
69 	  "GET", "/", SOUP_HTTP_1_1,
70 	  { { "Foo", "bar baz" },
71 	    { NULL }
72 	  }
73 	},
74 
75 	{ "Req w/ 1 header, wrapped with tab", NULL,
76 	  "GET / HTTP/1.1\r\nFoo: bar\r\n\tbaz\r\n", -1,
77 	  SOUP_STATUS_OK,
78 	  "GET", "/", SOUP_HTTP_1_1,
79 	  { { "Foo", "bar baz" },
80 	    { NULL }
81 	  }
82 	},
83 
84 	{ "Req w/ 1 header, wrapped before value", NULL,
85 	  "GET / HTTP/1.1\r\nFoo:\r\n bar baz\r\n", -1,
86 	  SOUP_STATUS_OK,
87 	  "GET", "/", SOUP_HTTP_1_1,
88 	  { { "Foo", "bar baz" },
89 	    { NULL }
90 	  }
91 	},
92 
93 	{ "Req w/ 1 header with empty value", NULL,
94 	  "GET / HTTP/1.1\r\nHost:\r\n", -1,
95 	  SOUP_STATUS_OK,
96 	  "GET", "/", SOUP_HTTP_1_1,
97 	  { { "Host", "" },
98 	    { NULL }
99 	  }
100 	},
101 
102 	{ "Req w/ 2 headers", NULL,
103 	  "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n", -1,
104 	  SOUP_STATUS_OK,
105 	  "GET", "/", SOUP_HTTP_1_1,
106 	  { { "Host", "example.com" },
107 	    { "Connection", "close" },
108 	    { NULL }
109 	  }
110 	},
111 
112 	{ "Req w/ 3 headers", NULL,
113 	  "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\nBlah: blah\r\n", -1,
114 	  SOUP_STATUS_OK,
115 	  "GET", "/", SOUP_HTTP_1_1,
116 	  { { "Host", "example.com" },
117 	    { "Connection", "close" },
118 	    { "Blah", "blah" },
119 	    { NULL }
120 	  }
121 	},
122 
123 	{ "Req w/ 3 headers, 1st wrapped", NULL,
124 	  "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\nConnection: close\r\nBlah: blah\r\n", -1,
125 	  SOUP_STATUS_OK,
126 	  "GET", "/", SOUP_HTTP_1_1,
127 	  { { "Foo", "bar baz" },
128 	    { "Connection", "close" },
129 	    { "Blah", "blah" },
130 	    { NULL }
131 	  }
132 	},
133 
134 	{ "Req w/ 3 headers, 2nd wrapped", NULL,
135 	  "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
136 	  SOUP_STATUS_OK,
137 	  "GET", "/", SOUP_HTTP_1_1,
138 	  { { "Connection", "close" },
139 	    { "Blah", "blah" },
140 	    { "Foo", "bar baz" },
141 	    { NULL }
142 	  }
143 	},
144 
145 	{ "Req w/ 3 headers, 3rd wrapped", NULL,
146 	  "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
147 	  SOUP_STATUS_OK,
148 	  "GET", "/", SOUP_HTTP_1_1,
149 	  { { "Connection", "close" },
150 	    { "Blah", "blah" },
151 	    { "Foo", "bar baz" },
152 	    { NULL }
153 	  }
154 	},
155 
156 	{ "Req w/ same header multiple times", NULL,
157 	  "GET / HTTP/1.1\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
158 	  SOUP_STATUS_OK,
159 	  "GET", "/", SOUP_HTTP_1_1,
160 	  { { "Foo", "bar, baz, quux" },
161 	    { NULL }
162 	  }
163 	},
164 
165 	{ "Connection header on HTTP/1.0 message", NULL,
166 	  "GET / HTTP/1.0\r\nFoo: bar\r\nConnection: Bar, Quux\r\nBar: baz\r\nQuux: foo\r\n", -1,
167 	  SOUP_STATUS_OK,
168 	  "GET", "/", SOUP_HTTP_1_0,
169 	  { { "Foo", "bar" },
170 	    { "Connection", "Bar, Quux" },
171 	    { NULL }
172 	  }
173 	},
174 
175 	{ "GET with full URI", "667637",
176 	  "GET http://example.com HTTP/1.1\r\n", -1,
177 	  SOUP_STATUS_OK,
178 	  "GET", "http://example.com", SOUP_HTTP_1_1,
179 	  { { NULL } }
180 	},
181 
182 	{ "GET with full URI in upper-case", "667637",
183 	  "GET HTTP://example.com HTTP/1.1\r\n", -1,
184 	  SOUP_STATUS_OK,
185 	  "GET", "HTTP://example.com", SOUP_HTTP_1_1,
186 	  { { NULL } }
187 	},
188 
189 	/* It's better for this to be passed through: this means a SoupServer
190 	 * could implement ftp-over-http proxying, for instance
191 	 */
192 	{ "GET with full URI of unrecognised scheme", "667637",
193 	  "GET AbOuT: HTTP/1.1\r\n", -1,
194 	  SOUP_STATUS_OK,
195 	  "GET", "AbOuT:", SOUP_HTTP_1_1,
196 	  { { NULL } }
197 	},
198 
199 	/****************************/
200 	/*** RECOVERABLE REQUESTS ***/
201 	/****************************/
202 
203 	/* RFC 2616 section 4.1 says we SHOULD accept this */
204 
205 	{ "Spurious leading CRLF", NULL,
206 	  "\r\nGET / HTTP/1.1\r\nHost: example.com\r\n", -1,
207 	  SOUP_STATUS_OK,
208 	  "GET", "/", SOUP_HTTP_1_1,
209 	  { { "Host", "example.com" },
210 	    { NULL }
211 	  }
212 	},
213 
214 	/* RFC 2616 section 3.1 says we MUST accept this */
215 
216 	{ "HTTP/01.01 request", NULL,
217 	  "GET / HTTP/01.01\r\nHost: example.com\r\n", -1,
218 	  SOUP_STATUS_OK,
219 	  "GET", "/", SOUP_HTTP_1_1,
220 	  { { "Host", "example.com" },
221 	    { NULL }
222 	  }
223 	},
224 
225 	/* RFC 2616 section 19.3 says we SHOULD accept these */
226 
227 	{ "LF instead of CRLF after header", NULL,
228 	  "GET / HTTP/1.1\r\nHost: example.com\nConnection: close\n", -1,
229 	  SOUP_STATUS_OK,
230 	  "GET", "/", SOUP_HTTP_1_1,
231 	  { { "Host", "example.com" },
232 	    { "Connection", "close" },
233 	    { NULL }
234 	  }
235 	},
236 
237 	{ "LF instead of CRLF after Request-Line", NULL,
238 	  "GET / HTTP/1.1\nHost: example.com\r\n", -1,
239 	  SOUP_STATUS_OK,
240 	  "GET", "/", SOUP_HTTP_1_1,
241 	  { { "Host", "example.com" },
242 	    { NULL }
243 	  }
244 	},
245 
246 	{ "Mixed CRLF/LF", "666316",
247 	  "GET / HTTP/1.1\r\na: b\r\nc: d\ne: f\r\ng: h\n", -1,
248 	  SOUP_STATUS_OK,
249 	  "GET", "/", SOUP_HTTP_1_1,
250 	  { { "a", "b" },
251 	    { "c", "d" },
252 	    { "e", "f" },
253 	    { "g", "h" },
254 	    { NULL }
255 	  }
256 	},
257 
258 	{ "Req w/ incorrect whitespace in Request-Line", NULL,
259 	  "GET  /\tHTTP/1.1\r\nHost: example.com\r\n", -1,
260 	  SOUP_STATUS_OK,
261 	  "GET", "/", SOUP_HTTP_1_1,
262 	  { { "Host", "example.com" },
263 	    { NULL }
264 	  }
265 	},
266 
267 	{ "Req w/ incorrect whitespace after Request-Line", "475169",
268 	  "GET / HTTP/1.1 \r\nHost: example.com\r\n", -1,
269 	  SOUP_STATUS_OK,
270 	  "GET", "/", SOUP_HTTP_1_1,
271 	  { { "Host", "example.com" },
272 	    { NULL }
273 	  }
274 	},
275 
276 	/* If the request/status line is parseable, then we
277 	 * just ignore any invalid-looking headers after that.
278 	 */
279 
280 	{ "Req w/ mangled header", "579318",
281 	  "GET / HTTP/1.1\r\nHost: example.com\r\nFoo one\r\nBar: two\r\n", -1,
282 	  SOUP_STATUS_OK,
283 	  "GET", "/", SOUP_HTTP_1_1,
284 	  { { "Host", "example.com" },
285 	    { "Bar", "two" },
286 	    { NULL }
287 	  }
288 	},
289 
290 	{ "First header line is continuation", "666316",
291 	  "GET / HTTP/1.1\r\n b\r\nHost: example.com\r\nc: d\r\n", -1,
292 	  SOUP_STATUS_OK,
293 	  "GET", "/", SOUP_HTTP_1_1,
294 	  { { "Host", "example.com" },
295 	    { "c", "d" },
296 	    { NULL }
297 	  }
298 	},
299 
300 	{ "Zero-length header name", "666316",
301 	  "GET / HTTP/1.1\r\na: b\r\n: example.com\r\nc: d\r\n", -1,
302 	  SOUP_STATUS_OK,
303 	  "GET", "/", SOUP_HTTP_1_1,
304 	  { { "a", "b" },
305 	    { "c", "d" },
306 	    { NULL }
307 	  }
308 	},
309 
310 	{ "CR in header name", "666316",
311 	  "GET / HTTP/1.1\r\na: b\r\na\rb: cd\r\nx\r: y\r\n\rz: w\r\nc: d\r\n", -1,
312 	  SOUP_STATUS_OK,
313 	  "GET", "/", SOUP_HTTP_1_1,
314 	  { { "a", "b" },
315 	    { "c", "d" },
316 	    { NULL }
317 	  }
318 	},
319 
320 	{ "CR in header value", "666316",
321 	  "GET / HTTP/1.1\r\na: b\r\nHost: example\rcom\r\np: \rq\r\ns: t\r\r\nc: d\r\n", -1,
322 	  SOUP_STATUS_OK,
323 	  "GET", "/", SOUP_HTTP_1_1,
324 	  { { "a", "b" },
325 	    { "Host", "example com" },	/* CR in the middle turns to space */
326 	    { "p", "q" },		/* CR at beginning is ignored */
327 	    { "s", "t" },		/* CR at end is ignored */
328 	    { "c", "d" },
329 	    { NULL }
330 	  }
331 	},
332 
333 	{ "Tab in header name", "666316",
334 	  "GET / HTTP/1.1\r\na: b\r\na\tb: cd\r\nx\t: y\r\np: q\r\n\tz: w\r\nc: d\r\n", -1,
335 	  SOUP_STATUS_OK,
336 	  "GET", "/", SOUP_HTTP_1_1,
337 	  { { "a", "b" },
338 	    /* Tab anywhere in the header name causes it to be
339 	     * ignored... except at beginning of line where it's a
340 	     * continuation line
341 	     */
342 	    { "p", "q z: w" },
343 	    { "c", "d" },
344 	    { NULL }
345 	  }
346 	},
347 
348 	{ "Tab in header value", "666316",
349 	  "GET / HTTP/1.1\r\na: b\r\nab: c\td\r\nx: \ty\r\nz: w\t\r\nc: d\r\n", -1,
350 	  SOUP_STATUS_OK,
351 	  "GET", "/", SOUP_HTTP_1_1,
352 	  { { "a", "b" },
353 	    { "ab", "c\td" },	/* internal tab preserved */
354 	    { "x", "y" },	/* leading tab ignored */
355 	    { "z", "w" },	/* trailing tab ignored */
356 	    { "c", "d" },
357 	    { NULL }
358 	  }
359 	},
360 
361 	{ "NUL in header name", "760832",
362 	  "GET / HTTP/1.1\r\nHost\x00: example.com\r\n", 36,
363 	  SOUP_STATUS_OK,
364 	  "GET", "/", SOUP_HTTP_1_1,
365 	  { { "Host", "example.com" },
366 	    { NULL }
367 	  }
368 	},
369 
370 	{ "NUL in header value", "760832",
371 	  "GET / HTTP/1.1\r\nHost: example\x00" "com\r\n", 35,
372 	  SOUP_STATUS_OK,
373 	  "GET", "/", SOUP_HTTP_1_1,
374 	  { { "Host", "examplecom" },
375 	    { NULL }
376 	  }
377 	},
378 
379 	/************************/
380 	/*** INVALID REQUESTS ***/
381 	/************************/
382 
383 	{ "HTTP 0.9 request; not supported", NULL,
384 	  "GET /\r\n", -1,
385 	  SOUP_STATUS_BAD_REQUEST,
386 	  NULL, NULL, -1,
387 	  { { NULL } }
388 	},
389 
390 	{ "HTTP 1.2 request (no such thing)", NULL,
391 	  "GET / HTTP/1.2\r\n", -1,
392 	  SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
393 	  NULL, NULL, -1,
394 	  { { NULL } }
395 	},
396 
397 	{ "HTTP 2000 request (no such thing)", NULL,
398 	  "GET / HTTP/2000.0\r\n", -1,
399 	  SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
400 	  NULL, NULL, -1,
401 	  { { NULL } }
402 	},
403 
404 	{ "Non-HTTP request", NULL,
405 	  "GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
406 	  SOUP_STATUS_BAD_REQUEST,
407 	  NULL, NULL, -1,
408 	  { { NULL } }
409 	},
410 
411 	{ "Junk after Request-Line", NULL,
412 	  "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1,
413 	  SOUP_STATUS_BAD_REQUEST,
414 	  NULL, NULL, -1,
415 	  { { NULL } }
416 	},
417 
418 	{ "NUL in Method", NULL,
419 	  "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37,
420 	  SOUP_STATUS_BAD_REQUEST,
421 	  NULL, NULL, -1,
422 	  { { NULL } }
423 	},
424 
425 	{ "NUL at beginning of Method", "666316",
426 	  "\x00 / HTTP/1.1\r\nHost: example.com\r\n", 35,
427 	  SOUP_STATUS_BAD_REQUEST,
428 	  NULL, NULL, -1,
429 	  { { NULL } }
430 	},
431 
432 	{ "NUL in Path", NULL,
433 	  "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38,
434 	  SOUP_STATUS_BAD_REQUEST,
435 	  NULL, NULL, -1,
436 	  { { NULL } }
437 	},
438 
439 	{ "No terminating CRLF", NULL,
440 	  "GET / HTTP/1.1\r\nHost: example.com", -1,
441 	  SOUP_STATUS_BAD_REQUEST,
442 	  NULL, NULL, -1,
443 	  { { NULL } }
444 	},
445 
446 	{ "Unrecognized expectation", NULL,
447 	  "GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1,
448 	  SOUP_STATUS_EXPECTATION_FAILED,
449 	  NULL, NULL, -1,
450 	  { { NULL } }
451 	}
452 };
453 static const int num_reqtests = G_N_ELEMENTS (reqtests);
454 
455 static struct ResponseTest {
456 	const char *description;
457 	const char *bugref;
458 	const char *response;
459 	int length;
460 	SoupHTTPVersion version;
461 	guint status_code;
462 	const char *reason_phrase;
463 	Header headers[10];
464 } resptests[] = {
465 	/***********************/
466 	/*** VALID RESPONSES ***/
467 	/***********************/
468 
469 	{ "HTTP 1.0 response w/ no headers", NULL,
470 	  "HTTP/1.0 200 ok\r\n", -1,
471 	  SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
472 	  { { NULL } }
473 	},
474 
475 	{ "HTTP 1.1 response w/ no headers", NULL,
476 	  "HTTP/1.1 200 ok\r\n", -1,
477 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
478 	  { { NULL } }
479 	},
480 
481 	{ "Response w/ multi-word Reason-Phrase", NULL,
482 	  "HTTP/1.1 400 bad request\r\n", -1,
483 	  SOUP_HTTP_1_1, SOUP_STATUS_BAD_REQUEST, "bad request",
484 	  { { NULL } }
485 	},
486 
487 	{ "Response w/ 1 header", NULL,
488 	  "HTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
489 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
490 	  { { "Foo", "bar" },
491 	    { NULL }
492 	  }
493 	},
494 
495 	{ "Response w/ 2 headers", NULL,
496 	  "HTTP/1.1 200 ok\r\nFoo: bar\r\nBaz: quux\r\n", -1,
497 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
498 	  { { "Foo", "bar" },
499 	    { "Baz", "quux" },
500 	    { NULL }
501 	  }
502 	},
503 
504 	{ "Response w/ same header multiple times", NULL,
505 	  "HTTP/1.1 200 ok\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
506 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
507 	  { { "Foo", "bar, baz, quux" },
508 	    { NULL }
509 	  }
510 	},
511 
512 	{ "Response w/ no reason phrase", NULL,
513 	  "HTTP/1.1 200 \r\nFoo: bar\r\n", -1,
514 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
515 	  { { "Foo", "bar" },
516 	    { NULL }
517 	  }
518 	},
519 
520 	{ "Response w/ unknown status code", NULL,
521 	  "HTTP/1.1 999 Request denied\r\nFoo: bar\r\n", -1,
522 	  SOUP_HTTP_1_1, 999, "Request denied",
523 	  { { "Foo", "bar" },
524 	    { NULL }
525 	  }
526 	},
527 
528 	{ "Connection header on HTTP/1.0 message", NULL,
529 	  "HTTP/1.0 200 ok\r\nFoo: bar\r\nConnection: Bar\r\nBar: quux\r\n", -1,
530 	  SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
531 	  { { "Foo", "bar" },
532 	    { "Connection", "Bar" },
533 	    { NULL }
534 	  }
535 	},
536 
537 	/* Tests from Cockpit */
538 
539 	{ "Response w/ 3 headers, check case-insensitivity", "722341",
540 	  "HTTP/1.0 200 ok\r\nHeader1: value3\r\nHeader2:  field\r\nHead3:  Another \r\n", -1,
541 	  SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
542 	  { { "header1", "value3" },
543 	    { "Header2", "field" },
544 	    { "hEAD3", "Another" },
545 	    { "Something else", NULL },
546 	    { NULL }
547 	  }
548 	},
549 
550 	/*****************************/
551 	/*** RECOVERABLE RESPONSES ***/
552 	/*****************************/
553 
554 	/* RFC 2616 section 3.1 says we MUST accept this */
555 
556 	{ "HTTP/01.01 response", NULL,
557 	  "HTTP/01.01 200 ok\r\nFoo: bar\r\n", -1,
558 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
559 	  { { "Foo", "bar" },
560 	    { NULL }
561 	  }
562 	},
563 
564 	/* RFC 2616 section 19.3 says we SHOULD accept these */
565 
566 	{ "Response w/ LF instead of CRLF after Status-Line", NULL,
567 	  "HTTP/1.1 200 ok\nFoo: bar\r\n", -1,
568 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
569 	  { { "Foo", "bar" },
570 	    { NULL }
571 	  }
572 	},
573 
574 	{ "Response w/ incorrect spacing in Status-Line", NULL,
575 	  "HTTP/1.1  200\tok\r\nFoo: bar\r\n", -1,
576 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
577 	  { { "Foo", "bar" },
578 	    { NULL }
579 	  }
580 	},
581 
582 	{ "Response w/ no reason phrase or preceding SP", NULL,
583 	  "HTTP/1.1 200\r\nFoo: bar\r\n", -1,
584 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
585 	  { { "Foo", "bar" },
586 	    { NULL }
587 	  }
588 	},
589 
590 	{ "Response w/ no whitespace after status code", NULL,
591 	  "HTTP/1.1 200ok\r\nFoo: bar\r\n", -1,
592 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
593 	  { { "Foo", "bar" },
594 	    { NULL }
595 	  }
596 	},
597 
598 	/* Shoutcast support */
599 	{ "Shoutcast server not-quite-HTTP", "502325",
600 	  "ICY 200 OK\r\nFoo: bar\r\n", -1,
601 	  SOUP_HTTP_1_0, SOUP_STATUS_OK, "OK",
602 	  { { "Foo", "bar" },
603 	    { NULL }
604 	  }
605 	},
606 
607 	{ "Response w/ mangled header", "579318",
608 	  "HTTP/1.1 200 ok\r\nFoo: one\r\nBar two:2\r\nBaz: three\r\n", -1,
609 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
610 	  { { "Foo", "one" },
611 	    { "Baz", "three" },
612 	    { NULL }
613 	  }
614 	},
615 
616 	{ "HTTP 1.1 response with leading line break", "602863",
617 	  "\nHTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
618 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
619 	  { { "Foo", "bar" },
620 	    { NULL } }
621 	},
622 
623 	{ "NUL in header name", "760832",
624 	  "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28,
625 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "OK",
626 	  { { "Foo", "bar" },
627 	    { NULL }
628 	  }
629 	},
630 
631 	{ "NUL in header value", "760832",
632 	  "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28,
633 	  SOUP_HTTP_1_1, SOUP_STATUS_OK, "OK",
634 	  { { "Foo", "bar" },
635 	    { NULL }
636 	  }
637 	},
638 
639 	/********************************/
640 	/*** VALID CONTINUE RESPONSES ***/
641 	/********************************/
642 
643 	/* Tests from Cockpit project */
644 
645 	{ "Response w/ 101 Switching Protocols + spaces after new line", NULL,
646 	  "HTTP/1.0 101 Switching Protocols\r\n  \r\n", 38,
647 	  SOUP_HTTP_1_0, SOUP_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols",
648 	  { { NULL } }
649 	},
650 
651 	{ "Response w/ 101 Switching Protocols missing \\r + spaces", NULL,
652 	  "HTTP/1.0  101  Switching Protocols\r\n  \r\n", 40,
653 	  SOUP_HTTP_1_0, SOUP_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols",
654 	  { { NULL } }
655 	},
656 
657 	{ "Response w/ 101 Switching Protocols + spaces after & before new line", NULL,
658 	  "HTTP/1.1  101  Switching Protocols  \r\n  \r\n", 42,
659 	  SOUP_HTTP_1_1, SOUP_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols",
660 	  { { NULL } }
661 	},
662 
663 	/*************************/
664 	/*** INVALID RESPONSES ***/
665 	/*************************/
666 
667 	{ "Invalid HTTP version", NULL,
668 	  "HTTP/1.2 200 OK\r\nFoo: bar\r\n", -1,
669 	  -1, 0, NULL,
670 	  { { NULL } }
671 	},
672 
673 	{ "Non-HTTP response", NULL,
674 	  "SOUP/1.1 200 OK\r\nFoo: bar\r\n", -1,
675 	  -1, 0, NULL,
676 	  { { NULL } }
677 	},
678 
679 	{ "Non-numeric status code", NULL,
680 	  "HTTP/1.1 XXX OK\r\nFoo: bar\r\n", -1,
681 	  -1, 0, NULL,
682 	  { { NULL } }
683 	},
684 
685 	{ "No status code", NULL,
686 	  "HTTP/1.1 OK\r\nFoo: bar\r\n", -1,
687 	  -1, 0, NULL,
688 	  { { NULL } }
689 	},
690 
691 	{ "One-digit status code", NULL,
692 	  "HTTP/1.1 2 OK\r\nFoo: bar\r\n", -1,
693 	  -1, 0, NULL,
694 	  { { NULL } }
695 	},
696 
697 	{ "Two-digit status code", NULL,
698 	  "HTTP/1.1 20 OK\r\nFoo: bar\r\n", -1,
699 	  -1, 0, NULL,
700 	  { { NULL } }
701 	},
702 
703 	{ "Four-digit status code", NULL,
704 	  "HTTP/1.1 2000 OK\r\nFoo: bar\r\n", -1,
705 	  -1, 0, NULL,
706 	  { { NULL } }
707 	},
708 
709 	{ "Status code < 100", NULL,
710 	  "HTTP/1.1 001 OK\r\nFoo: bar\r\n", -1,
711 	  -1, 0, NULL,
712 	  { { NULL } }
713 	},
714 
715 	{ "Status code > 999", NULL,
716 	  "HTTP/1.1 1000 OK\r\nFoo: bar\r\n", -1,
717 	  -1, 0, NULL,
718 	  { { NULL } }
719 	},
720 
721 	{ "NUL at start", "666316",
722 	  "\x00HTTP/1.1 200 OK\r\nFoo: bar\r\n", 28,
723 	  -1, 0, NULL,
724 	  { { NULL } }
725 	},
726 
727 	{ "NUL in Reason Phrase", NULL,
728 	  "HTTP/1.1 200 O\x00K\r\nFoo: bar\r\n", 28,
729 	  -1, 0, NULL,
730 	  { { NULL } }
731 	},
732 
733 	/* Failing test from Cockpit */
734 
735 	{ "Partial response stops after HTTP/", NULL,
736 	  "HTTP/", -1,
737 	  -1, 0, NULL,
738 	  { { NULL } }
739 	},
740 
741 	{ "Space before HTTP/", NULL,
742 	  " HTTP/1.0 101 Switching Protocols\r\n  ", -1,
743 	  -1, 0, NULL,
744 	  { { NULL } }
745 	},
746 
747 	{ "Missing reason", NULL,
748 	  "HTTP/1.0  101\r\n  ", -1,
749 	  -1, 0, NULL,
750 	  { { NULL } }
751 	},
752 
753 	{ "Response code containing alphabetic character", NULL,
754 	  "HTTP/1.1  1A01  Switching Protocols  \r\n  ", -1,
755 	  -1, 0, NULL,
756 	  { { NULL } }
757 	},
758 
759 	{ "TESTONE\\r\\n", NULL,
760 	  "TESTONE\r\n  ", -1,
761 	  -1, 0, NULL,
762 	  { { NULL } }
763 	},
764 
765 	{ "Response w/ 3 headers truncated", NULL,
766 	  "HTTP/1.0 200 ok\r\nHeader1: value3\r\nHeader2:  field\r\nHead3:  Anothe", -1,
767 	  -1, 0, NULL,
768 	  { { NULL }
769 	  }
770 	},
771 };
772 static const int num_resptests = G_N_ELEMENTS (resptests);
773 
774 static struct QValueTest {
775 	const char *header_value;
776 	const char *acceptable[7];
777 	const char *unacceptable[2];
778 } qvaluetests[] = {
779 	{ "text/plain; q=0.5, text/html,\t  text/x-dvi; q=0.8, text/x-c",
780 	  { "text/html", "text/x-c", "text/x-dvi", "text/plain", NULL },
781 	  { NULL },
782 	},
783 
784 	{ "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
785 	  { "text/html;level=1", "text/html", "*/*", "text/html;level=2",
786 	    "text/*", NULL },
787 	  { NULL }
788 	},
789 
790 	{ "gzip;q=1.0, identity; q=0.5, *;q=0",
791 	  { "gzip", "identity", NULL },
792 	  { "*", NULL },
793 	}
794 };
795 static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests);
796 
797 static struct ParamListTest {
798 	gboolean strict;
799 	const char *header_value;
800 	struct ParamListResult {
801 		const char * param;
802 		const char * value;
803 	} results[3];
804 } paramlisttests[] = {
805 	{ TRUE,
806 	  "UserID=JohnDoe; Max-Age=3600; Version=1",
807 	  { { "UserID", "JohnDoe" },
808 	    { "Max-Age", "3600" },
809 	    { "Version", "1" },
810 	  }
811 	},
812 
813 	{ TRUE,
814 	  "form-data; name=\"fieldName\"; filename=\"filename.jpg\"",
815 	  { { "form-data", NULL },
816 	    { "name", "fieldName" },
817 	    { "filename", "filename.jpg" },
818 	  },
819 	},
820 
821 	{ FALSE,
822 	  "form-data; form-data; filename=\"filename.jpg\"",
823 	  { { "form-data", NULL },
824 	    { "filename", "filename.jpg" },
825 	  },
826 	},
827 
828 	{ FALSE,
829 	  "attachment; filename*=UTF-8''t%C3%A9st.txt; filename=\"test.txt\"",
830 	  { { "attachment", NULL },
831 	    { "filename", "t\xC3\xA9st.txt" },
832 	  },
833 	},
834 };
835 static const int num_paramlisttests = G_N_ELEMENTS (paramlisttests);
836 
837 static void
check_headers(Header * headers,SoupMessageHeaders * hdrs)838 check_headers (Header *headers, SoupMessageHeaders *hdrs)
839 {
840 	GSList *header_names, *h;
841 	SoupMessageHeadersIter iter;
842 	const char *name, *value;
843 	int i;
844 
845 	header_names = NULL;
846 	soup_message_headers_iter_init (&iter, hdrs);
847 	while (soup_message_headers_iter_next (&iter, &name, &value)) {
848 		if (!g_slist_find_custom (header_names, name,
849 					  (GCompareFunc)strcmp))
850 			header_names = g_slist_append (header_names, (char *)name);
851 	}
852 
853 	for (i = 0, h = header_names; headers[i].name && h; i++, h = h->next) {
854 		g_assert (g_ascii_strcasecmp (h->data, headers[i].name) == 0);
855 
856 		value = soup_message_headers_get_list (hdrs, headers[i].name);
857 		g_assert_cmpstr (value, ==, headers[i].value);
858 	}
859 	/* If we have remaining fields to check, they should return NULL */
860 	for (; headers[i].name; i++) {
861 		value = soup_message_headers_get_list (hdrs, headers[i].name);
862 		g_assert_null (value);
863 	}
864 	g_assert_null (headers[i].name);
865 	g_assert_null (h);
866 
867 	g_slist_free (header_names);
868 }
869 
870 static void
do_request_tests(void)871 do_request_tests (void)
872 {
873 	int i, len;
874 	char *method, *path;
875 	SoupHTTPVersion version;
876 	SoupMessageHeaders *headers;
877 	guint status;
878 
879 	for (i = 0; i < num_reqtests; i++) {
880 		debug_printf (1, "%2d. %s (%s)\n", i + 1, reqtests[i].description,
881 			      soup_status_get_phrase (reqtests[i].status));
882 
883 		headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
884 		method = path = NULL;
885 
886 		if (reqtests[i].length == -1)
887 			len = strlen (reqtests[i].request);
888 		else
889 			len = reqtests[i].length;
890 		status = soup_headers_parse_request (reqtests[i].request, len,
891 						     headers, &method, &path,
892 						     &version);
893 		g_assert_cmpint (status, ==, reqtests[i].status);
894 		if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
895 			g_assert_cmpstr (method, ==, reqtests[i].method);
896 			g_assert_cmpstr (path, ==, reqtests[i].path);
897 			g_assert_cmpint (version, ==, reqtests[i].version);
898 
899 			check_headers (reqtests[i].headers, headers);
900 		}
901 
902 		g_free (method);
903 		g_free (path);
904 		soup_message_headers_free (headers);
905 	}
906 }
907 
908 static void
do_response_tests(void)909 do_response_tests (void)
910 {
911 	int i, len;
912 	guint status_code;
913 	char *reason_phrase;
914 	SoupHTTPVersion version;
915 	SoupMessageHeaders *headers;
916 
917 	for (i = 0; i < num_resptests; i++) {
918 		debug_printf (1, "%2d. %s (%s)\n", i + 1, resptests[i].description,
919 			      resptests[i].reason_phrase ? "should parse" : "should NOT parse");
920 
921 		headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
922 		reason_phrase = NULL;
923 
924 		if (resptests[i].length == -1)
925 			len = strlen (resptests[i].response);
926 		else
927 			len = resptests[i].length;
928 		if (soup_headers_parse_response (resptests[i].response, len,
929 						 headers, &version,
930 						 &status_code, &reason_phrase)) {
931 			g_assert_cmpint (version, ==, resptests[i].version);
932 			g_assert_cmpint (status_code, ==, resptests[i].status_code);
933 			g_assert_cmpstr (reason_phrase, ==, resptests[i].reason_phrase);
934 
935 			check_headers (resptests[i].headers, headers);
936 		} else
937 			g_assert_null (resptests[i].reason_phrase);
938 
939 		g_free (reason_phrase);
940 		soup_message_headers_free (headers);
941 	}
942 }
943 
944 static void
do_qvalue_tests(void)945 do_qvalue_tests (void)
946 {
947 	int i, j;
948 	GSList *acceptable, *unacceptable, *iter;
949 
950 	for (i = 0; i < num_qvaluetests; i++) {
951 		debug_printf (1, "%2d. %s:\n", i + 1, qvaluetests[i].header_value);
952 
953 		unacceptable = NULL;
954 		acceptable = soup_header_parse_quality_list (qvaluetests[i].header_value,
955 							     &unacceptable);
956 
957 		debug_printf (1, "    acceptable: ");
958 		if (acceptable) {
959 			/* Kludge to deal with the fact that the sort order of the first
960 			 * test is not fully specified.
961 			 */
962 			if (i == 0 && acceptable->next &&
963 			    !g_str_equal (acceptable->data, qvaluetests[i].acceptable[0]) &&
964 			    g_str_equal (acceptable->data, qvaluetests[i].acceptable[1])) {
965 				gpointer tmp = acceptable->data;
966 				acceptable->data = acceptable->next->data;
967 				acceptable->next->data = tmp;
968 			}
969 
970 			for (iter = acceptable, j = 0; iter; iter = iter->next, j++) {
971 				debug_printf (1, "%s ", (char *)iter->data);
972 				g_assert_cmpstr (iter->data, ==, qvaluetests[i].acceptable[j]);
973 			}
974 			debug_printf (1, "\n");
975 			soup_header_free_list (acceptable);
976 		} else
977 			debug_printf (1, "(none)\n");
978 
979 		debug_printf (1, "  unacceptable: ");
980 		if (unacceptable) {
981 			for (iter = unacceptable, j = 0; iter; iter = iter->next, j++) {
982 				debug_printf (1, "%s ", (char *)iter->data);
983 				g_assert_cmpstr (iter->data, ==, qvaluetests[i].unacceptable[j]);
984 			}
985 			debug_printf (1, "\n");
986 			soup_header_free_list (unacceptable);
987 		} else
988 			debug_printf (1, "(none)\n");
989 	}
990 }
991 
992 static void
do_param_list_tests(void)993 do_param_list_tests (void)
994 {
995 	int i, j, n_params;
996 	GHashTable* params;
997 
998 	for (i = 0; i < num_paramlisttests; i++) {
999 		params = soup_header_parse_semi_param_list (paramlisttests[i].header_value);
1000 		g_assert_nonnull (params);
1001 		n_params = paramlisttests[i].strict ? 3 : 2;
1002 		g_assert_cmpuint (g_hash_table_size (params), ==, n_params);
1003 		for (j = 0; j < n_params; j++) {
1004 			g_assert_cmpstr (g_hash_table_lookup (params, paramlisttests[i].results[j].param),
1005 					 ==, paramlisttests[i].results[j].value);
1006 		}
1007 		soup_header_free_param_list (params);
1008 	}
1009 
1010 	for (i = 0; i < num_paramlisttests; i++) {
1011 		params = soup_header_parse_semi_param_list_strict (paramlisttests[i].header_value);
1012 		if (paramlisttests[i].strict) {
1013 			g_assert_nonnull (params);
1014 			n_params = 3;
1015 			g_assert_cmpuint (g_hash_table_size (params), ==, n_params);
1016 			for (j = 0; j < n_params; j++) {
1017 				g_assert_cmpstr (g_hash_table_lookup (params, paramlisttests[i].results[j].param),
1018 						 ==, paramlisttests[i].results[j].value);
1019 			}
1020 			soup_header_free_param_list (params);
1021 		} else {
1022 			g_assert_null (params);
1023 		}
1024 	}
1025 }
1026 
1027 #define RFC5987_TEST_FILENAME "t\xC3\xA9st.txt"
1028 #define RFC5987_TEST_FALLBACK_FILENAME "test.txt"
1029 
1030 #define RFC5987_TEST_HEADER_ENCODED  "attachment; filename*=UTF-8''t%C3%A9st.txt"
1031 
1032 #define RFC5987_TEST_HEADER_UTF8     "attachment; filename*=UTF-8''t%C3%A9st.txt; filename=\"test.txt\""
1033 #define RFC5987_TEST_HEADER_ISO      "attachment; filename=\"test.txt\"; filename*=iso-8859-1''t%E9st.txt"
1034 #define RFC5987_TEST_HEADER_FALLBACK "attachment; filename*=Unknown''t%FF%FF%FFst.txt; filename=\"test.txt\""
1035 
1036 static void
do_content_disposition_tests(void)1037 do_content_disposition_tests (void)
1038 {
1039 	SoupMessageHeaders *hdrs;
1040 	GHashTable *params;
1041 	const char *header, *filename;
1042 	char *disposition;
1043 	SoupBuffer *buffer;
1044 	SoupMultipart *multipart;
1045 	SoupMessageBody *body;
1046 
1047 	hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1048 	params = g_hash_table_new (g_str_hash, g_str_equal);
1049 	g_hash_table_insert (params, "filename", RFC5987_TEST_FILENAME);
1050 	soup_message_headers_set_content_disposition (hdrs, "attachment", params);
1051 	g_hash_table_destroy (params);
1052 
1053 	header = soup_message_headers_get_one (hdrs, "Content-Disposition");
1054 	g_assert_cmpstr (header, ==, RFC5987_TEST_HEADER_ENCODED);
1055 
1056 	/* UTF-8 decoding */
1057 	soup_message_headers_clear (hdrs);
1058 	soup_message_headers_append (hdrs, "Content-Disposition",
1059 				     RFC5987_TEST_HEADER_UTF8);
1060 	if (!soup_message_headers_get_content_disposition (hdrs,
1061 							   &disposition,
1062 							   &params)) {
1063 		soup_test_assert (FALSE, "UTF-8 decoding FAILED");
1064 		return;
1065 	}
1066 	g_free (disposition);
1067 
1068 	filename = g_hash_table_lookup (params, "filename");
1069 	g_assert_cmpstr (filename, ==, RFC5987_TEST_FILENAME);
1070 	g_hash_table_destroy (params);
1071 
1072 	/* ISO-8859-1 decoding */
1073 	soup_message_headers_clear (hdrs);
1074 	soup_message_headers_append (hdrs, "Content-Disposition",
1075 				     RFC5987_TEST_HEADER_ISO);
1076 	if (!soup_message_headers_get_content_disposition (hdrs,
1077 							   &disposition,
1078 							   &params)) {
1079 		soup_test_assert (FALSE, "iso-8859-1 decoding FAILED");
1080 		return;
1081 	}
1082 	g_free (disposition);
1083 
1084 	filename = g_hash_table_lookup (params, "filename");
1085 	g_assert_cmpstr (filename, ==, RFC5987_TEST_FILENAME);
1086 	g_hash_table_destroy (params);
1087 
1088 	/* Fallback */
1089 	soup_message_headers_clear (hdrs);
1090 	soup_message_headers_append (hdrs, "Content-Disposition",
1091 				     RFC5987_TEST_HEADER_FALLBACK);
1092 	if (!soup_message_headers_get_content_disposition (hdrs,
1093 							   &disposition,
1094 							   &params)) {
1095 		soup_test_assert (FALSE, "fallback decoding FAILED");
1096 		return;
1097 	}
1098 	g_free (disposition);
1099 
1100 	filename = g_hash_table_lookup (params, "filename");
1101 	g_assert_cmpstr (filename, ==, RFC5987_TEST_FALLBACK_FILENAME);
1102 	g_hash_table_destroy (params);
1103 
1104 	soup_message_headers_free (hdrs);
1105 
1106 	/* Ensure that soup-multipart always quotes filename */
1107 	g_test_bug ("641280");
1108 	multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
1109 	buffer = soup_buffer_new (SOUP_MEMORY_STATIC, "foo", 3);
1110 	soup_multipart_append_form_file (multipart, "test", "token",
1111 					 "text/plain", buffer);
1112 	soup_buffer_free (buffer);
1113 
1114 	hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1115 	body = soup_message_body_new ();
1116 	soup_multipart_to_message (multipart, hdrs, body);
1117 	soup_message_headers_free (hdrs);
1118 	soup_multipart_free (multipart);
1119 
1120 	buffer = soup_message_body_flatten (body);
1121 	soup_message_body_free (body);
1122 
1123 	g_assert_true (strstr (buffer->data, "filename=\"token\""));
1124 
1125 	soup_buffer_free (buffer);
1126 }
1127 
1128 #define CONTENT_TYPE_TEST_MIME_TYPE "text/plain"
1129 #define CONTENT_TYPE_TEST_ATTRIBUTE "charset"
1130 #define CONTENT_TYPE_TEST_VALUE     "US-ASCII"
1131 #define CONTENT_TYPE_TEST_HEADER    "text/plain; charset=US-ASCII"
1132 
1133 #define CONTENT_TYPE_BAD_HEADER     "plain text, not text/html"
1134 
1135 static void
do_content_type_tests(void)1136 do_content_type_tests (void)
1137 {
1138 	SoupMessageHeaders *hdrs;
1139 	GHashTable *params;
1140 	const char *header, *mime_type;
1141 
1142 	g_test_bug ("576760");
1143 
1144 	hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1145 	params = g_hash_table_new (g_str_hash, g_str_equal);
1146 	g_hash_table_insert (params, CONTENT_TYPE_TEST_ATTRIBUTE,
1147 			     CONTENT_TYPE_TEST_VALUE);
1148 	soup_message_headers_set_content_type (hdrs, CONTENT_TYPE_TEST_MIME_TYPE, params);
1149 	g_hash_table_destroy (params);
1150 
1151 	header = soup_message_headers_get_one (hdrs, "Content-Type");
1152 	g_assert_cmpstr (header, ==, CONTENT_TYPE_TEST_HEADER);
1153 
1154 	soup_message_headers_clear (hdrs);
1155 	soup_message_headers_append (hdrs, "Content-Type",
1156 				     CONTENT_TYPE_TEST_MIME_TYPE);
1157 	/* Add a second Content-Type header: should be ignored */
1158 	soup_message_headers_append (hdrs, "Content-Type",
1159 				     CONTENT_TYPE_TEST_MIME_TYPE);
1160 
1161 	mime_type = soup_message_headers_get_content_type (hdrs, &params);
1162 	g_assert_cmpstr (mime_type, ==, CONTENT_TYPE_TEST_MIME_TYPE);
1163 	g_assert_cmpint (g_hash_table_size (params), ==, 0);
1164 	if (params)
1165 		g_hash_table_destroy (params);
1166 
1167 	g_test_bug ("577630");
1168 
1169 	soup_message_headers_clear (hdrs);
1170 	soup_message_headers_append (hdrs, "Content-Type",
1171 				     CONTENT_TYPE_BAD_HEADER);
1172 	mime_type = soup_message_headers_get_content_type (hdrs, &params);
1173 	g_assert_null (mime_type);
1174 
1175 	soup_message_headers_free (hdrs);
1176 }
1177 
1178 struct {
1179 	const char *name, *value;
1180 } test_params[] = {
1181 	{ "one", "foo" },
1182 	{ "two", "test with spaces" },
1183 	{ "three", "test with \"quotes\" and \\s" },
1184 	{ "four", NULL },
1185 	{ "five", "test with \xC3\xA1\xC3\xA7\xC4\x89\xC3\xA8\xC3\xB1\xC5\xA3\xC5\xA1" }
1186 };
1187 
1188 #define TEST_PARAMS_RESULT "one=foo, two=\"test with spaces\", three=\"test with \\\"quotes\\\" and \\\\s\", four, five*=UTF-8''test%20with%20%C3%A1%C3%A7%C4%89%C3%A8%C3%B1%C5%A3%C5%A1"
1189 
1190 static void
do_append_param_tests(void)1191 do_append_param_tests (void)
1192 {
1193 	GString *params;
1194 	int i;
1195 
1196 	g_test_bug ("577728");
1197 
1198 	params = g_string_new (NULL);
1199 	for (i = 0; i < G_N_ELEMENTS (test_params); i++) {
1200 		if (i > 0)
1201 			g_string_append (params, ", ");
1202 		soup_header_g_string_append_param (params,
1203 						   test_params[i].name,
1204 						   test_params[i].value);
1205 	}
1206 	g_assert_cmpstr (params->str, ==, TEST_PARAMS_RESULT);
1207 	g_string_free (params, TRUE);
1208 }
1209 
1210 static const struct {
1211 	const char *description, *name, *value;
1212 } bad_headers[] = {
1213 	{ "Empty name", "", "value" },
1214 	{ "Name with spaces", "na me", "value" },
1215 	{ "Name with colon", "na:me", "value" },
1216 	{ "Name with CR", "na\rme", "value" },
1217 	{ "Name with LF", "na\nme", "value" },
1218 	{ "Name with tab", "na\tme", "value" },
1219 	{ "Value with CR", "name", "val\rue" },
1220 	{ "Value with LF", "name", "val\nue" },
1221 	{ "Value with LWS", "name", "val\r\n ue" }
1222 };
1223 
1224 static void
do_bad_header_tests(void)1225 do_bad_header_tests (void)
1226 {
1227 	SoupMessageHeaders *hdrs;
1228 	int i;
1229 
1230 	hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1231 	for (i = 0; i < G_N_ELEMENTS (bad_headers); i++) {
1232 		debug_printf (1, "  %s\n", bad_headers[i].description);
1233 
1234 		g_test_expect_message ("libsoup", G_LOG_LEVEL_CRITICAL,
1235 				       "*soup_message_headers_append*assertion*failed*");
1236 		soup_message_headers_append (hdrs, bad_headers[i].name,
1237 					     bad_headers[i].value);
1238 		g_test_assert_expected_messages ();
1239 	}
1240 	soup_message_headers_free (hdrs);
1241 }
1242 
1243 int
main(int argc,char ** argv)1244 main (int argc, char **argv)
1245 {
1246 	int ret;
1247 
1248 	test_init (argc, argv, NULL);
1249 
1250 	g_test_add_func ("/header-parsing/request", do_request_tests);
1251 	g_test_add_func ("/header-parsing/response", do_response_tests);
1252 	g_test_add_func ("/header-parsing/qvalue", do_qvalue_tests);
1253 	g_test_add_func ("/header-parsing/param-list", do_param_list_tests);
1254 	g_test_add_func ("/header-parsing/content-disposition", do_content_disposition_tests);
1255 	g_test_add_func ("/header-parsing/content-type", do_content_type_tests);
1256 	g_test_add_func ("/header-parsing/append-param", do_append_param_tests);
1257 	g_test_add_func ("/header-parsing/bad", do_bad_header_tests);
1258 
1259 	ret = g_test_run ();
1260 
1261 	test_cleanup ();
1262 	return ret;
1263 }
1264