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.
10 #include "soup-message-headers.h"
14 * SECTION:soup-message-headers
15 * @short_description: HTTP message headers
16 * @see_also: #SoupMessage
18 * #SoupMessageHeaders represents the HTTP message headers associated
19 * with a request or response.
25 * The HTTP message headers associated with a request or response.
29 * SoupMessageHeadersType:
30 * @SOUP_MESSAGE_HEADERS_REQUEST: request headers
31 * @SOUP_MESSAGE_HEADERS_RESPONSE: response headers
32 * @SOUP_MESSAGE_HEADERS_MULTIPART: multipart body part headers
34 * Value passed to soup_message_headers_new() to set certain default
38 typedef void (*SoupHeaderSetter) (SoupMessageHeaders *, const char *);
39 static const char *intern_header_name (const char *name, SoupHeaderSetter *setter);
40 static void clear_special_headers (SoupMessageHeaders *hdrs);
47 struct SoupMessageHeaders {
50 SoupMessageHeadersType type;
52 SoupEncoding encoding;
53 goffset content_length;
54 SoupExpectation expectations;
61 * soup_message_headers_new:
62 * @type: the type of headers
64 * Creates a #SoupMessageHeaders. (#SoupMessage does this
65 * automatically for its own headers. You would only need to use this
66 * method if you are manually parsing or generating message headers.)
68 * Return value: a new #SoupMessageHeaders
71 soup_message_headers_new (SoupMessageHeadersType type)
73 SoupMessageHeaders *hdrs;
75 hdrs = g_slice_new0 (SoupMessageHeaders);
76 /* FIXME: is "5" a good default? */
77 hdrs->array = g_array_sized_new (TRUE, FALSE, sizeof (SoupHeader), 5);
85 static SoupMessageHeaders *
86 soup_message_headers_copy (SoupMessageHeaders *hdrs)
93 * soup_message_headers_free:
94 * @hdrs: a #SoupMessageHeaders
99 soup_message_headers_free (SoupMessageHeaders *hdrs)
101 if (--hdrs->ref_count == 0) {
102 soup_message_headers_clear (hdrs);
103 g_array_free (hdrs->array, TRUE);
104 g_clear_pointer (&hdrs->concat, g_hash_table_destroy);
105 g_slice_free (SoupMessageHeaders, hdrs);
109 G_DEFINE_BOXED_TYPE (SoupMessageHeaders, soup_message_headers, soup_message_headers_copy, soup_message_headers_free)
112 * soup_message_headers_clear:
113 * @hdrs: a #SoupMessageHeaders
118 soup_message_headers_clear (SoupMessageHeaders *hdrs)
120 SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
123 for (i = 0; i < hdrs->array->len; i++)
124 g_free (hdr_array[i].value);
125 g_array_set_size (hdrs->array, 0);
128 g_hash_table_remove_all (hdrs->concat);
130 clear_special_headers (hdrs);
134 * soup_message_headers_clean_connection_headers:
135 * @hdrs: a #SoupMessageHeaders
137 * Removes all the headers listed in the Connection header.
142 soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs)
145 const char *connection;
148 connection = soup_message_headers_get_list (hdrs, "Connection");
152 tokens = soup_header_parse_list (connection);
153 for (t = tokens; t; t = t->next)
154 soup_message_headers_remove (hdrs, t->data);
155 soup_header_free_list (tokens);
159 * soup_message_headers_append:
160 * @hdrs: a #SoupMessageHeaders
161 * @name: the header name to add
162 * @value: the new value of @name
164 * Appends a new header with name @name and value @value to @hdrs. (If
165 * there is an existing header with name @name, then this creates a
166 * second one, which is only allowed for list-valued headers; see also
167 * soup_message_headers_replace().)
169 * The caller is expected to make sure that @name and @value are
170 * syntactically correct.
173 soup_message_headers_append (SoupMessageHeaders *hdrs,
174 const char *name, const char *value)
177 SoupHeaderSetter setter;
179 g_return_if_fail (name != NULL);
180 g_return_if_fail (value != NULL);
182 /* Setting a syntactically invalid header name or value is
183 * considered to be a programming error. However, it can also
184 * be a security hole, so we want to fail here even if
185 * compiled with G_DISABLE_CHECKS.
187 #ifndef G_DISABLE_CHECKS
188 g_return_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL);
189 g_return_if_fail (strpbrk (value, "\r\n") == NULL);
191 if (*name && strpbrk (name, " \t\r\n:")) {
192 g_warning ("soup_message_headers_append: Ignoring bad name '%s'", name);
195 if (strpbrk (value, "\r\n")) {
196 g_warning ("soup_message_headers_append: Ignoring bad value '%s'", value);
201 header.name = intern_header_name (name, &setter);
202 header.value = g_strdup (value);
203 g_array_append_val (hdrs->array, header);
205 g_hash_table_remove (hdrs->concat, header.name);
207 setter (hdrs, header.value);
211 * soup_message_headers_replace:
212 * @hdrs: a #SoupMessageHeaders
213 * @name: the header name to replace
214 * @value: the new value of @name
216 * Replaces the value of the header @name in @hdrs with @value. (See
217 * also soup_message_headers_append().)
219 * The caller is expected to make sure that @name and @value are
220 * syntactically correct.
223 soup_message_headers_replace (SoupMessageHeaders *hdrs,
224 const char *name, const char *value)
226 soup_message_headers_remove (hdrs, name);
227 soup_message_headers_append (hdrs, name, value);
231 find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
235 for (i = 0; hdr_array[i].name; i++) {
236 if (hdr_array[i].name == interned_name) {
245 find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name, int nth)
249 for (i = length; i >= 0; i--) {
250 if (hdr_array[i].name == interned_name) {
259 * soup_message_headers_remove:
260 * @hdrs: a #SoupMessageHeaders
261 * @name: the header name to remove
263 * Removes @name from @hdrs. If there are multiple values for @name,
264 * they are all removed.
267 soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
269 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
270 SoupHeaderSetter setter;
273 g_return_if_fail (name != NULL);
275 name = intern_header_name (name, &setter);
276 while ((index = find_header (hdr_array, name, 0)) != -1) {
277 g_free (hdr_array[index].value);
278 g_array_remove_index (hdrs->array, index);
281 g_hash_table_remove (hdrs->concat, name);
287 * soup_message_headers_get_one:
288 * @hdrs: a #SoupMessageHeaders
291 * Gets the value of header @name in @hdrs. Use this for headers whose
292 * values are <emphasis>not</emphasis> comma-delimited lists, and
293 * which therefore can only appear at most once in the headers. For
294 * list-valued headers, use soup_message_headers_get_list().
296 * If @hdrs does erroneously contain multiple copies of the header, it
297 * is not defined which one will be returned. (Ideally, it will return
298 * whichever one makes libsoup most compatible with other HTTP
301 * Return value: the header's value or %NULL if not found.
306 soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
308 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
309 guint hdr_length = hdrs->array->len;
312 g_return_val_if_fail (name != NULL, NULL);
314 name = intern_header_name (name, NULL);
316 index = find_last_header (hdr_array, hdr_length, name, 0);
318 return (index == -1) ? NULL : hdr_array[index].value;
322 * soup_message_headers_get_list:
323 * @hdrs: a #SoupMessageHeaders
326 * Gets the value of header @name in @hdrs. Use this for headers whose
327 * values are comma-delimited lists, and which are therefore allowed
328 * to appear multiple times in the headers. For non-list-valued
329 * headers, use soup_message_headers_get_one().
331 * If @name appears multiple times in @hdrs,
332 * soup_message_headers_get_list() will concatenate all of the values
333 * together, separated by commas. This is sometimes awkward to parse
334 * (eg, WWW-Authenticate, Set-Cookie), but you have to be able to deal
335 * with it anyway, because the HTTP spec explicitly states that this
336 * transformation is allowed, and so an upstream proxy could do the
339 * Return value: the header's value or %NULL if not found.
344 soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name)
346 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
351 g_return_val_if_fail (name != NULL, NULL);
353 name = intern_header_name (name, NULL);
355 value = g_hash_table_lookup (hdrs->concat, name);
360 index = find_header (hdr_array, name, 0);
363 else if (find_header (hdr_array, name, 1) == -1)
364 return hdr_array[index].value;
366 concat = g_string_new (NULL);
367 for (i = 0; (index = find_header (hdr_array, name, i)) != -1; i++) {
369 g_string_append (concat, ", ");
370 g_string_append (concat, hdr_array[index].value);
372 value = g_string_free (concat, FALSE);
375 hdrs->concat = g_hash_table_new_full (NULL, NULL, NULL, g_free);
376 g_hash_table_insert (hdrs->concat, (gpointer)name, value);
381 * soup_message_headers_get:
382 * @hdrs: a #SoupMessageHeaders
385 * Gets the value of header @name in @hdrs.
387 * This method was supposed to work correctly for both single-valued
388 * and list-valued headers, but because some HTTP clients/servers
389 * mistakenly send multiple copies of headers that are supposed to be
390 * single-valued, it sometimes returns incorrect results. To fix this,
391 * the methods soup_message_headers_get_one() and
392 * soup_message_headers_get_list() were introduced, so callers can
393 * explicitly state which behavior they are expecting.
395 * Return value: as with soup_message_headers_get_list().
397 * Deprecated: Use soup_message_headers_get_one() or
398 * soup_message_headers_get_list() instead.
401 soup_message_headers_get (SoupMessageHeaders *hdrs, const char *name)
403 return soup_message_headers_get_list (hdrs, name);
407 * SoupMessageHeadersIter:
409 * An opaque type used to iterate over a %SoupMessageHeaders
412 * After intializing the iterator with
413 * soup_message_headers_iter_init(), call
414 * soup_message_headers_iter_next() to fetch data from it.
416 * You may not modify the headers while iterating over them.
420 SoupMessageHeaders *hdrs;
422 } SoupMessageHeadersIterReal;
425 * soup_message_headers_iter_init:
426 * @iter: (out) (transfer none): a pointer to a %SoupMessageHeadersIter
428 * @hdrs: a %SoupMessageHeaders
430 * Initializes @iter for iterating @hdrs.
433 soup_message_headers_iter_init (SoupMessageHeadersIter *iter,
434 SoupMessageHeaders *hdrs)
436 SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
443 * soup_message_headers_iter_next:
444 * @iter: (inout) (transfer none): a %SoupMessageHeadersIter
445 * @name: (out) (transfer none): pointer to a variable to return
447 * @value: (out) (transfer none): pointer to a variable to return
448 * the header value in
450 * Yields the next name/value pair in the %SoupMessageHeaders being
451 * iterated by @iter. If @iter has already yielded the last header,
452 * then soup_message_headers_iter_next() will return %FALSE and @name
453 * and @value will be unchanged.
455 * Return value: %TRUE if another name and value were returned, %FALSE
456 * if the end of the headers has been reached.
459 soup_message_headers_iter_next (SoupMessageHeadersIter *iter,
460 const char **name, const char **value)
462 SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
463 SoupHeader *hdr_array = (SoupHeader *)real->hdrs->array->data;
465 if (real->index >= real->hdrs->array->len)
468 *name = hdr_array[real->index].name;
469 *value = hdr_array[real->index].value;
475 * SoupMessageHeadersForeachFunc:
476 * @name: the header name
477 * @value: the header value
478 * @user_data: the data passed to soup_message_headers_foreach()
480 * The callback passed to soup_message_headers_foreach().
484 * soup_message_headers_foreach:
485 * @hdrs: a #SoupMessageHeaders
486 * @func: (scope call): callback function to run for each header
487 * @user_data: data to pass to @func
489 * Calls @func once for each header value in @hdrs.
491 * Beware that unlike soup_message_headers_get(), this processes the
492 * headers in exactly the way they were added, rather than
493 * concatenating multiple same-named headers into a single value.
494 * (This is intentional; it ensures that if you call
495 * soup_message_headers_append() multiple times with the same name,
496 * then the I/O code will output multiple copies of the header when
497 * sending the message to the remote implementation, which may be
498 * required for interoperability in some cases.)
500 * You may not modify the headers from @func.
503 soup_message_headers_foreach (SoupMessageHeaders *hdrs,
504 SoupMessageHeadersForeachFunc func,
507 SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
510 for (i = 0; i < hdrs->array->len; i++)
511 func (hdr_array[i].name, hdr_array[i].value, user_data);
515 G_LOCK_DEFINE_STATIC (header_pool);
516 static GHashTable *header_pool, *header_setters;
518 static void transfer_encoding_setter (SoupMessageHeaders *, const char *);
519 static void content_length_setter (SoupMessageHeaders *, const char *);
520 static void expectation_setter (SoupMessageHeaders *, const char *);
521 static void content_type_setter (SoupMessageHeaders *, const char *);
524 intern_header_locked (const char *name)
528 interned = g_hash_table_lookup (header_pool, name);
530 char *dup = g_strdup (name);
531 g_hash_table_insert (header_pool, dup, dup);
538 intern_header_name (const char *name, SoupHeaderSetter *setter)
540 const char *interned;
542 G_LOCK (header_pool);
545 header_pool = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
546 header_setters = g_hash_table_new (NULL, NULL);
547 g_hash_table_insert (header_setters,
548 intern_header_locked ("Transfer-Encoding"),
549 transfer_encoding_setter);
550 g_hash_table_insert (header_setters,
551 intern_header_locked ("Content-Length"),
552 content_length_setter);
553 g_hash_table_insert (header_setters,
554 intern_header_locked ("Expect"),
556 g_hash_table_insert (header_setters,
557 intern_header_locked ("Content-Type"),
558 content_type_setter);
561 interned = intern_header_locked (name);
563 *setter = g_hash_table_lookup (header_setters, interned);
565 G_UNLOCK (header_pool);
570 clear_special_headers (SoupMessageHeaders *hdrs)
572 SoupHeaderSetter setter;
576 /* Make sure header_setters has been initialized */
577 intern_header_name ("", NULL);
579 g_hash_table_iter_init (&iter, header_setters);
580 while (g_hash_table_iter_next (&iter, &key, &value)) {
586 /* Specific headers */
589 transfer_encoding_setter (SoupMessageHeaders *hdrs, const char *value)
592 if (g_ascii_strcasecmp (value, "chunked") == 0)
593 hdrs->encoding = SOUP_ENCODING_CHUNKED;
595 hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
601 content_length_setter (SoupMessageHeaders *hdrs, const char *value)
603 /* Transfer-Encoding trumps Content-Length */
604 if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
610 hdrs->content_length = g_ascii_strtoull (value, &end, 10);
612 hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
614 hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
621 * @SOUP_ENCODING_UNRECOGNIZED: unknown / error
622 * @SOUP_ENCODING_NONE: no body is present (which is not the same as a
623 * 0-length body, and only occurs in certain places)
624 * @SOUP_ENCODING_CONTENT_LENGTH: Content-Length encoding
625 * @SOUP_ENCODING_EOF: Response body ends when the connection is closed
626 * @SOUP_ENCODING_CHUNKED: chunked encoding (currently only supported
628 * @SOUP_ENCODING_BYTERANGES: multipart/byteranges (Reserved for future
629 * use: NOT CURRENTLY IMPLEMENTED)
631 * How a message body is encoded for transport
635 * soup_message_headers_get_encoding:
636 * @hdrs: a #SoupMessageHeaders
638 * Gets the message body encoding that @hdrs declare. This may not
639 * always correspond to the encoding used on the wire; eg, a HEAD
640 * response may declare a Content-Length or Transfer-Encoding, but
641 * it will never actually include a body.
643 * Return value: the encoding declared by @hdrs.
646 soup_message_headers_get_encoding (SoupMessageHeaders *hdrs)
650 if (hdrs->encoding != -1)
651 return hdrs->encoding;
653 /* If Transfer-Encoding was set, hdrs->encoding would already
654 * be set. So we don't need to check that possibility.
656 header = soup_message_headers_get_one (hdrs, "Content-Length");
658 content_length_setter (hdrs, header);
659 if (hdrs->encoding != -1)
660 return hdrs->encoding;
663 /* Per RFC 2616 4.4, a response body that doesn't indicate its
664 * encoding otherwise is terminated by connection close, and a
665 * request that doesn't indicate otherwise has no body. Note
666 * that SoupMessage calls soup_message_headers_set_encoding()
667 * to override the response body default for our own
668 * server-side messages.
670 hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
671 SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
672 return hdrs->encoding;
676 * soup_message_headers_set_encoding:
677 * @hdrs: a #SoupMessageHeaders
678 * @encoding: a #SoupEncoding
680 * Sets the message body encoding that @hdrs will declare. In particular,
681 * you should use this if you are going to send a request or response in
685 soup_message_headers_set_encoding (SoupMessageHeaders *hdrs,
686 SoupEncoding encoding)
688 if (encoding == hdrs->encoding)
692 case SOUP_ENCODING_NONE:
693 case SOUP_ENCODING_EOF:
694 soup_message_headers_remove (hdrs, "Transfer-Encoding");
695 soup_message_headers_remove (hdrs, "Content-Length");
698 case SOUP_ENCODING_CONTENT_LENGTH:
699 soup_message_headers_remove (hdrs, "Transfer-Encoding");
702 case SOUP_ENCODING_CHUNKED:
703 soup_message_headers_remove (hdrs, "Content-Length");
704 soup_message_headers_replace (hdrs, "Transfer-Encoding", "chunked");
708 g_return_if_reached ();
711 hdrs->encoding = encoding;
715 * soup_message_headers_get_content_length:
716 * @hdrs: a #SoupMessageHeaders
718 * Gets the message body length that @hdrs declare. This will only
719 * be non-0 if soup_message_headers_get_encoding() returns
720 * %SOUP_ENCODING_CONTENT_LENGTH.
722 * Return value: the message body length declared by @hdrs.
725 soup_message_headers_get_content_length (SoupMessageHeaders *hdrs)
727 SoupEncoding encoding;
729 encoding = soup_message_headers_get_encoding (hdrs);
730 if (encoding == SOUP_ENCODING_CONTENT_LENGTH)
731 return hdrs->content_length;
737 * soup_message_headers_set_content_length:
738 * @hdrs: a #SoupMessageHeaders
739 * @content_length: the message body length
741 * Sets the message body length that @hdrs will declare, and sets
742 * @hdrs's encoding to %SOUP_ENCODING_CONTENT_LENGTH.
744 * You do not normally need to call this; if @hdrs is set to use
745 * Content-Length encoding, libsoup will automatically set its
746 * Content-Length header for you immediately before sending the
747 * headers. One situation in which this method is useful is when
748 * generating the response to a HEAD request; Calling
749 * soup_message_headers_set_content_length() allows you to put the
750 * correct content length into the response without needing to waste
751 * memory by filling in a response body which won't actually be sent.
754 soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
755 goffset content_length)
759 g_snprintf (length, sizeof (length), "%" G_GUINT64_FORMAT,
761 soup_message_headers_remove (hdrs, "Transfer-Encoding");
762 soup_message_headers_replace (hdrs, "Content-Length", length);
766 expectation_setter (SoupMessageHeaders *hdrs, const char *value)
769 if (!g_ascii_strcasecmp (value, "100-continue"))
770 hdrs->expectations = SOUP_EXPECTATION_CONTINUE;
772 hdrs->expectations = SOUP_EXPECTATION_UNRECOGNIZED;
774 hdrs->expectations = 0;
779 * @SOUP_EXPECTATION_CONTINUE: "100-continue"
780 * @SOUP_EXPECTATION_UNRECOGNIZED: any unrecognized expectation
782 * Represents the parsed value of the "Expect" header.
786 * soup_message_headers_get_expectations:
787 * @hdrs: a #SoupMessageHeaders
789 * Gets the expectations declared by @hdrs's "Expect" header.
790 * Currently this will either be %SOUP_EXPECTATION_CONTINUE or
791 * %SOUP_EXPECTATION_UNRECOGNIZED.
793 * Return value: the contents of @hdrs's "Expect" header
796 soup_message_headers_get_expectations (SoupMessageHeaders *hdrs)
798 return hdrs->expectations;
802 * soup_message_headers_set_expectations:
803 * @hdrs: a #SoupMessageHeaders
804 * @expectations: the expectations to set
806 * Sets @hdrs's "Expect" header according to @expectations.
808 * Currently %SOUP_EXPECTATION_CONTINUE is the only known expectation
809 * value. You should set this value on a request if you are sending a
810 * large message body (eg, via POST or PUT), and want to give the
811 * server a chance to reject the request after seeing just the headers
812 * (eg, because it will require authentication before allowing you to
813 * post, or because you're POSTing to a URL that doesn't exist). This
814 * saves you from having to transmit the large request body when the
815 * server is just going to ignore it anyway.
818 soup_message_headers_set_expectations (SoupMessageHeaders *hdrs,
819 SoupExpectation expectations)
821 g_return_if_fail ((expectations & ~SOUP_EXPECTATION_CONTINUE) == 0);
823 if (expectations & SOUP_EXPECTATION_CONTINUE)
824 soup_message_headers_replace (hdrs, "Expect", "100-continue");
826 soup_message_headers_remove (hdrs, "Expect");
831 * @start: the start of the range
832 * @end: the end of the range
834 * Represents a byte range as used in the Range header.
836 * If @end is non-negative, then @start and @end represent the bounds
837 * of of the range, counting from 0. (Eg, the first 500 bytes would be
838 * represented as @start = 0 and @end = 499.)
840 * If @end is -1 and @start is non-negative, then this represents a
841 * range starting at @start and ending with the last byte of the
842 * requested resource body. (Eg, all but the first 500 bytes would be
843 * @start = 500, and @end = -1.)
845 * If @end is -1 and @start is negative, then it represents a "suffix
846 * range", referring to the last -@start bytes of the resource body.
847 * (Eg, the last 500 bytes would be @start = -500 and @end = -1.)
853 sort_ranges (gconstpointer a, gconstpointer b)
855 SoupRange *ra = (SoupRange *)a;
856 SoupRange *rb = (SoupRange *)b;
858 return ra->start - rb->start;
862 * soup_message_headers_get_ranges:
863 * @hdrs: a #SoupMessageHeaders
864 * @total_length: the total_length of the response body
865 * @ranges: (out): return location for an array of #SoupRange
866 * @length: the length of the returned array
868 * Parses @hdrs's Range header and returns an array of the requested
869 * byte ranges. The returned array must be freed with
870 * soup_message_headers_free_ranges().
872 * If @total_length is non-0, its value will be used to adjust the
873 * returned ranges to have explicit start and end values, and the
874 * returned ranges will be sorted and non-overlapping. If
875 * @total_length is 0, then some ranges may have an end value of -1,
876 * as described under #SoupRange, and some of the ranges may be
879 * Return value: %TRUE if @hdrs contained a "Range" header containing
880 * byte ranges which could be parsed, %FALSE otherwise (in which case
881 * @range and @length will not be set).
886 soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
887 goffset total_length,
891 const char *range = soup_message_headers_get_one (hdrs, "Range");
892 GSList *range_list, *r;
897 if (!range || strncmp (range, "bytes", 5) != 0)
901 while (g_ascii_isspace (*range))
905 while (g_ascii_isspace (*range))
908 range_list = soup_header_parse_list (range);
912 array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
913 for (r = range_list; r; r = r->next) {
918 cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
919 cur.end = total_length - 1;
921 cur.start = g_ascii_strtoull (spec, &end, 10);
925 cur.end = g_ascii_strtoull (end, &end, 10);
927 cur.end = total_length - 1;
930 g_array_free (array, TRUE);
931 soup_header_free_list (range_list);
935 g_array_append_val (array, cur);
938 soup_header_free_list (range_list);
941 g_array_sort (array, sort_ranges);
942 for (i = 1; i < array->len; i++) {
943 SoupRange *cur = &((SoupRange *)array->data)[i];
944 SoupRange *prev = &((SoupRange *)array->data)[i - 1];
946 if (cur->start <= prev->end) {
947 prev->end = MAX (prev->end, cur->end);
948 g_array_remove_index (array, i);
953 *ranges = (SoupRange *)array->data;
954 *length = array->len;
956 g_array_free (array, FALSE);
961 * soup_message_headers_free_ranges:
962 * @hdrs: a #SoupMessageHeaders
963 * @ranges: an array of #SoupRange
965 * Frees the array of ranges returned from soup_message_headers_get_ranges().
970 soup_message_headers_free_ranges (SoupMessageHeaders *hdrs,
977 * soup_message_headers_set_ranges:
978 * @hdrs: a #SoupMessageHeaders
979 * @ranges: an array of #SoupRange
980 * @length: the length of @range
982 * Sets @hdrs's Range header to request the indicated ranges. (If you
983 * only want to request a single range, you can use
984 * soup_message_headers_set_range().)
989 soup_message_headers_set_ranges (SoupMessageHeaders *hdrs,
996 header = g_string_new ("bytes=");
997 for (i = 0; i < length; i++) {
999 g_string_append_c (header, ',');
1000 if (ranges[i].end >= 0) {
1001 g_string_append_printf (header, "%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT,
1002 ranges[i].start, ranges[i].end);
1003 } else if (ranges[i].start >= 0) {
1004 g_string_append_printf (header,"%" G_GINT64_FORMAT "-",
1007 g_string_append_printf (header, "%" G_GINT64_FORMAT,
1012 soup_message_headers_replace (hdrs, "Range", header->str);
1013 g_string_free (header, TRUE);
1017 * soup_message_headers_set_range:
1018 * @hdrs: a #SoupMessageHeaders
1019 * @start: the start of the range to request
1020 * @end: the end of the range to request
1022 * Sets @hdrs's Range header to request the indicated range.
1023 * @start and @end are interpreted as in a #SoupRange.
1025 * If you need to request multiple ranges, use
1026 * soup_message_headers_set_ranges().
1031 soup_message_headers_set_range (SoupMessageHeaders *hdrs,
1037 range.start = start;
1039 soup_message_headers_set_ranges (hdrs, &range, 1);
1043 * soup_message_headers_get_content_range:
1044 * @hdrs: a #SoupMessageHeaders
1045 * @start: return value for the start of the range
1046 * @end: return value for the end of the range
1047 * @total_length: return value for the total length of the resource,
1048 * or %NULL if you don't care.
1050 * Parses @hdrs's Content-Range header and returns it in @start,
1051 * @end, and @total_length. If the total length field in the header
1052 * was specified as "*", then @total_length will be set to -1.
1054 * Return value: %TRUE if @hdrs contained a "Content-Range" header
1055 * containing a byte range which could be parsed, %FALSE otherwise.
1060 soup_message_headers_get_content_range (SoupMessageHeaders *hdrs,
1063 goffset *total_length)
1065 const char *header = soup_message_headers_get_one (hdrs, "Content-Range");
1069 if (!header || strncmp (header, "bytes ", 6) != 0)
1073 while (g_ascii_isspace (*header))
1075 if (!g_ascii_isdigit (*header))
1078 *start = g_ascii_strtoull (header, &p, 10);
1081 *end = g_ascii_strtoull (p + 1, &p, 10);
1089 length = g_ascii_strtoull (p, &p, 10);
1092 *total_length = length;
1097 * soup_message_headers_set_content_range:
1098 * @hdrs: a #SoupMessageHeaders
1099 * @start: the start of the range
1100 * @end: the end of the range
1101 * @total_length: the total length of the resource, or -1 if unknown
1103 * Sets @hdrs's Content-Range header according to the given values.
1104 * (Note that @total_length is the total length of the entire resource
1105 * that this is a range of, not simply @end - @start + 1.)
1110 soup_message_headers_set_content_range (SoupMessageHeaders *hdrs,
1113 goffset total_length)
1117 if (total_length >= 0) {
1118 header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
1119 G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
1120 start, end, total_length);
1122 header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
1123 G_GINT64_FORMAT "/*", start, end);
1125 soup_message_headers_replace (hdrs, "Content-Range", header);
1130 parse_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
1131 char **foo, GHashTable **params)
1136 header = soup_message_headers_get_one (hdrs, header_name);
1141 *foo = g_strdup (header);
1142 semi = strchr (*foo, ';');
1147 while (p - 1 > *foo && g_ascii_isspace(p[-1]))
1151 semi = strchr (header, ';');
1160 *params = soup_header_parse_semi_param_list ("");
1164 *params = soup_header_parse_semi_param_list (semi);
1169 set_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
1170 const char *foo, GHashTable *params)
1173 GHashTableIter iter;
1174 gpointer key, value;
1176 str = g_string_new (foo);
1178 g_hash_table_iter_init (&iter, params);
1179 while (g_hash_table_iter_next (&iter, &key, &value)) {
1180 g_string_append (str, "; ");
1181 soup_header_g_string_append_param (str, key, value);
1185 soup_message_headers_replace (hdrs, header_name, str->str);
1186 g_string_free (str, TRUE);
1190 content_type_setter (SoupMessageHeaders *hdrs, const char *value)
1192 g_free (hdrs->content_type);
1194 char *content_type, *p;
1196 parse_content_foo (hdrs, "Content-Type", &content_type, NULL);
1197 p = strpbrk (content_type, " /");
1198 if (!p || *p != '/' || strpbrk (p + 1, " /")) {
1199 g_free (content_type);
1200 hdrs->content_type = NULL;
1202 hdrs->content_type = content_type;
1204 hdrs->content_type = NULL;
1208 * soup_message_headers_get_content_type:
1209 * @hdrs: a #SoupMessageHeaders
1210 * @params: (out) (element-type utf8 utf8) (allow-none) (transfer full):
1211 * return location for the Content-Type parameters (eg, "charset"), or
1214 * Looks up the "Content-Type" header in @hdrs, parses it, and returns
1215 * its value in *@content_type and *@params. @params can be %NULL if you
1216 * are only interested in the content type itself.
1218 * Return value: a string with the value of the "Content-Type" header
1219 * or NULL if @hdrs does not contain that header or it cannot be
1220 * parsed (in which case *@params will be unchanged).
1225 soup_message_headers_get_content_type (SoupMessageHeaders *hdrs,
1226 GHashTable **params)
1228 if (!hdrs->content_type)
1232 parse_content_foo (hdrs, "Content-Type", NULL, params);
1233 return hdrs->content_type;
1237 * soup_message_headers_set_content_type:
1238 * @hdrs: a #SoupMessageHeaders
1239 * @content_type: the MIME type
1240 * @params: (allow-none) (element-type utf8 utf8): additional
1241 * parameters, or %NULL
1243 * Sets the "Content-Type" header in @hdrs to @content_type,
1244 * optionally with additional parameters specified in @params.
1249 soup_message_headers_set_content_type (SoupMessageHeaders *hdrs,
1250 const char *content_type,
1253 set_content_foo (hdrs, "Content-Type", content_type, params);
1257 * soup_message_headers_get_content_disposition:
1258 * @hdrs: a #SoupMessageHeaders
1259 * @disposition: (out) (transfer full): return location for the
1260 * disposition-type, or %NULL
1261 * @params: (out) (transfer full) (element-type utf8 utf8): return
1262 * location for the Content-Disposition parameters, or %NULL
1264 * Looks up the "Content-Disposition" header in @hdrs, parses it, and
1265 * returns its value in *@disposition and *@params. @params can be
1266 * %NULL if you are only interested in the disposition-type.
1268 * In HTTP, the most common use of this header is to set a
1269 * disposition-type of "attachment", to suggest to the browser that a
1270 * response should be saved to disk rather than displayed in the
1271 * browser. If @params contains a "filename" parameter, this is a
1272 * suggestion of a filename to use. (If the parameter value in the
1273 * header contains an absolute or relative path, libsoup will truncate
1274 * it down to just the final path component, so you do not need to
1275 * test this yourself.)
1277 * Content-Disposition is also used in "multipart/form-data", however
1278 * this is handled automatically by #SoupMultipart and the associated
1281 * Return value: %TRUE if @hdrs contains a "Content-Disposition"
1282 * header, %FALSE if not (in which case *@disposition and *@params
1283 * will be unchanged).
1288 soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs,
1290 GHashTable **params)
1292 gpointer orig_key, orig_value;
1294 if (!parse_content_foo (hdrs, "Content-Disposition",
1295 disposition, params))
1298 /* If there is a filename parameter, make sure it contains
1299 * only a single path component
1301 if (params && g_hash_table_lookup_extended (*params, "filename",
1302 &orig_key, &orig_value)) {
1303 char *filename = strrchr (orig_value, '/');
1306 g_hash_table_insert (*params, g_strdup (orig_key), filename + 1);
1312 * soup_message_headers_set_content_disposition:
1313 * @hdrs: a #SoupMessageHeaders
1314 * @disposition: the disposition-type
1315 * @params: (allow-none) (element-type utf8 utf8): additional
1316 * parameters, or %NULL
1318 * Sets the "Content-Disposition" header in @hdrs to @disposition,
1319 * optionally with additional parameters specified in @params.
1321 * See soup_message_headers_get_content_disposition() for a discussion
1322 * of how Content-Disposition is used in HTTP.
1327 soup_message_headers_set_content_disposition (SoupMessageHeaders *hdrs,
1328 const char *disposition,
1331 set_content_foo (hdrs, "Content-Disposition", disposition, params);