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.
25 * SoupMessageHeadersType:
26 * @SOUP_MESSAGE_HEADERS_REQUEST: request headers
27 * @SOUP_MESSAGE_HEADERS_RESPONSE: response headers
28 * @SOUP_MESSAGE_HEADERS_MULTIPART: multipart body part headers
30 * Value passed to soup_message_headers_new() to set certain default
34 typedef void (*SoupHeaderSetter) (SoupMessageHeaders *, const char *);
35 static const char *intern_header_name (const char *name, SoupHeaderSetter *setter);
36 static void clear_special_headers (SoupMessageHeaders *hdrs);
43 struct SoupMessageHeaders {
46 SoupMessageHeadersType type;
48 SoupEncoding encoding;
49 goffset content_length;
50 SoupExpectation expectations;
57 * soup_message_headers_new:
58 * @type: the type of headers
60 * Creates a #SoupMessageHeaders. (#SoupMessage does this
61 * automatically for its own headers. You would only need to use this
62 * method if you are manually parsing or generating message headers.)
64 * Return value: a new #SoupMessageHeaders
67 soup_message_headers_new (SoupMessageHeadersType type)
69 SoupMessageHeaders *hdrs;
71 hdrs = g_slice_new0 (SoupMessageHeaders);
72 /* FIXME: is "5" a good default? */
73 hdrs->array = g_array_sized_new (TRUE, FALSE, sizeof (SoupHeader), 5);
81 static SoupMessageHeaders *
82 soup_message_headers_copy (SoupMessageHeaders *hdrs)
89 * soup_message_headers_free:
90 * @hdrs: a #SoupMessageHeaders
95 soup_message_headers_free (SoupMessageHeaders *hdrs)
97 if (--hdrs->ref_count == 0) {
98 soup_message_headers_clear (hdrs);
99 g_array_free (hdrs->array, TRUE);
101 g_hash_table_destroy (hdrs->concat);
102 g_slice_free (SoupMessageHeaders, hdrs);
107 soup_message_headers_get_type (void)
109 static volatile gsize type_volatile = 0;
111 if (g_once_init_enter (&type_volatile)) {
112 GType type = g_boxed_type_register_static (
113 g_intern_static_string ("SoupMessageHeaders"),
114 (GBoxedCopyFunc) soup_message_headers_copy,
115 (GBoxedFreeFunc) soup_message_headers_free);
116 g_once_init_leave (&type_volatile, type);
118 return type_volatile;
122 * soup_message_headers_clear:
123 * @hdrs: a #SoupMessageHeaders
128 soup_message_headers_clear (SoupMessageHeaders *hdrs)
130 SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
133 for (i = 0; i < hdrs->array->len; i++)
134 g_free (hdr_array[i].value);
135 g_array_set_size (hdrs->array, 0);
138 g_hash_table_remove_all (hdrs->concat);
140 clear_special_headers (hdrs);
144 * soup_message_headers_clean_connection_headers:
145 * @hdrs: a #SoupMessageHeaders
147 * Removes all the headers listed in the Connection header.
152 soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs)
155 const char *connection;
158 connection = soup_message_headers_get_list (hdrs, "Connection");
162 tokens = soup_header_parse_list (connection);
163 for (t = tokens; t; t = t->next)
164 soup_message_headers_remove (hdrs, t->data);
165 soup_header_free_list (tokens);
169 * soup_message_headers_append:
170 * @hdrs: a #SoupMessageHeaders
171 * @name: the header name to add
172 * @value: the new value of @name
174 * Appends a new header with name @name and value @value to @hdrs. (If
175 * there is an existing header with name @name, then this creates a
176 * second one, which is only allowed for list-valued headers; see also
177 * soup_message_headers_replace().)
179 * The caller is expected to make sure that @name and @value are
180 * syntactically correct.
183 soup_message_headers_append (SoupMessageHeaders *hdrs,
184 const char *name, const char *value)
187 SoupHeaderSetter setter;
189 g_return_if_fail (name != NULL);
190 g_return_if_fail (value != NULL);
192 /* Setting a syntactically invalid header name or value is
193 * considered to be a programming error. However, it can also
194 * be a security hole, so we want to fail here even if
195 * compiled with G_DISABLE_CHECKS.
197 #ifndef G_DISABLE_CHECKS
198 g_return_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL);
199 g_return_if_fail (strpbrk (value, "\r\n") == NULL);
201 if (*name && strpbrk (name, " \t\r\n:")) {
202 g_warning ("soup_message_headers_append: Ignoring bad name '%s'", name);
205 if (strpbrk (value, "\r\n")) {
206 g_warning ("soup_message_headers_append: Ignoring bad value '%s'", value);
211 header.name = intern_header_name (name, &setter);
212 header.value = g_strdup (value);
213 g_array_append_val (hdrs->array, header);
215 g_hash_table_remove (hdrs->concat, header.name);
217 setter (hdrs, header.value);
221 * soup_message_headers_replace:
222 * @hdrs: a #SoupMessageHeaders
223 * @name: the header name to replace
224 * @value: the new value of @name
226 * Replaces the value of the header @name in @hdrs with @value. (See
227 * also soup_message_headers_append().)
229 * The caller is expected to make sure that @name and @value are
230 * syntactically correct.
233 soup_message_headers_replace (SoupMessageHeaders *hdrs,
234 const char *name, const char *value)
236 soup_message_headers_remove (hdrs, name);
237 soup_message_headers_append (hdrs, name, value);
241 find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
245 for (i = 0; hdr_array[i].name; i++) {
246 if (hdr_array[i].name == interned_name) {
255 find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name, int nth)
259 for (i = length; i >= 0; i--) {
260 if (hdr_array[i].name == interned_name) {
269 * soup_message_headers_remove:
270 * @hdrs: a #SoupMessageHeaders
271 * @name: the header name to remove
273 * Removes @name from @hdrs. If there are multiple values for @name,
274 * they are all removed.
277 soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
279 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
280 SoupHeaderSetter setter;
283 g_return_if_fail (name != NULL);
285 name = intern_header_name (name, &setter);
286 while ((index = find_header (hdr_array, name, 0)) != -1) {
287 g_free (hdr_array[index].value);
288 g_array_remove_index (hdrs->array, index);
291 g_hash_table_remove (hdrs->concat, name);
297 * soup_message_headers_get_one:
298 * @hdrs: a #SoupMessageHeaders
301 * Gets the value of header @name in @hdrs. Use this for headers whose
302 * values are <emphasis>not</emphasis> comma-delimited lists, and
303 * which therefore can only appear at most once in the headers. For
304 * list-valued headers, use soup_message_headers_get_list().
306 * If @hdrs does erroneously contain multiple copies of the header, it
307 * is not defined which one will be returned. (Ideally, it will return
308 * whichever one makes libsoup most compatible with other HTTP
311 * Return value: the header's value or %NULL if not found.
316 soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
318 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
319 guint hdr_length = hdrs->array->len;
322 g_return_val_if_fail (name != NULL, NULL);
324 name = intern_header_name (name, NULL);
326 index = find_last_header (hdr_array, hdr_length, name, 0);
328 return (index == -1) ? NULL : hdr_array[index].value;
332 * soup_message_headers_get_list:
333 * @hdrs: a #SoupMessageHeaders
336 * Gets the value of header @name in @hdrs. Use this for headers whose
337 * values are comma-delimited lists, and which are therefore allowed
338 * to appear multiple times in the headers. For non-list-valued
339 * headers, use soup_message_headers_get_one().
341 * If @name appears multiple times in @hdrs,
342 * soup_message_headers_get_list() will concatenate all of the values
343 * together, separated by commas. This is sometimes awkward to parse
344 * (eg, WWW-Authenticate, Set-Cookie), but you have to be able to deal
345 * with it anyway, because the HTTP spec explicitly states that this
346 * transformation is allowed, and so an upstream proxy could do the
349 * Return value: the header's value or %NULL if not found.
354 soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name)
356 SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
361 g_return_val_if_fail (name != NULL, NULL);
363 name = intern_header_name (name, NULL);
365 value = g_hash_table_lookup (hdrs->concat, name);
370 index = find_header (hdr_array, name, 0);
373 else if (find_header (hdr_array, name, 1) == -1)
374 return hdr_array[index].value;
376 concat = g_string_new (NULL);
377 for (i = 0; (index = find_header (hdr_array, name, i)) != -1; i++) {
379 g_string_append (concat, ", ");
380 g_string_append (concat, hdr_array[index].value);
382 value = g_string_free (concat, FALSE);
385 hdrs->concat = g_hash_table_new_full (NULL, NULL, NULL, g_free);
386 g_hash_table_insert (hdrs->concat, (gpointer)name, value);
391 * soup_message_headers_get:
392 * @hdrs: a #SoupMessageHeaders
395 * Gets the value of header @name in @hdrs.
397 * This method was supposed to work correctly for both single-valued
398 * and list-valued headers, but because some HTTP clients/servers
399 * mistakenly send multiple copies of headers that are supposed to be
400 * single-valued, it sometimes returns incorrect results. To fix this,
401 * the methods soup_message_headers_get_one() and
402 * soup_message_headers_get_list() were introduced, so callers can
403 * explicitly state which behavior they are expecting.
405 * Return value: as with soup_message_headers_get_list().
407 * Deprecated: Use soup_message_headers_get_one() or
408 * soup_message_headers_get_list() instead.
411 soup_message_headers_get (SoupMessageHeaders *hdrs, const char *name)
413 return soup_message_headers_get_list (hdrs, name);
417 * SoupMessageHeadersIter:
419 * An opaque type used to iterate over a %SoupMessageHeaders
422 * After intializing the iterator with
423 * soup_message_headers_iter_init(), call
424 * soup_message_headers_iter_next() to fetch data from it.
426 * You may not modify the headers while iterating over them.
430 SoupMessageHeaders *hdrs;
432 } SoupMessageHeadersIterReal;
435 * soup_message_headers_iter_init:
436 * @iter: (out) (transfer none): a pointer to a %SoupMessageHeadersIter
438 * @hdrs: a %SoupMessageHeaders
440 * Initializes @iter for iterating @hdrs.
443 soup_message_headers_iter_init (SoupMessageHeadersIter *iter,
444 SoupMessageHeaders *hdrs)
446 SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
453 * soup_message_headers_iter_next:
454 * @iter: (inout) (transfer none): a %SoupMessageHeadersIter
455 * @name: (out) (transfer none): pointer to a variable to return
457 * @value: (out) (transfer none): pointer to a variable to return
458 * the header value in
460 * Yields the next name/value pair in the %SoupMessageHeaders being
461 * iterated by @iter. If @iter has already yielded the last header,
462 * then soup_message_headers_iter_next() will return %FALSE and @name
463 * and @value will be unchanged.
465 * Return value: %TRUE if another name and value were returned, %FALSE
466 * if the end of the headers has been reached.
469 soup_message_headers_iter_next (SoupMessageHeadersIter *iter,
470 const char **name, const char **value)
472 SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
473 SoupHeader *hdr_array = (SoupHeader *)real->hdrs->array->data;
475 if (real->index >= real->hdrs->array->len)
478 *name = hdr_array[real->index].name;
479 *value = hdr_array[real->index].value;
485 * SoupMessageHeadersForeachFunc:
486 * @name: the header name
487 * @value: the header value
488 * @user_data: the data passed to soup_message_headers_foreach()
490 * The callback passed to soup_message_headers_foreach().
494 * soup_message_headers_foreach:
495 * @hdrs: a #SoupMessageHeaders
496 * @func: (scope call): callback function to run for each header
497 * @user_data: data to pass to @func
499 * Calls @func once for each header value in @hdrs.
501 * Beware that unlike soup_message_headers_get(), this processes the
502 * headers in exactly the way they were added, rather than
503 * concatenating multiple same-named headers into a single value.
504 * (This is intentional; it ensures that if you call
505 * soup_message_headers_append() multiple times with the same name,
506 * then the I/O code will output multiple copies of the header when
507 * sending the message to the remote implementation, which may be
508 * required for interoperability in some cases.)
510 * You may not modify the headers from @func.
513 soup_message_headers_foreach (SoupMessageHeaders *hdrs,
514 SoupMessageHeadersForeachFunc func,
517 SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
520 for (i = 0; i < hdrs->array->len; i++)
521 func (hdr_array[i].name, hdr_array[i].value, user_data);
525 static GStaticMutex header_pool_mutex = G_STATIC_MUTEX_INIT;
526 static GHashTable *header_pool, *header_setters;
528 static void transfer_encoding_setter (SoupMessageHeaders *, const char *);
529 static void content_length_setter (SoupMessageHeaders *, const char *);
530 static void expectation_setter (SoupMessageHeaders *, const char *);
531 static void content_type_setter (SoupMessageHeaders *, const char *);
534 intern_header_locked (const char *name)
538 interned = g_hash_table_lookup (header_pool, name);
540 char *dup = g_strdup (name);
541 g_hash_table_insert (header_pool, dup, dup);
548 intern_header_name (const char *name, SoupHeaderSetter *setter)
550 const char *interned;
552 g_static_mutex_lock (&header_pool_mutex);
555 header_pool = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
556 header_setters = g_hash_table_new (NULL, NULL);
557 g_hash_table_insert (header_setters,
558 intern_header_locked ("Transfer-Encoding"),
559 transfer_encoding_setter);
560 g_hash_table_insert (header_setters,
561 intern_header_locked ("Content-Length"),
562 content_length_setter);
563 g_hash_table_insert (header_setters,
564 intern_header_locked ("Expect"),
566 g_hash_table_insert (header_setters,
567 intern_header_locked ("Content-Type"),
568 content_type_setter);
571 interned = intern_header_locked (name);
573 *setter = g_hash_table_lookup (header_setters, interned);
575 g_static_mutex_unlock (&header_pool_mutex);
580 clear_special_headers (SoupMessageHeaders *hdrs)
582 SoupHeaderSetter setter;
586 /* Make sure header_setters has been initialized */
587 intern_header_name ("", NULL);
589 g_hash_table_iter_init (&iter, header_setters);
590 while (g_hash_table_iter_next (&iter, &key, &value)) {
596 /* Specific headers */
599 transfer_encoding_setter (SoupMessageHeaders *hdrs, const char *value)
602 if (g_ascii_strcasecmp (value, "chunked") == 0)
603 hdrs->encoding = SOUP_ENCODING_CHUNKED;
605 hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
611 content_length_setter (SoupMessageHeaders *hdrs, const char *value)
613 /* Transfer-Encoding trumps Content-Length */
614 if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
620 hdrs->content_length = g_ascii_strtoull (value, &end, 10);
622 hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
624 hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
631 * @SOUP_ENCODING_UNRECOGNIZED: unknown / error
632 * @SOUP_ENCODING_NONE: no body is present (which is not the same as a
633 * 0-length body, and only occurs in certain places)
634 * @SOUP_ENCODING_CONTENT_LENGTH: Content-Length encoding
635 * @SOUP_ENCODING_EOF: Response body ends when the connection is closed
636 * @SOUP_ENCODING_CHUNKED: chunked encoding (currently only supported
638 * @SOUP_ENCODING_BYTERANGES: multipart/byteranges (Reserved for future
639 * use: NOT CURRENTLY IMPLEMENTED)
641 * How a message body is encoded for transport
645 * soup_message_headers_get_encoding:
646 * @hdrs: a #SoupMessageHeaders
648 * Gets the message body encoding that @hdrs declare. This may not
649 * always correspond to the encoding used on the wire; eg, a HEAD
650 * response may declare a Content-Length or Transfer-Encoding, but
651 * it will never actually include a body.
653 * Return value: the encoding declared by @hdrs.
656 soup_message_headers_get_encoding (SoupMessageHeaders *hdrs)
660 if (hdrs->encoding != -1)
661 return hdrs->encoding;
663 /* If Transfer-Encoding was set, hdrs->encoding would already
664 * be set. So we don't need to check that possibility.
666 header = soup_message_headers_get_one (hdrs, "Content-Length");
668 content_length_setter (hdrs, header);
669 if (hdrs->encoding != -1)
670 return hdrs->encoding;
673 /* Per RFC 2616 4.4, a response body that doesn't indicate its
674 * encoding otherwise is terminated by connection close, and a
675 * request that doesn't indicate otherwise has no body. Note
676 * that SoupMessage calls soup_message_headers_set_encoding()
677 * to override the response body default for our own
678 * server-side messages.
680 hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
681 SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
682 return hdrs->encoding;
686 * soup_message_headers_set_encoding:
687 * @hdrs: a #SoupMessageHeaders
688 * @encoding: a #SoupEncoding
690 * Sets the message body encoding that @hdrs will declare. In particular,
691 * you should use this if you are going to send a request or response in
695 soup_message_headers_set_encoding (SoupMessageHeaders *hdrs,
696 SoupEncoding encoding)
698 if (encoding == hdrs->encoding)
702 case SOUP_ENCODING_NONE:
703 case SOUP_ENCODING_EOF:
704 soup_message_headers_remove (hdrs, "Transfer-Encoding");
705 soup_message_headers_remove (hdrs, "Content-Length");
708 case SOUP_ENCODING_CONTENT_LENGTH:
709 soup_message_headers_remove (hdrs, "Transfer-Encoding");
712 case SOUP_ENCODING_CHUNKED:
713 soup_message_headers_remove (hdrs, "Content-Length");
714 soup_message_headers_replace (hdrs, "Transfer-Encoding", "chunked");
718 g_return_if_reached ();
721 hdrs->encoding = encoding;
725 * soup_message_headers_get_content_length:
726 * @hdrs: a #SoupMessageHeaders
728 * Gets the message body length that @hdrs declare. This will only
729 * be non-0 if soup_message_headers_get_encoding() returns
730 * %SOUP_ENCODING_CONTENT_LENGTH.
732 * Return value: the message body length declared by @hdrs.
735 soup_message_headers_get_content_length (SoupMessageHeaders *hdrs)
737 return (hdrs->encoding == SOUP_ENCODING_CONTENT_LENGTH) ?
738 hdrs->content_length : 0;
742 * soup_message_headers_set_content_length:
743 * @hdrs: a #SoupMessageHeaders
744 * @content_length: the message body length
746 * Sets the message body length that @hdrs will declare, and sets
747 * @hdrs's encoding to %SOUP_ENCODING_CONTENT_LENGTH.
749 * You do not normally need to call this; if @hdrs is set to use
750 * Content-Length encoding, libsoup will automatically set its
751 * Content-Length header for you immediately before sending the
752 * headers. One situation in which this method is useful is when
753 * generating the response to a HEAD request; Calling
754 * soup_message_headers_set_content_length() allows you to put the
755 * correct content length into the response without needing to waste
756 * memory by filling in a response body which won't actually be sent.
759 soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
760 goffset content_length)
764 snprintf (length, sizeof (length), "%" G_GUINT64_FORMAT,
766 soup_message_headers_remove (hdrs, "Transfer-Encoding");
767 soup_message_headers_replace (hdrs, "Content-Length", length);
771 expectation_setter (SoupMessageHeaders *hdrs, const char *value)
774 if (!g_ascii_strcasecmp (value, "100-continue"))
775 hdrs->expectations = SOUP_EXPECTATION_CONTINUE;
777 hdrs->expectations = SOUP_EXPECTATION_UNRECOGNIZED;
779 hdrs->expectations = 0;
784 * @SOUP_EXPECTATION_CONTINUE: "100-continue"
785 * @SOUP_EXPECTATION_UNRECOGNIZED: any unrecognized expectation
787 * Represents the parsed value of the "Expect" header.
791 * soup_message_headers_get_expectations:
792 * @hdrs: a #SoupMessageHeaders
794 * Gets the expectations declared by @hdrs's "Expect" header.
795 * Currently this will either be %SOUP_EXPECTATION_CONTINUE or
796 * %SOUP_EXPECTATION_UNRECOGNIZED.
798 * Return value: the contents of @hdrs's "Expect" header
801 soup_message_headers_get_expectations (SoupMessageHeaders *hdrs)
803 return hdrs->expectations;
807 * soup_message_headers_set_expectations:
808 * @hdrs: a #SoupMessageHeaders
809 * @expectations: the expectations to set
811 * Sets @hdrs's "Expect" header according to @expectations.
813 * Currently %SOUP_EXPECTATION_CONTINUE is the only known expectation
814 * value. You should set this value on a request if you are sending a
815 * large message body (eg, via POST or PUT), and want to give the
816 * server a chance to reject the request after seeing just the headers
817 * (eg, because it will require authentication before allowing you to
818 * post, or because you're POSTing to a URL that doesn't exist). This
819 * saves you from having to transmit the large request body when the
820 * server is just going to ignore it anyway.
823 soup_message_headers_set_expectations (SoupMessageHeaders *hdrs,
824 SoupExpectation expectations)
826 g_return_if_fail ((expectations & ~SOUP_EXPECTATION_CONTINUE) == 0);
828 if (expectations & SOUP_EXPECTATION_CONTINUE)
829 soup_message_headers_replace (hdrs, "Expect", "100-continue");
831 soup_message_headers_remove (hdrs, "Expect");
836 * @start: the start of the range
837 * @end: the end of the range
839 * Represents a byte range as used in the Range header.
841 * If @end is non-negative, then @start and @end represent the bounds
842 * of of the range, counting from %0. (Eg, the first 500 bytes would be
843 * represented as @start = %0 and @end = %499.)
845 * If @end is %-1 and @start is non-negative, then this represents a
846 * range starting at @start and ending with the last byte of the
847 * requested resource body. (Eg, all but the first 500 bytes would be
848 * @start = %500, and @end = %-1.)
850 * If @end is %-1 and @start is negative, then it represents a "suffix
851 * range", referring to the last -@start bytes of the resource body.
852 * (Eg, the last 500 bytes would be @start = %-500 and @end = %-1.)
858 sort_ranges (gconstpointer a, gconstpointer b)
860 SoupRange *ra = (SoupRange *)a;
861 SoupRange *rb = (SoupRange *)b;
863 return ra->start - rb->start;
867 * soup_message_headers_get_ranges:
868 * @hdrs: a #SoupMessageHeaders
869 * @total_length: the total_length of the response body
870 * @ranges: (out): return location for an array of #SoupRange
871 * @length: the length of the returned array
873 * Parses @hdrs's Range header and returns an array of the requested
874 * byte ranges. The returned array must be freed with
875 * soup_message_headers_free_ranges().
877 * If @total_length is non-0, its value will be used to adjust the
878 * returned ranges to have explicit start and end values, and the
879 * returned ranges will be sorted and non-overlapping. If
880 * @total_length is 0, then some ranges may have an end value of -1,
881 * as described under #SoupRange, and some of the ranges may be
884 * Return value: %TRUE if @hdrs contained a "Range" header containing
885 * byte ranges which could be parsed, %FALSE otherwise (in which case
886 * @range and @length will not be set).
891 soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
892 goffset total_length,
896 const char *range = soup_message_headers_get_one (hdrs, "Range");
897 GSList *range_list, *r;
902 if (!range || strncmp (range, "bytes", 5) != 0)
906 while (g_ascii_isspace (*range))
910 while (g_ascii_isspace (*range))
913 range_list = soup_header_parse_list (range);
917 array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
918 for (r = range_list; r; r = r->next) {
923 cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
924 cur.end = total_length - 1;
926 cur.start = g_ascii_strtoull (spec, &end, 10);
930 cur.end = g_ascii_strtoull (end, &end, 10);
932 cur.end = total_length - 1;
935 g_array_free (array, TRUE);
936 soup_header_free_list (range_list);
940 g_array_append_val (array, cur);
943 soup_header_free_list (range_list);
946 g_array_sort (array, sort_ranges);
947 for (i = 1; i < array->len; i++) {
948 SoupRange *cur = &((SoupRange *)array->data)[i];
949 SoupRange *prev = &((SoupRange *)array->data)[i - 1];
951 if (cur->start <= prev->end) {
952 prev->end = MAX (prev->end, cur->end);
953 g_array_remove_index (array, i);
958 *ranges = (SoupRange *)array->data;
959 *length = array->len;
961 g_array_free (array, FALSE);
966 * soup_message_headers_free_ranges:
967 * @hdrs: a #SoupMessageHeaders
968 * @ranges: an array of #SoupRange
970 * Frees the array of ranges returned from soup_message_headers_get_ranges().
975 soup_message_headers_free_ranges (SoupMessageHeaders *hdrs,
982 * soup_message_headers_set_ranges:
983 * @hdrs: a #SoupMessageHeaders
984 * @ranges: an array of #SoupRange
985 * @length: the length of @range
987 * Sets @hdrs's Range header to request the indicated ranges. (If you
988 * only want to request a single range, you can use
989 * soup_message_headers_set_range().)
994 soup_message_headers_set_ranges (SoupMessageHeaders *hdrs,
1001 header = g_string_new ("bytes=");
1002 for (i = 0; i < length; i++) {
1004 g_string_append_c (header, ',');
1005 if (ranges[i].end >= 0) {
1006 g_string_append_printf (header, "%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT,
1007 ranges[i].start, ranges[i].end);
1008 } else if (ranges[i].start >= 0) {
1009 g_string_append_printf (header,"%" G_GINT64_FORMAT "-",
1012 g_string_append_printf (header, "%" G_GINT64_FORMAT,
1017 soup_message_headers_replace (hdrs, "Range", header->str);
1018 g_string_free (header, TRUE);
1022 * soup_message_headers_set_range:
1023 * @hdrs: a #SoupMessageHeaders
1024 * @start: the start of the range to request
1025 * @end: the end of the range to request
1027 * Sets @hdrs's Range header to request the indicated range.
1028 * @start and @end are interpreted as in a #SoupRange.
1030 * If you need to request multiple ranges, use
1031 * soup_message_headers_set_ranges().
1036 soup_message_headers_set_range (SoupMessageHeaders *hdrs,
1042 range.start = start;
1044 soup_message_headers_set_ranges (hdrs, &range, 1);
1048 * soup_message_headers_get_content_range:
1049 * @hdrs: a #SoupMessageHeaders
1050 * @start: return value for the start of the range
1051 * @end: return value for the end of the range
1052 * @total_length: return value for the total length of the resource,
1053 * or %NULL if you don't care.
1055 * Parses @hdrs's Content-Range header and returns it in @start,
1056 * @end, and @total_length. If the total length field in the header
1057 * was specified as "*", then @total_length will be set to -1.
1059 * Return value: %TRUE if @hdrs contained a "Content-Range" header
1060 * containing a byte range which could be parsed, %FALSE otherwise.
1065 soup_message_headers_get_content_range (SoupMessageHeaders *hdrs,
1068 goffset *total_length)
1070 const char *header = soup_message_headers_get_one (hdrs, "Content-Range");
1074 if (!header || strncmp (header, "bytes ", 6) != 0)
1078 while (g_ascii_isspace (*header))
1080 if (!g_ascii_isdigit (*header))
1083 *start = g_ascii_strtoull (header, &p, 10);
1086 *end = g_ascii_strtoull (p + 1, &p, 10);
1094 length = g_ascii_strtoull (p, &p, 10);
1097 *total_length = length;
1102 * soup_message_headers_set_content_range:
1103 * @hdrs: a #SoupMessageHeaders
1104 * @start: the start of the range
1105 * @end: the end of the range
1106 * @total_length: the total length of the resource, or -1 if unknown
1108 * Sets @hdrs's Content-Range header according to the given values.
1109 * (Note that @total_length is the total length of the entire resource
1110 * that this is a range of, not simply @end - @start + 1.)
1115 soup_message_headers_set_content_range (SoupMessageHeaders *hdrs,
1118 goffset total_length)
1122 if (total_length >= 0) {
1123 header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
1124 G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
1125 start, end, total_length);
1127 header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
1128 G_GINT64_FORMAT "/*", start, end);
1130 soup_message_headers_replace (hdrs, "Content-Range", header);
1135 parse_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
1136 char **foo, GHashTable **params)
1141 header = soup_message_headers_get_one (hdrs, header_name);
1146 *foo = g_strdup (header);
1147 semi = strchr (*foo, ';');
1152 while (p - 1 > *foo && g_ascii_isspace(p[-1]))
1156 semi = strchr (header, ';');
1165 *params = soup_header_parse_semi_param_list ("");
1169 *params = soup_header_parse_semi_param_list (semi);
1174 set_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
1175 const char *foo, GHashTable *params)
1178 GHashTableIter iter;
1179 gpointer key, value;
1181 str = g_string_new (foo);
1183 g_hash_table_iter_init (&iter, params);
1184 while (g_hash_table_iter_next (&iter, &key, &value)) {
1185 g_string_append (str, "; ");
1186 soup_header_g_string_append_param (str, key, value);
1190 soup_message_headers_replace (hdrs, header_name, str->str);
1191 g_string_free (str, TRUE);
1195 content_type_setter (SoupMessageHeaders *hdrs, const char *value)
1197 g_free (hdrs->content_type);
1199 char *content_type, *p;
1201 parse_content_foo (hdrs, "Content-Type", &content_type, NULL);
1202 p = strpbrk (content_type, " /");
1203 if (!p || *p != '/' || strpbrk (p + 1, " /")) {
1204 g_free (content_type);
1205 hdrs->content_type = NULL;
1207 hdrs->content_type = content_type;
1209 hdrs->content_type = NULL;
1213 * soup_message_headers_get_content_type:
1214 * @hdrs: a #SoupMessageHeaders
1215 * @params: (out) (allow-none) (transfer full): return location for
1216 * the Content-Type parameters (eg, "charset"), or %NULL
1218 * Looks up the "Content-Type" header in @hdrs, parses it, and returns
1219 * its value in *@content_type and *@params. @params can be %NULL if you
1220 * are only interested in the content type itself.
1222 * Return value: a string with the value of the "Content-Type" header
1223 * or NULL if @hdrs does not contain that header or it cannot be
1224 * parsed (in which case *@params will be unchanged).
1229 soup_message_headers_get_content_type (SoupMessageHeaders *hdrs,
1230 GHashTable **params)
1232 if (!hdrs->content_type)
1236 parse_content_foo (hdrs, "Content-Type", NULL, params);
1237 return hdrs->content_type;
1241 * soup_message_headers_set_content_type:
1242 * @hdrs: a #SoupMessageHeaders
1243 * @content_type: the MIME type
1244 * @params: (allow-none) (element-type utf8 utf8): additional
1245 * parameters, or %NULL
1247 * Sets the "Content-Type" header in @hdrs to @content_type,
1248 * optionally with additional parameters specified in @params.
1253 soup_message_headers_set_content_type (SoupMessageHeaders *hdrs,
1254 const char *content_type,
1257 set_content_foo (hdrs, "Content-Type", content_type, params);
1261 * soup_message_headers_get_content_disposition:
1262 * @hdrs: a #SoupMessageHeaders
1263 * @disposition: (out) (transfer full): return location for the
1264 * disposition-type, or %NULL
1265 * @params: (out) (transfer full) (element-type utf8 utf8): return
1266 * location for the Content-Disposition parameters, or %NULL
1268 * Looks up the "Content-Disposition" header in @hdrs, parses it, and
1269 * returns its value in *@disposition and *@params. @params can be
1270 * %NULL if you are only interested in the disposition-type.
1272 * In HTTP, the most common use of this header is to set a
1273 * disposition-type of "attachment", to suggest to the browser that a
1274 * response should be saved to disk rather than displayed in the
1275 * browser. If @params contains a "filename" parameter, this is a
1276 * suggestion of a filename to use. (If the parameter value in the
1277 * header contains an absolute or relative path, libsoup will truncate
1278 * it down to just the final path component, so you do not need to
1279 * test this yourself.)
1281 * Content-Disposition is also used in "multipart/form-data", however
1282 * this is handled automatically by #SoupMultipart and the associated
1285 * Return value: %TRUE if @hdrs contains a "Content-Disposition"
1286 * header, %FALSE if not (in which case *@disposition and *@params
1287 * will be unchanged).
1292 soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs,
1294 GHashTable **params)
1296 gpointer orig_key, orig_value;
1298 if (!parse_content_foo (hdrs, "Content-Disposition",
1299 disposition, params))
1302 /* If there is a filename parameter, make sure it contains
1303 * only a single path component
1305 if (params && g_hash_table_lookup_extended (*params, "filename",
1306 &orig_key, &orig_value)) {
1307 char *filename = strrchr (orig_value, '/');
1310 g_hash_table_insert (*params, orig_key, filename + 1);
1316 * soup_message_headers_set_content_disposition:
1317 * @hdrs: a #SoupMessageHeaders
1318 * @disposition: the disposition-type
1319 * @params: (allow-none) (element-type utf8 utf8): additional
1320 * parameters, or %NULL
1322 * Sets the "Content-Disposition" header in @hdrs to @disposition,
1323 * optionally with additional parameters specified in @params.
1325 * See soup_message_headers_get_content_disposition() for a discussion
1326 * of how Content-Disposition is used in HTTP.
1331 soup_message_headers_set_content_disposition (SoupMessageHeaders *hdrs,
1332 const char *disposition,
1335 set_content_foo (hdrs, "Content-Disposition", disposition, params);