1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-message-headers.c: HTTP message header arrays
5 * Copyright (C) 2007, 2008 Red Hat, Inc.
11 #include "soup-message-headers.h"
12 #include "soup-headers.h"
13 #include "soup-misc.h"
16 * SECTION:soup-message-headers
17 * @short_description: HTTP message headers
18 * @see_also: #SoupMessage
20 * #SoupMessageHeaders represents the HTTP message headers associated
21 * with a request or response.
27 * The HTTP message headers associated with a request or response.
31 * SoupMessageHeadersType:
32 * @SOUP_MESSAGE_HEADERS_REQUEST: request headers
33 * @SOUP_MESSAGE_HEADERS_RESPONSE: response headers
34 * @SOUP_MESSAGE_HEADERS_MULTIPART: multipart body part headers
36 * Value passed to soup_message_headers_new() to set certain default
40 typedef void (*SoupHeaderSetter) (SoupMessageHeaders *, const char *);
41 static const char *intern_header_name (const char *name, SoupHeaderSetter *setter);
42 static void clear_special_headers (SoupMessageHeaders *hdrs);
49 struct SoupMessageHeaders {
52 SoupMessageHeadersType type;
54 SoupEncoding encoding;
55 goffset content_length;
56 SoupExpectation expectations;
63 * soup_message_headers_new:
64 * @type: the type of headers
66 * Creates a #SoupMessageHeaders. (#SoupMessage does this
67 * automatically for its own headers. You would only need to use this
68 * method if you are manually parsing or generating message headers.)
70 * Return value: a new #SoupMessageHeaders
73 soup_message_headers_new (SoupMessageHeadersType type)
75 SoupMessageHeaders *hdrs;
77 hdrs = g_slice_new0 (SoupMessageHeaders);
78 /* FIXME: is "5" a good default? */
79 hdrs->array = g_array_sized_new (TRUE, FALSE, sizeof (SoupHeader), 5);
87 static SoupMessageHeaders *
88 soup_message_headers_copy (SoupMessageHeaders *hdrs)
95 * soup_message_headers_free:
96 * @hdrs: a #SoupMessageHeaders
101 soup_message_headers_free (SoupMessageHeaders *hdrs)
103 if (--hdrs->ref_count == 0) {
104 soup_message_headers_clear (hdrs);
105 g_array_free (hdrs->array, TRUE);
107 g_hash_table_destroy (hdrs->concat);
108 g_slice_free (SoupMessageHeaders, hdrs);
113 soup_message_headers_get_type (void)
115 static volatile gsize type_volatile = 0;
117 if (g_once_init_enter (&type_volatile)) {
118 GType type = g_boxed_type_register_static (
119 g_intern_static_string ("SoupMessageHeaders"),
120 (GBoxedCopyFunc) soup_message_headers_copy,
121 (GBoxedFreeFunc) soup_message_headers_free);
122 g_once_init_leave (&type_volatile, type);
124 return type_volatile;
128 * soup_message_headers_clear:
129 * @hdrs: a #SoupMessageHeaders
134 soup_message_headers_clear (SoupMessageHeaders *hdrs)
136 SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
139 for (i = 0; i < hdrs->array->len; i++)
140 g_free (hdr_array[i].value);
141 g_array_set_size (hdrs->array, 0);
144 g_hash_table_remove_all (hdrs->concat);
146 clear_special_headers (hdrs);
150 * soup_message_headers_clean_connection_headers:
151 * @hdrs: a #SoupMessageHeaders
153 * Removes all the headers listed in the Connection header.
158 soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs)
161 const char *connection;
164 connection = soup_message_headers_get_list (hdrs, "Connection");
168 tokens = soup_header_parse_list (connection);
169 for (t = tokens; t; t = t->next)
170 soup_message_headers_remove (hdrs, t->data);
171 soup_header_free_list (tokens);
175 * soup_message_headers_append:
176 * @hdrs: a #SoupMessageHeaders
177 * @name: the header name to add
178 * @value: the new value of @name
180 * Appends a new header with name @name and value @value to @hdrs. (If
181 * there is an existing header with name @name, then this creates a
182 * second one, which is only allowed for list-valued headers; see also
183 * soup_message_headers_replace().)
185 * The caller is expected to make sure that @name and @value are
186 * syntactically correct.
189 soup_message_headers_append (SoupMessageHeaders *hdrs,
190 const char *name, const char *value)
193 SoupHeaderSetter setter;
195 g_return_if_fail (name != NULL);
196 g_return_if_fail (value != NULL);
198 /* Setting a syntactically invalid header name or value is
199 * considered to be a programming error. However, it can also
200 * be a security hole, so we want to fail here even if
201 * compiled with G_DISABLE_CHECKS.
203 #ifndef G_DISABLE_CHECKS
204 g_return_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL);
205 g_return_if_fail (strpbrk (value, "\r\n") == NULL);
207 if (*name && strpbrk (name, " \t\r\n:")) {
208 g_warning ("soup_message_headers_append: Ignoring bad name '%s'", name);
211 if (strpbrk (value, "\r\n")) {
212 g_warning ("soup_message_headers_append: Ignoring bad value '%s'", value);
217 header.name = intern_header_name (name, &setter);
218 header.value = g_strdup (value);
219 g_array_append_val (hdrs->array, header);
221 g_hash_table_remove (hdrs->concat, header.name);
223 setter (hdrs, header.value);
227 * soup_message_headers_replace:
228 * @hdrs: a #SoupMessageHeaders
229 * @name: the header name to replace
230 * @value: the new value of @name
232 * Replaces the value of the header @name in @hdrs with @value. (See
233 * also soup_message_headers_append().)
235 * The caller is expected to make sure that @name and @value are
236 * syntactically correct.
239 soup_message_headers_replace (SoupMessageHeaders *hdrs,
240 const char *name, const char *value)
242 soup_message_headers_remove (hdrs, name);
243 soup_message_headers_append (hdrs, name, value);
247 find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
251 for (i = 0; hdr_array[i].name; i++) {
252 if (hdr_array[i].name == interned_name) {
261 find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name, int nth)
265 for (i = length; i >= 0; i--) {
266 if (hdr_array[i].name == interned_name) {
275 * soup_message_headers_remove:
276 * @hdrs: a #SoupMessageHeaders
277 * @name: the header name to remove
279 * Removes @name from @hdrs. If there are multiple values for @name,
280 * they are all removed.
283 soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
285 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
286 SoupHeaderSetter setter;
289 g_return_if_fail (name != NULL);
291 name = intern_header_name (name, &setter);
292 while ((index = find_header (hdr_array, name, 0)) != -1) {
293 g_free (hdr_array[index].value);
294 g_array_remove_index (hdrs->array, index);
297 g_hash_table_remove (hdrs->concat, name);
303 * soup_message_headers_get_one:
304 * @hdrs: a #SoupMessageHeaders
307 * Gets the value of header @name in @hdrs. Use this for headers whose
308 * values are <emphasis>not</emphasis> comma-delimited lists, and
309 * which therefore can only appear at most once in the headers. For
310 * list-valued headers, use soup_message_headers_get_list().
312 * If @hdrs does erroneously contain multiple copies of the header, it
313 * is not defined which one will be returned. (Ideally, it will return
314 * whichever one makes libsoup most compatible with other HTTP
317 * Return value: the header's value or %NULL if not found.
322 soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
324 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
325 guint hdr_length = hdrs->array->len;
328 g_return_val_if_fail (name != NULL, NULL);
330 name = intern_header_name (name, NULL);
332 index = find_last_header (hdr_array, hdr_length, name, 0);
334 return (index == -1) ? NULL : hdr_array[index].value;
338 * soup_message_headers_get_list:
339 * @hdrs: a #SoupMessageHeaders
342 * Gets the value of header @name in @hdrs. Use this for headers whose
343 * values are comma-delimited lists, and which are therefore allowed
344 * to appear multiple times in the headers. For non-list-valued
345 * headers, use soup_message_headers_get_one().
347 * If @name appears multiple times in @hdrs,
348 * soup_message_headers_get_list() will concatenate all of the values
349 * together, separated by commas. This is sometimes awkward to parse
350 * (eg, WWW-Authenticate, Set-Cookie), but you have to be able to deal
351 * with it anyway, because the HTTP spec explicitly states that this
352 * transformation is allowed, and so an upstream proxy could do the
355 * Return value: the header's value or %NULL if not found.
360 soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name)
362 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
367 g_return_val_if_fail (name != NULL, NULL);
369 name = intern_header_name (name, NULL);
371 value = g_hash_table_lookup (hdrs->concat, name);
376 index = find_header (hdr_array, name, 0);
379 else if (find_header (hdr_array, name, 1) == -1)
380 return hdr_array[index].value;
382 concat = g_string_new (NULL);
383 for (i = 0; (index = find_header (hdr_array, name, i)) != -1; i++) {
385 g_string_append (concat, ", ");
386 g_string_append (concat, hdr_array[index].value);
388 value = g_string_free (concat, FALSE);
391 hdrs->concat = g_hash_table_new_full (NULL, NULL, NULL, g_free);
392 g_hash_table_insert (hdrs->concat, (gpointer)name, value);
397 * soup_message_headers_get:
398 * @hdrs: a #SoupMessageHeaders
401 * Gets the value of header @name in @hdrs.
403 * This method was supposed to work correctly for both single-valued
404 * and list-valued headers, but because some HTTP clients/servers
405 * mistakenly send multiple copies of headers that are supposed to be
406 * single-valued, it sometimes returns incorrect results. To fix this,
407 * the methods soup_message_headers_get_one() and
408 * soup_message_headers_get_list() were introduced, so callers can
409 * explicitly state which behavior they are expecting.
411 * Return value: as with soup_message_headers_get_list().
413 * Deprecated: Use soup_message_headers_get_one() or
414 * soup_message_headers_get_list() instead.
417 soup_message_headers_get (SoupMessageHeaders *hdrs, const char *name)
419 return soup_message_headers_get_list (hdrs, name);
423 * SoupMessageHeadersIter:
425 * An opaque type used to iterate over a %SoupMessageHeaders
428 * After intializing the iterator with
429 * soup_message_headers_iter_init(), call
430 * soup_message_headers_iter_next() to fetch data from it.
432 * You may not modify the headers while iterating over them.
436 SoupMessageHeaders *hdrs;
438 } SoupMessageHeadersIterReal;
441 * soup_message_headers_iter_init:
442 * @iter: (out) (transfer none): a pointer to a %SoupMessageHeadersIter
444 * @hdrs: a %SoupMessageHeaders
446 * Initializes @iter for iterating @hdrs.
449 soup_message_headers_iter_init (SoupMessageHeadersIter *iter,
450 SoupMessageHeaders *hdrs)
452 SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
459 * soup_message_headers_iter_next:
460 * @iter: (inout) (transfer none): a %SoupMessageHeadersIter
461 * @name: (out) (transfer none): pointer to a variable to return
463 * @value: (out) (transfer none): pointer to a variable to return
464 * the header value in
466 * Yields the next name/value pair in the %SoupMessageHeaders being
467 * iterated by @iter. If @iter has already yielded the last header,
468 * then soup_message_headers_iter_next() will return %FALSE and @name
469 * and @value will be unchanged.
471 * Return value: %TRUE if another name and value were returned, %FALSE
472 * if the end of the headers has been reached.
475 soup_message_headers_iter_next (SoupMessageHeadersIter *iter,
476 const char **name, const char **value)
478 SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
479 SoupHeader *hdr_array = (SoupHeader *)real->hdrs->array->data;
481 if (real->index >= real->hdrs->array->len)
484 *name = hdr_array[real->index].name;
485 *value = hdr_array[real->index].value;
491 * SoupMessageHeadersForeachFunc:
492 * @name: the header name
493 * @value: the header value
494 * @user_data: the data passed to soup_message_headers_foreach()
496 * The callback passed to soup_message_headers_foreach().
500 * soup_message_headers_foreach:
501 * @hdrs: a #SoupMessageHeaders
502 * @func: (scope call): callback function to run for each header
503 * @user_data: data to pass to @func
505 * Calls @func once for each header value in @hdrs.
507 * Beware that unlike soup_message_headers_get(), this processes the
508 * headers in exactly the way they were added, rather than
509 * concatenating multiple same-named headers into a single value.
510 * (This is intentional; it ensures that if you call
511 * soup_message_headers_append() multiple times with the same name,
512 * then the I/O code will output multiple copies of the header when
513 * sending the message to the remote implementation, which may be
514 * required for interoperability in some cases.)
516 * You may not modify the headers from @func.
519 soup_message_headers_foreach (SoupMessageHeaders *hdrs,
520 SoupMessageHeadersForeachFunc func,
523 SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
526 for (i = 0; i < hdrs->array->len; i++)
527 func (hdr_array[i].name, hdr_array[i].value, user_data);
531 G_LOCK_DEFINE_STATIC (header_pool);
532 static GHashTable *header_pool, *header_setters;
534 static void transfer_encoding_setter (SoupMessageHeaders *, const char *);
535 static void content_length_setter (SoupMessageHeaders *, const char *);
536 static void expectation_setter (SoupMessageHeaders *, const char *);
537 static void content_type_setter (SoupMessageHeaders *, const char *);
540 intern_header_locked (const char *name)
544 interned = g_hash_table_lookup (header_pool, name);
546 char *dup = g_strdup (name);
547 g_hash_table_insert (header_pool, dup, dup);
554 intern_header_name (const char *name, SoupHeaderSetter *setter)
556 const char *interned;
558 G_LOCK (header_pool);
561 header_pool = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
562 header_setters = g_hash_table_new (NULL, NULL);
563 g_hash_table_insert (header_setters,
564 intern_header_locked ("Transfer-Encoding"),
565 transfer_encoding_setter);
566 g_hash_table_insert (header_setters,
567 intern_header_locked ("Content-Length"),
568 content_length_setter);
569 g_hash_table_insert (header_setters,
570 intern_header_locked ("Expect"),
572 g_hash_table_insert (header_setters,
573 intern_header_locked ("Content-Type"),
574 content_type_setter);
577 interned = intern_header_locked (name);
579 *setter = g_hash_table_lookup (header_setters, interned);
581 G_UNLOCK (header_pool);
586 clear_special_headers (SoupMessageHeaders *hdrs)
588 SoupHeaderSetter setter;
592 /* Make sure header_setters has been initialized */
593 intern_header_name ("", NULL);
595 g_hash_table_iter_init (&iter, header_setters);
596 while (g_hash_table_iter_next (&iter, &key, &value)) {
602 /* Specific headers */
605 transfer_encoding_setter (SoupMessageHeaders *hdrs, const char *value)
608 if (g_ascii_strcasecmp (value, "chunked") == 0)
609 hdrs->encoding = SOUP_ENCODING_CHUNKED;
611 hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
617 content_length_setter (SoupMessageHeaders *hdrs, const char *value)
619 /* Transfer-Encoding trumps Content-Length */
620 if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
626 hdrs->content_length = g_ascii_strtoull (value, &end, 10);
628 hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
630 hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
637 * @SOUP_ENCODING_UNRECOGNIZED: unknown / error
638 * @SOUP_ENCODING_NONE: no body is present (which is not the same as a
639 * 0-length body, and only occurs in certain places)
640 * @SOUP_ENCODING_CONTENT_LENGTH: Content-Length encoding
641 * @SOUP_ENCODING_EOF: Response body ends when the connection is closed
642 * @SOUP_ENCODING_CHUNKED: chunked encoding (currently only supported
644 * @SOUP_ENCODING_BYTERANGES: multipart/byteranges (Reserved for future
645 * use: NOT CURRENTLY IMPLEMENTED)
647 * How a message body is encoded for transport
651 * soup_message_headers_get_encoding:
652 * @hdrs: a #SoupMessageHeaders
654 * Gets the message body encoding that @hdrs declare. This may not
655 * always correspond to the encoding used on the wire; eg, a HEAD
656 * response may declare a Content-Length or Transfer-Encoding, but
657 * it will never actually include a body.
659 * Return value: the encoding declared by @hdrs.
662 soup_message_headers_get_encoding (SoupMessageHeaders *hdrs)
666 if (hdrs->encoding != -1)
667 return hdrs->encoding;
669 /* If Transfer-Encoding was set, hdrs->encoding would already
670 * be set. So we don't need to check that possibility.
672 header = soup_message_headers_get_one (hdrs, "Content-Length");
674 content_length_setter (hdrs, header);
675 if (hdrs->encoding != -1)
676 return hdrs->encoding;
679 /* Per RFC 2616 4.4, a response body that doesn't indicate its
680 * encoding otherwise is terminated by connection close, and a
681 * request that doesn't indicate otherwise has no body. Note
682 * that SoupMessage calls soup_message_headers_set_encoding()
683 * to override the response body default for our own
684 * server-side messages.
686 hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
687 SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
688 return hdrs->encoding;
692 * soup_message_headers_set_encoding:
693 * @hdrs: a #SoupMessageHeaders
694 * @encoding: a #SoupEncoding
696 * Sets the message body encoding that @hdrs will declare. In particular,
697 * you should use this if you are going to send a request or response in
701 soup_message_headers_set_encoding (SoupMessageHeaders *hdrs,
702 SoupEncoding encoding)
704 if (encoding == hdrs->encoding)
708 case SOUP_ENCODING_NONE:
709 case SOUP_ENCODING_EOF:
710 soup_message_headers_remove (hdrs, "Transfer-Encoding");
711 soup_message_headers_remove (hdrs, "Content-Length");
714 case SOUP_ENCODING_CONTENT_LENGTH:
715 soup_message_headers_remove (hdrs, "Transfer-Encoding");
718 case SOUP_ENCODING_CHUNKED:
719 soup_message_headers_remove (hdrs, "Content-Length");
720 soup_message_headers_replace (hdrs, "Transfer-Encoding", "chunked");
724 g_return_if_reached ();
727 hdrs->encoding = encoding;
731 * soup_message_headers_get_content_length:
732 * @hdrs: a #SoupMessageHeaders
734 * Gets the message body length that @hdrs declare. This will only
735 * be non-0 if soup_message_headers_get_encoding() returns
736 * %SOUP_ENCODING_CONTENT_LENGTH.
738 * Return value: the message body length declared by @hdrs.
741 soup_message_headers_get_content_length (SoupMessageHeaders *hdrs)
743 return (hdrs->encoding == SOUP_ENCODING_CONTENT_LENGTH) ?
744 hdrs->content_length : 0;
748 * soup_message_headers_set_content_length:
749 * @hdrs: a #SoupMessageHeaders
750 * @content_length: the message body length
752 * Sets the message body length that @hdrs will declare, and sets
753 * @hdrs's encoding to %SOUP_ENCODING_CONTENT_LENGTH.
755 * You do not normally need to call this; if @hdrs is set to use
756 * Content-Length encoding, libsoup will automatically set its
757 * Content-Length header for you immediately before sending the
758 * headers. One situation in which this method is useful is when
759 * generating the response to a HEAD request; Calling
760 * soup_message_headers_set_content_length() allows you to put the
761 * correct content length into the response without needing to waste
762 * memory by filling in a response body which won't actually be sent.
765 soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
766 goffset content_length)
770 snprintf (length, sizeof (length), "%" G_GUINT64_FORMAT,
772 soup_message_headers_remove (hdrs, "Transfer-Encoding");
773 soup_message_headers_replace (hdrs, "Content-Length", length);
777 expectation_setter (SoupMessageHeaders *hdrs, const char *value)
780 if (!g_ascii_strcasecmp (value, "100-continue"))
781 hdrs->expectations = SOUP_EXPECTATION_CONTINUE;
783 hdrs->expectations = SOUP_EXPECTATION_UNRECOGNIZED;
785 hdrs->expectations = 0;
790 * @SOUP_EXPECTATION_CONTINUE: "100-continue"
791 * @SOUP_EXPECTATION_UNRECOGNIZED: any unrecognized expectation
793 * Represents the parsed value of the "Expect" header.
797 * soup_message_headers_get_expectations:
798 * @hdrs: a #SoupMessageHeaders
800 * Gets the expectations declared by @hdrs's "Expect" header.
801 * Currently this will either be %SOUP_EXPECTATION_CONTINUE or
802 * %SOUP_EXPECTATION_UNRECOGNIZED.
804 * Return value: the contents of @hdrs's "Expect" header
807 soup_message_headers_get_expectations (SoupMessageHeaders *hdrs)
809 return hdrs->expectations;
813 * soup_message_headers_set_expectations:
814 * @hdrs: a #SoupMessageHeaders
815 * @expectations: the expectations to set
817 * Sets @hdrs's "Expect" header according to @expectations.
819 * Currently %SOUP_EXPECTATION_CONTINUE is the only known expectation
820 * value. You should set this value on a request if you are sending a
821 * large message body (eg, via POST or PUT), and want to give the
822 * server a chance to reject the request after seeing just the headers
823 * (eg, because it will require authentication before allowing you to
824 * post, or because you're POSTing to a URL that doesn't exist). This
825 * saves you from having to transmit the large request body when the
826 * server is just going to ignore it anyway.
829 soup_message_headers_set_expectations (SoupMessageHeaders *hdrs,
830 SoupExpectation expectations)
832 g_return_if_fail ((expectations & ~SOUP_EXPECTATION_CONTINUE) == 0);
834 if (expectations & SOUP_EXPECTATION_CONTINUE)
835 soup_message_headers_replace (hdrs, "Expect", "100-continue");
837 soup_message_headers_remove (hdrs, "Expect");
842 * @start: the start of the range
843 * @end: the end of the range
845 * Represents a byte range as used in the Range header.
847 * If @end is non-negative, then @start and @end represent the bounds
848 * of of the range, counting from 0. (Eg, the first 500 bytes would be
849 * represented as @start = 0 and @end = 499.)
851 * If @end is -1 and @start is non-negative, then this represents a
852 * range starting at @start and ending with the last byte of the
853 * requested resource body. (Eg, all but the first 500 bytes would be
854 * @start = 500, and @end = -1.)
856 * If @end is -1 and @start is negative, then it represents a "suffix
857 * range", referring to the last -@start bytes of the resource body.
858 * (Eg, the last 500 bytes would be @start = -500 and @end = -1.)
864 sort_ranges (gconstpointer a, gconstpointer b)
866 SoupRange *ra = (SoupRange *)a;
867 SoupRange *rb = (SoupRange *)b;
869 return ra->start - rb->start;
873 * soup_message_headers_get_ranges:
874 * @hdrs: a #SoupMessageHeaders
875 * @total_length: the total_length of the response body
876 * @ranges: (out): return location for an array of #SoupRange
877 * @length: the length of the returned array
879 * Parses @hdrs's Range header and returns an array of the requested
880 * byte ranges. The returned array must be freed with
881 * soup_message_headers_free_ranges().
883 * If @total_length is non-0, its value will be used to adjust the
884 * returned ranges to have explicit start and end values, and the
885 * returned ranges will be sorted and non-overlapping. If
886 * @total_length is 0, then some ranges may have an end value of -1,
887 * as described under #SoupRange, and some of the ranges may be
890 * Return value: %TRUE if @hdrs contained a "Range" header containing
891 * byte ranges which could be parsed, %FALSE otherwise (in which case
892 * @range and @length will not be set).
897 soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
898 goffset total_length,
902 const char *range = soup_message_headers_get_one (hdrs, "Range");
903 GSList *range_list, *r;
908 if (!range || strncmp (range, "bytes", 5) != 0)
912 while (g_ascii_isspace (*range))
916 while (g_ascii_isspace (*range))
919 range_list = soup_header_parse_list (range);
923 array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
924 for (r = range_list; r; r = r->next) {
929 cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
930 cur.end = total_length - 1;
932 cur.start = g_ascii_strtoull (spec, &end, 10);
936 cur.end = g_ascii_strtoull (end, &end, 10);
938 cur.end = total_length - 1;
941 g_array_free (array, TRUE);
942 soup_header_free_list (range_list);
946 g_array_append_val (array, cur);
949 soup_header_free_list (range_list);
952 g_array_sort (array, sort_ranges);
953 for (i = 1; i < array->len; i++) {
954 SoupRange *cur = &((SoupRange *)array->data)[i];
955 SoupRange *prev = &((SoupRange *)array->data)[i - 1];
957 if (cur->start <= prev->end) {
958 prev->end = MAX (prev->end, cur->end);
959 g_array_remove_index (array, i);
964 *ranges = (SoupRange *)array->data;
965 *length = array->len;
967 g_array_free (array, FALSE);
972 * soup_message_headers_free_ranges:
973 * @hdrs: a #SoupMessageHeaders
974 * @ranges: an array of #SoupRange
976 * Frees the array of ranges returned from soup_message_headers_get_ranges().
981 soup_message_headers_free_ranges (SoupMessageHeaders *hdrs,
988 * soup_message_headers_set_ranges:
989 * @hdrs: a #SoupMessageHeaders
990 * @ranges: an array of #SoupRange
991 * @length: the length of @range
993 * Sets @hdrs's Range header to request the indicated ranges. (If you
994 * only want to request a single range, you can use
995 * soup_message_headers_set_range().)
1000 soup_message_headers_set_ranges (SoupMessageHeaders *hdrs,
1007 header = g_string_new ("bytes=");
1008 for (i = 0; i < length; i++) {
1010 g_string_append_c (header, ',');
1011 if (ranges[i].end >= 0) {
1012 g_string_append_printf (header, "%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT,
1013 ranges[i].start, ranges[i].end);
1014 } else if (ranges[i].start >= 0) {
1015 g_string_append_printf (header,"%" G_GINT64_FORMAT "-",
1018 g_string_append_printf (header, "%" G_GINT64_FORMAT,
1023 soup_message_headers_replace (hdrs, "Range", header->str);
1024 g_string_free (header, TRUE);
1028 * soup_message_headers_set_range:
1029 * @hdrs: a #SoupMessageHeaders
1030 * @start: the start of the range to request
1031 * @end: the end of the range to request
1033 * Sets @hdrs's Range header to request the indicated range.
1034 * @start and @end are interpreted as in a #SoupRange.
1036 * If you need to request multiple ranges, use
1037 * soup_message_headers_set_ranges().
1042 soup_message_headers_set_range (SoupMessageHeaders *hdrs,
1048 range.start = start;
1050 soup_message_headers_set_ranges (hdrs, &range, 1);
1054 * soup_message_headers_get_content_range:
1055 * @hdrs: a #SoupMessageHeaders
1056 * @start: return value for the start of the range
1057 * @end: return value for the end of the range
1058 * @total_length: return value for the total length of the resource,
1059 * or %NULL if you don't care.
1061 * Parses @hdrs's Content-Range header and returns it in @start,
1062 * @end, and @total_length. If the total length field in the header
1063 * was specified as "*", then @total_length will be set to -1.
1065 * Return value: %TRUE if @hdrs contained a "Content-Range" header
1066 * containing a byte range which could be parsed, %FALSE otherwise.
1071 soup_message_headers_get_content_range (SoupMessageHeaders *hdrs,
1074 goffset *total_length)
1076 const char *header = soup_message_headers_get_one (hdrs, "Content-Range");
1080 if (!header || strncmp (header, "bytes ", 6) != 0)
1084 while (g_ascii_isspace (*header))
1086 if (!g_ascii_isdigit (*header))
1089 *start = g_ascii_strtoull (header, &p, 10);
1092 *end = g_ascii_strtoull (p + 1, &p, 10);
1100 length = g_ascii_strtoull (p, &p, 10);
1103 *total_length = length;
1108 * soup_message_headers_set_content_range:
1109 * @hdrs: a #SoupMessageHeaders
1110 * @start: the start of the range
1111 * @end: the end of the range
1112 * @total_length: the total length of the resource, or -1 if unknown
1114 * Sets @hdrs's Content-Range header according to the given values.
1115 * (Note that @total_length is the total length of the entire resource
1116 * that this is a range of, not simply @end - @start + 1.)
1121 soup_message_headers_set_content_range (SoupMessageHeaders *hdrs,
1124 goffset total_length)
1128 if (total_length >= 0) {
1129 header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
1130 G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
1131 start, end, total_length);
1133 header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
1134 G_GINT64_FORMAT "/*", start, end);
1136 soup_message_headers_replace (hdrs, "Content-Range", header);
1141 parse_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
1142 char **foo, GHashTable **params)
1147 header = soup_message_headers_get_one (hdrs, header_name);
1152 *foo = g_strdup (header);
1153 semi = strchr (*foo, ';');
1158 while (p - 1 > *foo && g_ascii_isspace(p[-1]))
1162 semi = strchr (header, ';');
1171 *params = soup_header_parse_semi_param_list ("");
1175 *params = soup_header_parse_semi_param_list (semi);
1180 set_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
1181 const char *foo, GHashTable *params)
1184 GHashTableIter iter;
1185 gpointer key, value;
1187 str = g_string_new (foo);
1189 g_hash_table_iter_init (&iter, params);
1190 while (g_hash_table_iter_next (&iter, &key, &value)) {
1191 g_string_append (str, "; ");
1192 soup_header_g_string_append_param (str, key, value);
1196 soup_message_headers_replace (hdrs, header_name, str->str);
1197 g_string_free (str, TRUE);
1201 content_type_setter (SoupMessageHeaders *hdrs, const char *value)
1203 g_free (hdrs->content_type);
1205 char *content_type, *p;
1207 parse_content_foo (hdrs, "Content-Type", &content_type, NULL);
1208 p = strpbrk (content_type, " /");
1209 if (!p || *p != '/' || strpbrk (p + 1, " /")) {
1210 g_free (content_type);
1211 hdrs->content_type = NULL;
1213 hdrs->content_type = content_type;
1215 hdrs->content_type = NULL;
1219 * soup_message_headers_get_content_type:
1220 * @hdrs: a #SoupMessageHeaders
1221 * @params: (out) (allow-none) (transfer full): return location for
1222 * the Content-Type parameters (eg, "charset"), or %NULL
1224 * Looks up the "Content-Type" header in @hdrs, parses it, and returns
1225 * its value in *@content_type and *@params. @params can be %NULL if you
1226 * are only interested in the content type itself.
1228 * Return value: a string with the value of the "Content-Type" header
1229 * or NULL if @hdrs does not contain that header or it cannot be
1230 * parsed (in which case *@params will be unchanged).
1235 soup_message_headers_get_content_type (SoupMessageHeaders *hdrs,
1236 GHashTable **params)
1238 if (!hdrs->content_type)
1242 parse_content_foo (hdrs, "Content-Type", NULL, params);
1243 return hdrs->content_type;
1247 * soup_message_headers_set_content_type:
1248 * @hdrs: a #SoupMessageHeaders
1249 * @content_type: the MIME type
1250 * @params: (allow-none) (element-type utf8 utf8): additional
1251 * parameters, or %NULL
1253 * Sets the "Content-Type" header in @hdrs to @content_type,
1254 * optionally with additional parameters specified in @params.
1259 soup_message_headers_set_content_type (SoupMessageHeaders *hdrs,
1260 const char *content_type,
1263 set_content_foo (hdrs, "Content-Type", content_type, params);
1267 * soup_message_headers_get_content_disposition:
1268 * @hdrs: a #SoupMessageHeaders
1269 * @disposition: (out) (transfer full): return location for the
1270 * disposition-type, or %NULL
1271 * @params: (out) (transfer full) (element-type utf8 utf8): return
1272 * location for the Content-Disposition parameters, or %NULL
1274 * Looks up the "Content-Disposition" header in @hdrs, parses it, and
1275 * returns its value in *@disposition and *@params. @params can be
1276 * %NULL if you are only interested in the disposition-type.
1278 * In HTTP, the most common use of this header is to set a
1279 * disposition-type of "attachment", to suggest to the browser that a
1280 * response should be saved to disk rather than displayed in the
1281 * browser. If @params contains a "filename" parameter, this is a
1282 * suggestion of a filename to use. (If the parameter value in the
1283 * header contains an absolute or relative path, libsoup will truncate
1284 * it down to just the final path component, so you do not need to
1285 * test this yourself.)
1287 * Content-Disposition is also used in "multipart/form-data", however
1288 * this is handled automatically by #SoupMultipart and the associated
1291 * Return value: %TRUE if @hdrs contains a "Content-Disposition"
1292 * header, %FALSE if not (in which case *@disposition and *@params
1293 * will be unchanged).
1298 soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs,
1300 GHashTable **params)
1302 gpointer orig_key, orig_value;
1304 if (!parse_content_foo (hdrs, "Content-Disposition",
1305 disposition, params))
1308 /* If there is a filename parameter, make sure it contains
1309 * only a single path component
1311 if (params && g_hash_table_lookup_extended (*params, "filename",
1312 &orig_key, &orig_value)) {
1313 char *filename = strrchr (orig_value, '/');
1316 g_hash_table_insert (*params, orig_key, filename + 1);
1322 * soup_message_headers_set_content_disposition:
1323 * @hdrs: a #SoupMessageHeaders
1324 * @disposition: the disposition-type
1325 * @params: (allow-none) (element-type utf8 utf8): additional
1326 * parameters, or %NULL
1328 * Sets the "Content-Disposition" header in @hdrs to @disposition,
1329 * optionally with additional parameters specified in @params.
1331 * See soup_message_headers_get_content_disposition() for a discussion
1332 * of how Content-Disposition is used in HTTP.
1337 soup_message_headers_set_content_disposition (SoupMessageHeaders *hdrs,
1338 const char *disposition,
1341 set_content_foo (hdrs, "Content-Disposition", disposition, params);