--- /dev/null
+SOUP Authors
+============
+
+Original SOUP 0.0 - 0.7:
+ Alex Graveley
+ Dick Porter
+ Miguel De Icaza
+
+libsoup 1.99.x - 2.2:
+ Joe Shaw
+ Dan Winship
+
+See MAINTAINERS for the current maintainers.
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+CODE STYLE
+----------
+
+Please use the style used by the rest of the code. Among other things,
+this means:
+
+ * Tabs, not spaces, for indentation
+
+ * Put spaces:
+ * around binary operators
+ * between if/while/for/switch and "("
+ * between function name and "("
+ * between ")" and "{"
+ * after ","
+
+ * if/for/while bodies:
+
+ * Single-line bodies should (a) be on their own line, and (b)
+ not have braces around them
+
+ * Multi-line bodies should have braces around them, even if
+ the body is only a single statement and the braces are not
+ syntactically necessary.
+
+ * Eg:
+
+ for (i = 0; i < len; i++) {
+ if (find (i, something))
+ break;
+ else {
+ function_with_big_name (i, something,
+ something_else);
+ }
+ }
+
+ * C89, not C99. (In particular, don't declare variables in the
+ middle of blocks.)
+
+ * Do not use gint, gchar, glong, and gshort. (Other g-types, such
+ as gpointer and the unsigned types are fine.)
+
+CORRECTNESS
+-----------
+
+ * libsoup builds with lots of -W options by default, and should
+ not print any warnings while compiling (unless they're caused by
+ #included files from other projects, eg, proxy.h). You can use
+ "make > /dev/null" to do a full compile showing only the
+ warnings/errors, to make sure your patch does not introduce any
+ more.
+
+ * There are a number of regression tests in the tests/ directory.
+ Running "make check" will run all of them (or at least, all of
+ the ones that it can run based on what software you have
+ installed. Eg, some tests require apache to be installed.) You
+ should run "make check" before submitting a patch that could
+ potentially change libsoup's behavior. ("make check" will warn
+ you if it was not able to run all of the tests. If you are
+ making extensive changes, or changing very low-level functions,
+ you may want to install all of the optional pieces so you can
+ run all of the regression tests.)
+
+ * libsoup ought to build correctly from outside its source tree,
+ so if you make large changes to the Makefiles, try a "make
+ distcheck" to verify that an out-of-source-tree build still
+ works.
--- /dev/null
+## Process this file with automake to produce Makefile.in
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = libsoup tests docs
+
+EXTRA_DIST = \
+ libsoup-2.4.pc.in \
+ libsoup-gnome-2.4.pc.in \
+ gtk-doc.make \
+ libsoup-zip.in \
+ m4/introspection.m4
+
+DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-introspection
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = libsoup-2.4.pc
+
+if BUILD_LIBSOUP_GNOME
+pkgconfig_DATA += libsoup-gnome-2.4.pc
+endif
--- /dev/null
+Changes in libsoup from 2.35.5 to 2.35.90:
+
+ * Added SOUP_MESSAGE_CAN_REBUILD flag, to use with
+ soup_message_body_set_accumulate(FALSE) on a request body,
+ to indicate that the caller will recreate the request body
+ after it has been discarded if the message needs to be
+ re-sent. [#656650]
+
+ * Fixed the build on mingw-w64 by not using "interface" as
+ as variable name. [#656402, Erik van Pienbroek]
+
+ * (The multihosted https server regression mentioned in the
+ 2.35.5 NEWS turned out to be a glib-networking bug, which is
+ fixed as of 2.29.18.)
+
+Changes in libsoup from 2.35.4 to 2.35.5:
+
+ * Support NTLM single sign on via samba's /usr/bin/ntlm_auth.
+ [#650940, Mandy Wu]. This is enabled by default (if NTLM is
+ enabled for the session and the ntlm_auth binary is
+ available), but will fall back to the standard
+ password-based authentication if SSO fails.
+
+ * Default to TLS+extensions for https connections, falling
+ back to SSLv3-without-extensions (the old default) only if
+ the server fails to negotiate TLS. [#581342]
+
+ * As a result of this change, some users are currently
+ seeing problems with sites that serve multiple https
+ hosts from a single IP address (eg,
+ *.launchpad.net). There is no known workaround at
+ this time.
+
+ * Fixed a longstanding problem with https pages sometimes not
+ loading when using a proxy. [#631368, based on patches from
+ DongJae Kim and Thierry Reding]
+
+ * SoupContentSniffer: don't use gio's sniffing rules, since
+ the spec now recommends that browsers not do any additional
+ sniffing beyond what's in the spec. [#648846, "arno"]
+
+ * Fixed SoupRequestHTTP to work properly with alternate
+ GMainContexts. [#653707]
+
+ * Added some annotations from Vala's vapi files. [#655397,
+ Evan Nemerson]. Also, removed SoupProxyResolver from the
+ gir/typelib, since it had been deprecated since before
+ introspection was available, and it was being scanned wrong
+ anyway.
+
+Changes in libsoup from 2.35.3 to 2.35.4:
+
+ * CVE-2011-2524: Fixed a security hole that caused some
+ SoupServer users to unintentionally allow accessing the
+ entire local filesystem when they thought they were only
+ providing access to a single directory. [#653258]
+
+ * Plugged another SoupCache memory leak [Xan]
+
+ * Simplified SoupCache keys, and handle collisions. [#649963,
+ Sergio]
+
+ * Annotate SoupSession:add-feature, etc, as (skip), so they
+ don't conflict with the methods of the same name. [#655150,
+ Jasper St. Pierre]
+
+Changes in libsoup from 2.34.1 to 2.35.3:
+
+ * SoupCache fixes [Sergio]:
+
+ * Don't store hop-by-hop headers [#650094]
+
+ * Fix status code and headers of responses returned
+ from the cache after being revalidated [#649965]
+
+ * Added versioning support to the cache file format
+ [#652554] and extended it to keep track of the
+ status code [#649965]
+
+ * Fixed storage of time values in the cache [#653349]
+
+ * Fixed a use-after-free that could result in bogus
+ data being written to the cache [#650620]
+
+ * Various leaks [#649309]
+
+ * Work around an Apache bug where it tells browsers to
+ automatically decode .gz files but still keep the .gz in the
+ name. [#613361, Dan]
+
+ * Fixed an overflow when sending a response larger than 2^31
+ bytes (eg, streaming movies in Rygel). [#638468, Jens Georg]
+
+ * Always send the Keep-Alive header, not just to hosts we
+ already know to be HTTP/1.0 [#648680, Sergio]
+
+ * Fixed various leaks [#652699, Sergio], [#651643, Milan],
+ [etc, Xan]
+
+ * Minor build fix for Debian/Ubuntu [#648948]
+
+ * Fixed a docs typo
+
+Changes in libsoup from 2.34.0 to 2.34.1:
+
+ * Two multipart forms/Content-Disposition fixes [Dan]:
+
+ * UTF-8-encoded header encoding/decoding rules updated
+ to match RFC 5987. In particular, a UTF-8-encoded
+ filename parameter in Content-Disposition will now
+ override an ASCII one.
+
+ * When not using UTF-8-encoded filenames in
+ Content-Disposition, always quote the filename,
+ since some servers erroneously fail to handle
+ non-quoted ones. [#641280]
+
+ * Fixed several memory leaks [Dan, Xan]
+
+ * Fixed decoding base64 data: URLs [#646896, Sergio]
+
+ * Simplified the libsoup-gnome build on Windows by dropping
+ the gnome-keyring dependency. [Fridrich Å trba]
+
+ * Fixed a bug in soup_uri_to_string() in which (invalid) port
+ numbers could be output as negative numbers (tripping up a
+ WebKit "sanity checking" test). [#647767, Sergio]
+
+ * Fixed a cache corruption bug in SoupCache. [#648285, Sergio]
+
+ * Fixed a crash in SoupSessionSync when using
+ soup_session_abort().
+
+Changes in libsoup from 2.33.92 to 2.34.0:
+
+ * Fixed the GMainContext handling of the new
+ SoupProxyResolverDefault (which among other things fixes
+ gstreamer usage inside epiphany). [#646201, Sebastian Dröge]
+
+ * Tweaked the introspection Makefile rules to fix a problem
+ building on Debian/Ubuntu. [#645505]
+
+ * Belated bumped the shared library versioning to reflect the
+ API additions since 2.32.0
+
+Changes in libsoup from 2.33.90 to 2.33.92:
+
+ * LIBSOUP NO LONGER DEPENDS ON GCONF OR LIBPROXY.
+ (see below).
+
+ * Added SoupProxyResolverDefault, which uses uses gio's
+ GProxyResolver to resolve proxies [#642982, Gustavo Noronha
+ Silva]. Despite the "default" in the name, it is not used by
+ default, for compatibility reasons, but it is available in
+ plain libsoup, not libsoup-gnome. (Of course, it depends on
+ having glib-networking installed.)
+
+ * Updated SoupProxyResolverGNOME to be based on
+ SoupProxyResolverDefault, but explicitly requesting the
+ "gnome" GProxyResolver if it is available [#625898], and
+ removed the old code that used GConf and libproxy directly.
+
+ * Added soup_server_disconnect(), to explicitly disconnect a
+ SoupServer, since it is not possible to g_object_unref() it
+ from memory-managed language bindings. [#638576, Andreas
+ Rottmann]
+
+ * SoupDate now parses month names case-insensitively [#644048,
+ Christian Dywan]
+
+ * Avoid a g_return_if_fail() when using
+ SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY with non-http URIs
+ (file:, data:, etc). [#643226]
+
+ * SoupCookieJar now catches overflows when parsing very
+ distant dates [#643462, based on a patch from Mark
+ Starovoytov]
+
+ * Fixed a buggy interaction between request body streaming and
+ restarted requests [David Woodhouse]. Added some new tests
+ to tests/chunk-test.c to make sure that a specific
+ (unsupported!) way of using those methods would not get
+ broken in the future.
+
+ * Fixed soup_socket_get_remote_address(), which had been
+ broken since 2.33.4 (and which in turn caused
+ soup_client_context_get_address/_get_host to be broken).
+ [#645227]
+
+Changes in libsoup from 2.33.6 to 2.33.90:
+
+ * Attempted to make libsoup's dependency on glib-networking
+ (for TLS support) more explicit, by requiring that
+ glib-networking be present at compile time unless the
+ builder explicitly passes "--disable-tls-check", and noting
+ that packagers should give libsoup a runtime dependency on
+ glib-networking.
+
+ * Fixed a bug in talking to servers with virtual hosts with
+ Unicode names (IDNs) [#642075]
+
+ * Added a "Connection: Keep-Alive" header when talking to
+ HTTP/1.0 hosts, to improve performance. [#640414, Sergio
+ Villar Senin]
+
+ * Changed SoupCache to not cache multipart/x-mixed-replace
+ content. [#642028, Sergio Villar Senin]
+
+Changes in libsoup from 2.33.5 to 2.33.6:
+
+ * Made SoupSessionAsync do idle-connection cleanup more
+ sanely, resulting in faster load times for pages with lots
+ of subresources. [#639768, Sergio Villar Senin]
+
+ * Fixed soup_form_decode()'s behavior (and by extension,
+ SoupServer query string handling) with datasets that contain
+ multiple values for the same key, and added a test case.
+ [#639783, Sven Neumann]
+
+ * Fixed warnings pointed out by gcc 4.6, including a bug in
+ SoupCache that would cause unnecessary revalidations
+ [#640556].
+
+ * Belated copied a fix from the WebKit copy of soup-cache.c
+ into ours, and fixed a bug in the local copy of
+ soup-request-data.c [#641022, Sergio Villar Senin], in
+ preparation for making WebKit use the libsoup versions.
+
+Changes in libsoup from 2.33.4 to 2.33.5:
+
+ * Fixed certain cases of soup_session_cancel_message() with
+ SoupSessionSync that could previously cause warnings or
+ crashes. [#637741]
+
+Changes in libsoup from 2.32.2 to 2.33.4:
+
+ * SoupSocket now uses GSocketConnection and GTlsConnection
+ internally rather than making socket calls directly and
+ using GIOStream, and TLS is handled via glib's APIs rather
+ than using gnutls directly.
+
+ * The gzip Content-Encoding handler is now implemented using
+ GZlibDecompressor
+
+ * As a result of the above two changes, libsoup no longer
+ directly depends on gnutls, libgcrypt, or zlib, though it
+ still indirectly depends on zlib via glib and libxml2. Also,
+ although libsoup does not depend on glib-networking as a
+ build-time dependency, some "make check" tests will be
+ skipped if it is not installed.
+
+ * The SoupRequest/SoupCache code from WebKit has been
+ imported, but it is not yet recommended for general use, and
+ is not necessarily API stable. [#523100, Sergio Villar, with
+ the SoupRequest parts based on the Summer of Code work by
+ Gabriel Corvalan and the cache parts based on an earlier
+ patch by Xan Lopez]
+
+ * Added SoupMessage:tls-certificate and
+ SoupMessage:tls-errors, which give more information about
+ the certificate used to authenticate a TLS connection.
+
+ * It is now possible to disable Basic or Digest auth in a
+ session by using soup_session_remove_feature_by_type() with
+ SOUP_TYPE_AUTH_BASIC or SOUP_TYPE_AUTH_DIGEST. Likewise, the
+ right way to enable NTLM support now is to call
+ soup_session_add_feature_by_type() with SOUP_TYPE_AUTH_NTLM;
+ SOUP_SESSION_USE_NTLM is now deprecated.
+
+ * Allow setting cookies on file:// URIs, since other browsers
+ do, and WebKit has a test for it. [#603825]
+
+ * .gir/.typelib files now include C header/library information
+ (needed by vala and some other bindings) [#635395, Evan
+ Nemerson]
+
+ * Added annotations on soup_message_headers_get_content_type()
+ [Lucas Rocha] and SoupHTTPVersion [John Palmieri]
+
+ * Fixed a Set-Cookie processing leak [#636741, Jonathan
+ Jongsma]
+
+Changes in libsoup from 2.32.1 to 2.32.2:
+
+ * Fixed a regression in 2.32.0 that caused evolution-exchange
+ to get stuck and stop updating. [#634422]
+
+ * Fixed a regression in 2.32.0 with apps using asynchronous
+ sessions from multiple threads (in particular, gupnp)
+ [#635101]
+
+ * Fixed the regression test for #631525 to not cause spurious
+ "make check" failures on some machines.
+
+Changes in libsoup from 2.32.0 to 2.32.1:
+
+ * Fixed a regression in 2.32.0 with the use of persistent
+ connections that caused spurious "Connection terminated
+ unexpectedly" errors. [#631525, debugged by Sergio Villar]
+
+ * Fixed a regression in 2.32.0 that caused proxy-related
+ DNS errors to return SOUP_STATUS_CANT_RESOLVE rather than
+ SOUP_STATUS_CANT_RESOLVE_PROXY.
+
+ * Usernames/passwords specified explicitly in request URIs now
+ override existing cached auth info. [#631679, Sergio Villar]
+
+ * Changed soup_uri_decode() and soup_uri_normalize() to just
+ ignore malformed %-encoding rather than returning NULL,
+ for consistency with soup_uri_new(). [#630540]
+
+ * Fixed soup_form_decode() to ignore invalid parameters,
+ and soup_form_encode_hash() to just g_return_if_fail()
+ rather than crashing if there are NULL values in the
+ hash. [#620220]
+
+ * Added another workaround for stupid servers that close the
+ connection before returning the full response (in this case,
+ when using chunked encoding and failing to include the
+ final 0-length chunk). [#629160]
+
+ * Fixed a bug in SoupCookieJarText that deleted excess cookies
+ whenever any cookie expired. [#631641, Michał Kazior]
+
+ * Fixed a small leak in SoupContentDecoder if you were using
+ it incorrectly. [pointed out in email by Christophe
+ Gillette]
+
+ * Added regression tests for passwords-in-URIs [#631679,
+ Sergio Villar] and SOUP_SESSION_IDLE_TIMEOUT.
+
+Changes in libsoup from 2.31.92 to 2.32.0:
+
+ * (No changes, just a version bump)
+
+Changes in libsoup from 2.31.90 to 2.31.92:
+
+ * Updated for gobject-introspection 0.9.5. Also added some new
+ annotations and removed a bunch of private headers from the
+ scanning process.
+
+ * Percent-encoded characters in URIs are no longer
+ automatically normalized to uppercase, since apparently some
+ servers are stupid. [#628728, Sergio Villar Senin]
+
+ * Fixed a crash when resolving a URI containing both spaces
+ and non-UTF8 8bit characters. [#629449]
+
+Changes in libsoup from 2.31.2 to 2.31.90:
+
+ * libsoup now tries to connect to each IP address associated
+ with a hostname, if the first one fails. In particular, if a
+ host has both IPv4 and IPv6 addresses, and only one of them
+ is reachable from the current host, libsoup will now try the
+ other one rather than failing. (libc is supposed to sort the
+ IP addresses in the right order, such that, eg, if you don't
+ have IPv6 connectivity, it will put the IPv4 address first.
+ For some reason though, this seems to be broken on some
+ distros.) [#526321].
+
+ * Fixed Accept-Language header generation in locales where ","
+ is used as the decimal point.
+
+Changes in libsoup from 2.31.2 to 2.31.6:
+
+ * Disabled TLS 1.2 in addition to the already-disabled 1.1 and
+ 1.0 [see below, in libsoup 2.27.2], thus making libsoup
+ usable with gnutls 2.10. [#622857. This commit, 01a43ad9,
+ can be applied to libsoup 2.30.x as well if you need that
+ release to work with gnutls 2.10.]
+
+ * When using libproxy 0.3 or newer, libsoup no longer leaks
+ proxy-related environment variables into child processes
+ [#603285]
+
+ * Changed the way message/connection binding works in
+ SoupSession so that (among other things), when there are
+ multiple requests queued to a host, and one of them gets a
+ network error, the other requests are still allowed to try
+ to succeed, rather than all failing immediately. [#619633]
+
+ * SoupSession now limits the number of times a message can be
+ redirected, to avoid infinite loops [#604383, José Millán
+ Soto]
+
+ * Fixed handling of certain messages where the response
+ headers included "Connection: close" but the server did not
+ actually close the connection at the end. [#611481]
+
+ * Fixed some incorrect g-i annotations [#621021]
+
+ * Fixed an out-of-bounds memory access when processing certain
+ Set-Cookie headers [#620288]
+
+ * Improved msg->reason_phrase on network errors [#623274]
+
+ * Fixed gir file disting [#621727, Yaakov Selkowitz]
+
+Changes in libsoup from 2.30.1 to 2.31.2:
+
+ * gobject-introspection has now been merged in. Use
+ --enable-introspection to build. [#576595, Andreas Rottmann]
+ Note that the introspected API is not yet stable, and in
+ particular, there are numerous values annotated as "utf8"
+ which are actually not guaranteed to be utf8. (Eg, most
+ header data.)
+
+ * Added some helper functions for bindings:
+ soup_buffer_new_take(),
+ soup_message_body_append_take(), and
+ soup_buffer_get_data(). [#576595, Andreas Rottmann]
+
+ * Also added properties for several SoupMessage public
+ fields, and getter methods for various boxed types
+ (SoupCookie, SoupDate, SoupURI).
+
+ * Added some additional hash-table annotations.
+ [#619086, Gustavo Noronha Silva]
+
+ * Marked SoupSession abstract. [#617216, Lorenzo Gil, the
+ first bug filed by someone trying to use libsoup via
+ introspection!] Likewise for SoupAuth and SoupAuthDomain.
+
+ * Fixed a problem with SoupSessionAsync that would cause
+ messages to get lost if you aborted a previous message while
+ it was still looking up the hostname. Fixed several other
+ problems that were discovered while adding a regression test
+ for that. [#618641, thanks to Claudio Saavedra for a good
+ test case]
+
+ * Fixed another connecting-to-lame-http-server problem, and a
+ getting-stuck-in-a-loop-reconnecting bug that it revealed.
+ [#615535]
+
+Changes in libsoup from 2.30.0 to 2.30.1:
+
+ * Fix for https through proxies that close the connection when
+ returning a "407 Proxy Authentication Required" response,
+ and add a regression test for that case. [#611663]
+
+ * Fixed multiple forms/multipart-related interoperability
+ problems reported by Egon Andersen:
+
+ * Don't quote the multipart boundary string if it's
+ not needed, since RFC 2616 recommends that you
+ don't, and some servers don't handle quotes there
+ correctly. (Sigh.) [#614176]
+
+ * Don't put an extra blank line before the first
+ multipart part, since it's unnecessary and some
+ servers don't handle a multipart preamble correctly.
+ (Sigh.) [#614183]
+
+ * Don't put Content-Transfer-Encoding headers in the
+ multipart/form-data parts, even though the HTML 4
+ spec says you must, since no other browsers do, and
+ some servers don't handle them correctly. (Sigh.)
+ [#614198]
+
+ * Changed SoupCookieJarSqlite to actually erase deleted
+ cookies from the database. [#615711, Lukasz Slachciak]
+
+ * Fixed SoupLogger to be more robust against getting passed
+ bad data by the session. [#611663]
+
+ * Fixed SoupAuthDomain to ignore paths when doing proxy auth
+
+ * Fixed a g_warning when hovering over a javascript link in
+ WebKit. [#613442, Xan Lopez]
+
+Changes in libsoup from 2.29.91 to 2.30.0:
+
+ * Fixed a crash in the whitespace-stripping code in
+ soup_uri_new() [#612644, "arnaud.lb"]
+
+ * Update content-sniffing algorithm to match Chrome and the
+ soon-to-be-updated sniffing spec. [#611502, Gustavo Noronha
+ Silva]
+
+ * We now handle "Content-Encoding: x-gzip" as well as "gzip"
+ (even though "x-gzip" has been deprecated for more than 10
+ years). [#611476]
+
+ * Fixed leaks found by valgrind
+
+ * Make the "make check" programs only bind to 127.0.0.1, not
+ any public network interfaces. [#609489, Saleem Absulrasool]
+
+ * Add a test to sniffing-test to make sure that Content-Type
+ parameters are preserved correctly. [Gustavo Noronha Silva]
+
+Changes in libsoup from 2.29.90 to 2.29.91:
+
+ * Added SOUP_SESSION_SSL_STRICT and
+ SOUP_MESSAGE_CERTIFICATE_TRUSTED, to allow callers to
+ determine if an https response comes from a server with a
+ recognized/valid or unrecognized/invalid certificate.
+ [#610374, Gustavo Noronha Silva]
+
+ * Fixed handling of certain badly-formatted URIs [#590524]
+
+Changes in libsoup from 2.29.6 to 2.29.90:
+
+ * Added soup_cookie_jar_set_accept_policy() and related API
+ for implementing cookie acceptance policies. [#608353, Xan
+ Lopez]
+
+ * Fixed the "request-read" signal in SoupServer to actually be
+ emitted.
+
+Changes in libsoup from 2.29.5 to 2.29.6:
+
+ * Fixed SoupContentDecoder to ignore trailing junk after the
+ encoded message body (as other browsers do), rather than
+ getting stuck in an infinite loop. [#606352]
+
+ * Fixed an invalid read in soup_cookie_applies_to_uri()
+ [#607024, pointed out by Xan]
+
+ * Fixed linking on OS X [#606959]
+
+ * Removed a harmless warning in SoupServer. [#606645]
+
+Changes in libsoup from 2.29.3 to 2.29.5:
+
+ * Added SoupContentDecoder, providing support for
+ Content-Encoding: gzip for WebKitGTK. [#522772]
+
+ * Added "accept-language" and "accept-language-auto"
+ properties to SoupSession, to support the Accept-Language
+ header. [#597004, Mario Sanchez Prada]
+
+ * Fixed a bug in SoupPasswordManagerGNOME that could cause
+ crashes if you typed the wrong password once and then tried
+ again. [#595554, debugged by Gustavo Noronha Silva]
+
+ * Fixed a crash in SoupAuthDigest if the server claims support
+ for both qop=auth and qop=auth-int. (This was not noticed
+ sooner because no one actually supports qop=auth-int, and
+ the server in question here was probably confused. :)
+
+ * Updated cookie parsing/output to more closely match
+ draft-ietf-httpstate-cookie-00. [Also fixes #603496 (WebKit
+ unit test), and #604794 (hang parsing malformed Set-Cookie
+ header)]
+
+ * Fixed https-via-proxy to not hang if there is an error
+ communicating with the proxy immediately after the TLS
+ negotiation. [#587528]
+
+ * Fixed a bug that broke gobject-introspection's introspection
+ of libsoup. [#603696, Vincent Untz]
+
+ * Handle spurious CR/LFs between responses. [#602863,
+ Alexander V. Butenko]
+
+ * Fixed soup-message-client-io to not erroneously include URI
+ fragments on the Request-Line when sending via a proxy.
+ [Related to WebKit bug #28687]
+
+ * Fixed Digest authentication against certain (buggy?)
+ clients/servers that require you to use quotes in exactly the
+ same places where the spec uses them. [#582219]
+
+ * Fix ugly gtype-related hack to work with the latest unstable
+ glib. [Benjamin Otte]
+
+Changes in libsoup from 2.28.1 to 2.29.3:
+
+ * Fixed a crash in SoupCookieJarSqlite when using cookie
+ databases not created by libsoup (eg, upgraded epiphany
+ installations). [Patch from Emilio Pozuelo Monfort]
+
+ * Fixed SoupCookieJar to handle non-http URIs properly (so
+ that, eg, JavaScript bookmarklets that try to set/read
+ cookies won't cause crashes). [#602498]
+
+ * HEAD requests that receive a "303 See Other" response will
+ now do a HEAD, not a GET, on the redirected-to resource.
+ Fixes gvfs access to some sites, including certain
+ youtube.com URIs. [#600830]
+
+ * Fixed a g_warning that would always trigger in the
+ server-side SoupCookie code. [#602389]
+
+ * Fixed the server-side SoupMultipart code to be able to parse
+ multiparts containing binary attachments, rather than
+ rejecting them as malformed. [#601640]
+
+ * Fixed the Request-Line format in the https-over-proxy case.
+ Among other things, this fixes access to bugzilla.gnome.org
+ from WebKitGTK-based browsers. [#598277, #600826]
+
+ * Fixed a leak in SoupSession if a message was cancelled while
+ the initial socket connection was in progress. [#596074,
+ debugged by Arnout Vandecappelle]
+
+ * Fixed server-side parsing of Digest auth. [#602898, Chris
+ Head]
+
+ * Fixed WinSock initialization on Windows. [#600689, Tor
+ Lillqvist]
+
+ * Fixed a sporadic crash in the SSL code on Windows. [#600748,
+ Tor Lillqvist]
+
+ * Fixed handling of https connections with timeouts on
+ Windows. [#600749, Tor Lillqvist]
+
+ * Added soup_session_prepare_for_uri(), to allow DNS
+ prefetching for faster browsing. [#598948, José Millán Soto]
+
+ * SoupSession now avoids redundant DNS lookups again when
+ first connecting to a new site, resulting in (probably
+ imperceptibly) faster loads.
+
+ * Added some debugging APIs to SoupConnection and SoupSession
+ for use by, eg, epiphany's soup-fly extension. [#589163,
+ José Millán Soto]
+
+Changes in libsoup from 2.28.0 to 2.28.1:
+
+ * libsoup will now attempt to make multiple connections to a
+ server at once when there are multiple messages queued to
+ that server. The previous behavior (only allowing a single
+ pending connection to each server) resulted in slow load
+ times on pages with lots of subresources (images, css, js,
+ etc) on servers that disallow persistent connections.
+ [#594768]
+
+ * There should now be fewer (no?) "Connection terminated
+ unexpectedly" errors in WebKitGTK.
+
+ * Fixed a crash in SoupCookieJarSqlite [#596859, patch from
+ Alexander Sack].
+
+ * Fixed soup_address_get_physical() and address-to-name
+ resolution of SoupAddress [patch from Enrico Tröger].
+
+ * Fixed a bug in SoupContentSniffer that could cause false
+ negatives [#597545, patch from Alejandro Castro].
+
+ * Fixed the configure error if you have gnutls-devel but not
+ gcrypt-devel installed [#587709].
+
+Changes in libsoup from 2.27.92 to 2.28.0:
+
+ * Fixed a handful of leaks found with valgrind, including a
+ large one in SoupContentSniffer [WebKit bug 28148].
+
+ * Changed the behavior of SoupCookieJarSqlite to improve
+ performance. [#584522, patch from Gustavo Noronha Silva]
+
+ * Fixed a crash in SoupSocket that affected gupnp. [#594951,
+ patch from Olivier Crête]
+
+ * Fixed the type of the SOUP_METHOD_* and SOUP_URI_SCHEME_*
+ macros to be const char * rather than gpointer. [#594508]
+
+Changes in libsoup from 2.27.91 to 2.27.92:
+
+ * Removed SoupPasswordManager from the public API until its
+ problems can be addressed. Although it is still present, you
+ need to #define a special symbol for it to be visible in the
+ header files; see #594377 for details.
+
+ * Fixed a bug where empty query components were dropped from
+ URIs. [#594405]
+
+ * Fixed "make check" to work (but warn) when building with
+ --disable-ssl.
+
+ * Fixed some small documentation bugs pointed out by Dominik
+ Bylica and Lucian Langa.
+
+Changes in libsoup from 2.27.90 to 2.27.91:
+
+ * Added SoupPasswordManager, an interface for managing
+ persistent password storage, and SoupPasswordManagerGNOME
+ (in libsoup-gnome), which implements it using gnome-keyring.
+ tests/get.c provides a minimal example of how to use it.
+
+ * libsoup should now notice when the server closes a
+ persistent connection, and close its side of the connection
+ sooner. This should hopefully fix the spurious "Connection
+ terminated unexpectedly" errors in WebKitGTK. [#578990]
+
+ * Fixed some problems with connection management in
+ SoupSession that could cause a session to eventually "stall"
+ and be unable to process new requests. [#592084]
+
+ * Fixed an infinite loop that caused 100% CPU usage if the
+ network went down at exactly the right time while there were
+ unsent messages in the queue. [#592492]
+
+ * Fixed a crash in SoupLogger. [#591857]
+
+ * Fixed the definition of soup_message_is_keepalive() for
+ HTTP/1.0 messages, to fix a problem introduced in 2.27.90
+ where some messages would load completely but never emit
+ "finished".
+
+ * Fixed a crash in SoupServer introduced in 2.27.90 when
+ processing a request with no "Host" header.
+
+Changes in libsoup from 2.27.5 to 2.27.90:
+
+ * libsoup now uses glib's GResolver rather than its own DNS
+ code. For 2.27.90, the only visible change should be that
+ internationalized domain names are now supported. [#548287]
+
+ * Added soup_message_disable_feature(), which allows you to
+ disable particular features (eg, cookies, proxy,
+ content-sniffing, etc) on a per-message basis. [#574773]
+
+ * It is now possible to implement "OPTIONS *" in a SoupServer;
+ you must explicitly register a handler for "*" in order to
+ do this. [#590751]
+
+ * Ignore Content-Length on EOF-terminated responses, to match
+ other browsers and therefore cope with broken servers that
+ send the wrong length. [Patch from Benjamin Otte.]
+
+ * Fixed the status code when trying to fetch an https URI with
+ a non-gnutls build of libsoup. [#590464]
+
+ * Fixed strict-aliasing warnings introduced in 2.27.4
+ [#588771]
+
+ * Fixed some warnings noted by fortify [#591226] and -Wextra.
+
+ * libsoup now uses automake 1.11's silent-rules support by
+ default (if you are building with automake 1.11). Use
+ "./configure --disable-silent-rules" or "make V=1" to
+ disable.
+
+Changes in libsoup from 2.27.4 to 2.27.5:
+
+ * Fixed a crash when a web server redirected a request to a
+ non-http URI (eg, "about:blank"). [#528882]
+
+ * Fixed a hang when trying to create an attachment on certain
+ bugzilla installations from epiphany. [#584645]
+
+ * Fixed verification of V1 TLS certificates [#589323, Patrick
+ Ohly]
+
+ * Fixed compile problems on Windows (in the ssl code), and on
+ Linux (when the most recent version of gtk-doc was
+ installed).
+
+Changes in libsoup from 2.27.2 to 2.27.4:
+
+ * Added SoupContentSniffer and the "content-sniffed" signal on
+ SoupMessage, to do Content-Type sniffing per the HTML5 /
+ draft-abarth-mime-sniff algorithm. [#572589, Gustavo Noronha
+ Silva]
+
+ * Updated the earlier SoupSession timeout fixes ([#574414],
+ [#578928]) so that async connect() also times out [#588177,
+ Mark Nauwelaerts] and SSL works on Windows again [#587910,
+ Fridrich Strba].
+
+ * Fixed the behavior on a 301 response to a POST to match
+ real-world usage rather than what the spec says. (We were
+ doing the right thing on 302 and 303, but had missed 301.)
+ [#586692]
+
+ * Changed configure so that if GNUTLS isn't found then it
+ errors out, rather than silently building an SSL-less
+ libsoup. Configure with --disable-ssl if you actually don't
+ want SSL. [#584955]
+
+Changes in libsoup from 2.27.1 to 2.27.2:
+
+ * Replaced SoupProxyResolver with SoupProxyURIResolver, which
+ is a bit simpler, works with non-HTTP URIs (and so could be
+ used by gvfsd-ftp) and supports proxy auth correctly.
+ [#580051]
+
+ * Fixed SoupSession to not try to resolve http server
+ hostnames when it's just going to pass the hostname off to a
+ proxy server anyway. This fixes things on hosts that use a
+ proxy for everything and have no working DNS config
+ [#577532] and also makes WebKitGTK behave more like other
+ browsers in terms of per-host connection limits (we now
+ limit connections based on hostname rather than on IP
+ address).
+
+ We also no longer set the AI_CANONNAME flag when calling
+ getaddrinfo(), which saves us a little bit of unnecessary
+ network traffic. [Pointed out by Christophe Gillette on the
+ mailing list.]
+
+ * libsoup now always uses SSL 3.0 (not TLS 1.0 or 1.1) for
+ https URIs, to work around problems with older servers that
+ don't implement the (apparently quite confusing) TLS/SSL
+ compatibility rules correctly. Makes a bunch of
+ previously-inaccessible sites now accessible in WebKitGTK
+ (notably PayPal) [#581342]. Will eventually be revisited, to
+ first try TLS 1.1 and fall back if that fails.
+
+ * Fixed Digest auth to (recent) Apple CalDAV servers.
+ [#583091]
+
+ * Changed the way the SoupSession "authenticate" signal works
+ a bit. We now never emit "authenticate" before sending a
+ request, even if we know for sure that it's going to fail,
+ because this makes the semantics of the authenticate handler
+ too complicated (and because we'll only get into this
+ situation if a previous call to the authenticate handler
+ failed anyway). Fixes problems in WebKitGTK when you cancel
+ a password dialog, and then later try to load the page
+ again. [#583462, mostly figured out by Gustavo Noronha
+ Silva].
+
+ * Fixed a bug in the CRLF-vs-LF patch (#571283) that caused
+ libsoup to fail to parse the response headers (returning
+ SOUP_STATUS_MALFORMED) if a CR LF got split across two
+ read()s. [#582002]
+
+ * Allow using PUT in soup_form_request_for_data(), to work
+ with certain broken web APIs. [#581860, Ross Burton]. Also,
+ fixed a problem with empty POST bodies that made some parts
+ of gmail not work in WebKitGTK.
+
+ * Applied some minor bugfixes to configure.in and autogen.sh
+ [#583911, #583942]. Fixed configure.in to not use gcc
+ warning options that the installed version of gcc doesn't
+ recognize [#578851].
+
+ * Added G_GNUC_NULL_TERMINATED and G_GNUC_PRINTF to a few
+ methods that should have had them. [#581754, Ross Burton]
+
+Changes in libsoup from 2.26.1 to 2.27.1:
+
+ * SOUP_SESSION_TIMEOUT now works properly with
+ SoupSessionAsync [#574414] and SSL [#578928]. Added
+ tests/timeout-test to test this.
+
+ * SoupDate fixes:
+
+ * soup_date_to_string() now handles SOUP_DATE_RFC2822
+ [#579055, Enrico Tröger]
+
+ * soup_date_new_from_string() now accepts 24:00 as a
+ time in ISO8601 timestamps
+
+ * soup_date_to_string() now coerces the date to UTC
+ for HTTP and cookie dates, and outputs the UTC
+ correct offset for the other date types.
+
+ * Added regression tests to tests/date
+
+ * soup_headers_parse() now completely ignores
+ syntactically-incorrect headers, rather than passing them to
+ soup_message_headers_append() and causing a g_warning.
+ soup_message_headers_append() now also rejects 0-length
+ header names. Updated tests/header-parsing to check this.
+ [#579318]
+
+ * Fix a crash when cancelling a message from a "restarted"
+ handler, and updated a regression test to notice the
+ underlying cause. [#580193]
+
+ * Completing the API updates for #576760 from 2.26.1,
+ soup_message_headers_get() is now marked deprecated in favor
+ of soup_message_headers_get_one() and _get_list().
+
+Changes in libsoup from 2.26.0 to 2.26.1:
+
+ * libsoup uses libproxy for PAC and WPAD proxy resolution
+ again. However, it arranges to do all communication with
+ GConf itself, to ensure that libproxy doesn't call it in
+ non-thread-safe ways. [#571527] Also, fixed a bug in
+ SoupSessionSync when proxy resolution failed. [#574957,
+ patch from Milan Crha].
+
+ (Also fixed three SoupProxyResolverGNOME bugs since the
+ 2.26.0.9 preview release. [#578746, #578809])
+
+ * SoupURI now handles unencoded spaces in URIs. In particular,
+ redirects via Location headers with spaces in them now work.
+ [#566530]
+
+ * libsoup can now deal with servers (and clients) that
+ erroneously use LF LF instead of CR LF CR LF to separate
+ the headers and body. [#571283]
+
+ * Added soup_message_headers_get_one() and
+ soup_message_headers_get_list(), which will eventually
+ deprecate soup_message_headers_get(). This lets applications
+ deal correctly with implementations that erroneously send
+ multiple copies of single-valued headers. [#576760]
+
+ * In particular, soup_message_headers_get_content_type() now
+ ignores duplicate Content-Type headers [#576760] and also
+ ignores syntactically-incorrect Content-Type headers.
+ [#577630]
+
+ * SoupCookieJar can now store multiple cookies with the same
+ domain and name, but different paths. [#577360]
+
+ * Abnormal SSL connection closes are now treated as ordinary
+ EOFs, for compatibility with certain sites. [#577386]
+
+ * soup_header_g_string_append_param() now allows NULL values.
+ [#577728]
+
+ * soup_message_headers_append() now rejects header names and
+ values with newlines or certain other illegal data in them,
+ rather than generating syntactically invalid headers.
+
+ * Fixed a small bug in soup_date_new_from_string's ISO 8601
+ handling [qv #578369 for g_time_val_from_iso8601].
+
+ * The regression tests now work correctly on machines where
+ "localhost" resolves to "::1" instead of "127.0.0.1".
+ [#576583, patch from Andreas Rottmann]
+
+ * Fixed warnings when a message has a network problem when
+ many other messages are queued. [#578809]
+
+ * Miscellaneous documentation fixes/clarifications.
+
+Changes in libsoup from 2.25.91 to 2.26.0:
+
+ * Temporarily disable libproxy support to work around a bug in
+ its gnome plugin that causes gvfsd-http (and probably
+ eventually other apps) to crash. [#571527]. For now,
+ SoupProxyResolverGNOME uses only GConf. To be fixed in
+ 2.26.1
+
+ * Fixed a bug that showed up in WebKit, where if many messages
+ were queued all at once to a server that doesn't support
+ persistent connections, some of the requests will get lost.
+ #574365, reported by Xan Lopez.
+
+ * Fixed SoupServer to support using SOUP_ENCODING_EOF, so you
+ can stream responses of unknown length to HTTP/1.0 clients.
+ [#572153]. Added a regression test for this, and for chunked
+ and Content-Length-based streaming.
+
+ * Fixed several bugs that prevented SoupCookieJarSqlite from
+ working. [#572409, patch from Xan Lopez]
+
+ * Added G_{BEGIN,END}_DECLS guards to public headers that were
+ missing it. (Xan Lopez)
+
+ * Misc gtk-doc improvements
+
+Changes in libsoup from 2.25.5 to 2.25.91:
+
+ * Fixed a crash in SoupProxyResolverGNOME when the proxy
+ requires authentication. (This does not make proxy
+ authentication *work* yet, it just makes it not crash.)
+
+ * Updated documentation
+
+Changes in libsoup from 2.25.4 to 2.25.5:
+
+ * SoupProxyResolverGConf (which was incomplete) is gone, and
+ libsoup-gnome now requires libproxy, which is now officially
+ an external dependency of GNOME.
+
+ * Fixed a bug in SoupCookieJar that was making it send
+ "Cookie: (null)" when it had no cookies for a site, which
+ confused some web servers (WebKit bug 23240).
+
+ * Fixed a bug with using SOUP_MEMORY_TEMPORARY buffers and
+ soup_message_body_set_accumulate(FALSE). (Part of WebKit bug
+ 18343, noticed by Gustavo Noronha Silva.)
+
+ * Fixed the build with non-gcc compilers
+
+Changes in libsoup from 2.25.3 to 2.25.4:
+
+ * Added soup_session_get_feature() and
+ soup_session_get_features(), to query the features currently
+ available in a session (which is needed by the patch in
+ https://bugs.webkit.org/show_bug.cgi?id=22624)
+
+Changes in libsoup from 2.25.2 to 2.25.3:
+
+ * Fixed a crash when using both cookies and a proxy. [#562191,
+ Mark Lee]
+
+ * Fixed soup_form_decode() to correctly handle forms with
+ URI-encoded parameter names [#563302, Evan Nemerson] and
+ added a regression test.
+
+ * Fixed a crash in SoupProxyResolverGConf. [#563145]
+
+Changes in libsoup from 2.25.1 to 2.25.2:
+
+ * Fixed client behavior when presented with multiple auth
+ types to choose the *strongest* auth type (eg, Digest)
+ rather than the *weakest* one [#562339, Pontus Oldberg].
+ Added a regression test for this.
+
+ * Moved libsoup-gnome headers to a different directory to make
+ it easier to split libsoup and libsoup-gnome into separate
+ packages, and to ensure that things that only want to be
+ looking at plain libsoup headers (like gir-repository) don't
+ accidentally see the libsoup-gnome ones.
+
+ * Some minor doc fixes
+
+ * Fixed libsoup-gnome linking with --as-needed. [#559342]
+
+Changes in libsoup from 2.24.1 to 2.25.1:
+
+ libsoup 2.25.1 introduces a new library, libsoup-gnome, which
+ will be used for features which are important to GNOME apps,
+ but which require GNOME-specific libraries that non-GNOME apps
+ may not want to add dependencies on.
+
+ In 2.25.1, libsoup-gnome contains:
+
+ * SOUP_TYPE_PROXY_RESOLVER_GNOME, a SoupSessionFeature
+ type that can be added to a SoupSession to provide
+ automatic proxy handling via the GConf proxy keys. (See
+ below) The default implementation uses libproxy, which
+ also handles WPAD, PAC, etc, but if libproxy is not
+ available it will use GConf directly, supporting only
+ the basic HTTP proxy functionality.
+
+ * SoupCookieJarSqlite, a SoupSessionFeature that handles
+ cookies and stores them in a Firefox 3-compatible sqlite
+ file. (This is not actually a "GNOME-specific" feature,
+ but I didn't want to make libsoup itself depend on
+ sqlite, and I didn't want to make the dependency
+ optional. This might change before 2.26.)
+
+ * SOUP_TYPE_GNOME_FEATURES_2_26: a SoupSessionFeature type
+ that can be added to a SoupSession to add all
+ GNOME-integration features that are available for 2.26;
+ as of 2.25.1, this is just the GNOME proxy resolver, but
+ by 2.26.0 it may also include gnome-keyring support and
+ possibly other features.
+
+ Applications/libraries that are currently doing GConf proxy
+ lookup by hand can be updated as follows:
+
+ * Remove all of the existing code that listens to the
+ GConf keys and sets SOUP_SESSION_PROXY_URI
+
+ * Change the configure check to require
+ "libsoup-gnome-2.4 >= 2.25.1" instead of "libsoup-2.4"
+
+ * #include <libsoup/soup-gnome.h>
+
+ * After creating your SoupSession, do:
+
+ soup_session_add_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_GNOME);
+
+ (Or alternatively, use SOUP_SESSION_ADD_FEATURE_BY_TYPE
+ with soup_session_async_new_with_options() or
+ soup_session_sync_new_with_options().)
+
+
+ Other new features and bug fixes in 2.25.1 include:
+
+ * SoupCookieJarText, like SoupCookieJarSqlite, but using the
+ old-style cookies.txt format, and in the base libsoup rather
+ than libsoup-gnome.
+
+ * Various bugfixes to SoupCookie and SoupCookieJar to fix the
+ problems with cookies not working on certain sites.
+
+ * The new SoupMultipart type provides support for multipart
+ MIME bodies, and soup-form now includes several methods for
+ generating and parsing multipart form data and file uploads.
+
+ * SoupMessageHeaders now has methods for easy handling of the
+ Content-Type, Content-Disposition, Range, and Content-Range
+ headers. The Content-Disposition handling recognizes
+ RFC2231-encoded UTF-8 filenames.
+
+ * SoupServer now automatically handles partial GET requests;
+ if your server returns SOUP_STATUS_OK in response to a
+ partial GET, libsoup will automatically convert it to a
+ SOUP_STATUS_PARTIAL_CONTENT response with only the requested
+ portions.
+
+ Thanks to Xan Lopez and Diego Escalante Urrelo for their work
+ on SoupCookie, SoupCookieJar, SoupCookieJarText, and
+ SoupCookieJarSqlite.
+
+Changes in libsoup from 2.24.0.1 to 2.24.1:
+
+ * Fixed a crash when unreffing the session from a
+ soup_session_queue_message() callback [#533473], and added
+ regression test. In particular, this fixes a crash in
+ seahorse when trying to connect to a non-responsive
+ keyserver.
+
+ * Fixed an infinite loop when giving a bad password to a site
+ that uses non-standard capitalization in the
+ WWW-Authenticate header (eg, gmail.com) [#536285].
+
+ * Fixed a leak in SoupSessionAsync when using a non-default
+ GMainContext. [addendum to #498509, Arnout Vandecappelle]
+ Added additional code to the regression tests to make sure
+ sessions and servers do not get leaked.
+
+ * Fixed a leak in the XML-RPC code
+
+ * Compile fixes for "gcc -pedantic" [#553976, Sander Dijkhuis]
+ and -DG_DISABLE_DEPRECATED / -DG_DISABLE_SINGLE_INCLUDES
+ [#557072, Cosimo Cecchi]
+
+ * Patched xmlrpc-test to accept the incorrect response to
+ test_echo() that php-xmlrpc gives when it's built against
+ libxml2 >= 2.7.1 (qv http://bugs.php.net/45996), so that
+ I can "make distcheck"...
+
+ * Updated generated documentation
+
+Changes in libsoup from 2.23.92 to 2.24.0.1:
+
+ * Reverted part of the fix for #528882, which caused the DAAP
+ plugin in rhythmbox to crash. [#553466]
+
+Changes in libsoup from 2.23.91 to 2.23.92:
+
+ * Fixed the handling of a 302 response to a HEAD request,
+ which should NOT be treated like a 303 response. [#551190,
+ Jonathan Matthew]
+
+Changes in libsoup from 2.23.6 to 2.23.91:
+
+ * Fixed a crash in gvfs [#528882], though there is still an
+ unknown bug there. As part of this fix, libsoup will now
+ return an error if you try to do an operation on a non-HTTP
+ URI. (Previously it was just treating any URI scheme except
+ "https" as HTTP.)
+
+ * Added soup_date_to_timeval() for gvfs. [#549006, patch from
+ Bastien Nocera]
+
+Changes in libsoup from 2.23.1 to 2.23.6:
+
+ * Fixed use of g_idle_add() so that heavy I/O won't end up
+ blocking libsoup callbacks. [#536676, Benjamin Otte]
+
+ * Allow the caller to override the Host header. [#539803, Marc
+ Maurer]
+
+ * Properly handle responses larger than 4G. [#539861, Peter
+ Christensen]
+
+ * Fixed the build when using certain LDFLAGS [#541506, Götz
+ Waschk]
+
+ * Fixed a small bug in Digest auth handling. [#544681, Mads
+ Chr. Olesen]
+
+ * Fixed multiple Windows bugs [Tor Lillqvist]
+
+Changes in libsoup from 2.4.1 to 2.23.1:
+
+ * This is the first unstable release leading up to GNOME 2.24.
+ Bumped the libsoup version number up to 2.23.x to match the
+ GNOME version; note that the API version is still "2.4",
+ meaning in particular that you still call it "libsoup-2.4"
+ when using pkg-config.
+
+ * Added SoupSessionFeature, an interface type that will be
+ used for several new features. Ported SoupLogger and
+ SoupAuthManager to use it.
+
+ * Added SoupCookie and SoupCookieJar. This API is already
+ being used in Epiphany, via WebKit, but it is not yet
+ complete.
+
+ * Fixed GnuTLS support on Win32. [#528752, Marc Maurer]
+
+Changes in libsoup from 2.4.0 to 2.4.1:
+
+ * Fixed SoupMessage to not downgrade to HTTP/1.0 for the
+ second attempt when it receives an HTTP/1.0 redirect or 401.
+ [#521848, Tommu Komulainen]
+
+ * Fixed Host: header syntax when the host is an IPv6 address
+ literal.
+
+ * Fixed SoupSession to not emit "authenticate" multiple times
+ for messages that have been requeued. [#522601, Tommi
+ Komulainen]. Also added two new signals to SoupSession,
+ request-queued and request-unqueued, to help simplify
+ certain session-helpers and avoid bugs like this in the
+ future.
+
+ * Fixed soup_server_pause_message() to actually work (rather
+ than *un*pausing the message).
+
+ * Added a property SOUP_SESSION_IDLE_TIMEOUT that can be used
+ to set a timeout after which idle connections will
+ automatically be closed. [#518214, Jorn Baayen]
+
+ * Implemented RFC 2069-style Digest auth, and fixed SoupAuth
+ to compare auth scheme names case-insensitively, to fix
+ authentication against Apple's calendar server. [#498484]
+
+ * Fixed a crash in SoupAuthDomainDigest if the client provided
+ an unrecognized username. [pointed out by Curtis Magyar on
+ IRC]
+
+ * Fixed a few SoupDate bugs. (In particular, it was outputting
+ the wrong day of the week when stringifying dates.)
+
+ * Improved the cleanup of idle connections, to fix slow load
+ times with the libsoup backend of WebKit.
+
+ * Added a new SoupMessage signal "wrote-body-data" that can be
+ used for progress information when sending a large request
+ body. Also allow providing the request body in multiple
+ chunks even when using Content-Length encoding. [#525101,
+ Christian Kellner]
+
+ * libsoup now ignores SIGPIPE globally, instead of
+ un-thread-safe-ly ignoring it only around network writes. In
+ particular, this means it is ignored when the SSL code needs
+ to unexpectedly do a write when we asked it to do a read.
+ [#524397, Curtis Magyar]
+
+ * The discard-body-chunks-once-they're-no-longer-needed
+ behavior, confusingly called SOUP_MESSAGE_OVERWRITE_CHUNKS,
+ is now controlled by a SoupMessageBody method
+ (soup_message_body_set_accumulate()), and can be applied to
+ either the request body or the response body.
+ (OVERWRITE_CHUNKS is still available for backward
+ compatibility.) [#522146, Christian Kellner]
+
+ * The DNS cache no longer caches "no such host" results, since
+ some name servers lie to clients outside their firewall,
+ which could then cause problems for laptops moved between
+ networks. [#523269, Jörgen Scheibengruber]
+
+ * Added some new regression tests, fixed some small bugs in
+ the existing ones.
+
+Changes in libsoup from 2.3.4 to 2.4.0:
+
+ * Fixed a small memory leak in SoupSession. [#518798, Wouter
+ Cloetens]
+
+ * Minor fixes to redirect behavior; PROPFINDs can now be
+ automatically redirected (pointed out by Christian Kellner),
+ and 302 is treated like 307, not 303. Also fixed to make
+ sure that redirect-test actually gets run by "make check".
+
+ * The SoupSocket I/O methods now set nread/nwrote even on
+ error. [Benjamin Otte]
+
+Changes in libsoup from 2.3.2 to 2.3.4:
+
+ * The documentation should be accessible from devhelp again
+ [#518384, Mart Raudsepp]. (Also fixed another
+ documentation-generation bug that affected builds from svn,
+ but not the 2.3.2 tarball for some reason. [#518317,
+ Benjamin Otte].)
+
+ * Fixed dependencies in libsoup-2.4.pc file [#517631,
+ Sebastian Dröge]
+
+Changes in libsoup from 2.3.0.1 to 2.3.2:
+
+ API changes / Behavior changes:
+
+ * soup_server_add_auth_domain() now refs the auth domain when
+ adding it. (soup_server_remove_auth_domain() already
+ unreffed it.) This means existing applications using
+ SoupAuthDomain will now have a small memory leak. Those
+ applications should update their libsoup-2.4 requirement to
+ ">= 2.3.2" at some point before the final GNOME 2.22.0
+ release, and then fix the code to unref the auth domain
+ after adding it to the server.
+
+ * SoupSession's automatic redirect-handling behavior now obeys
+ RFC 2616 more closely. In particular, status codes 300 and
+ 304 are no longer mistakenly considered redirects; POSTs
+ that receive 303 are now redirected into GETs; and POSTs
+ that receive 301, 302, or 307 are now not redirected.
+
+ Applications that were using the SOUP_MESSAGE_NO_REDIRECT
+ flag to prevent libsoup from redirecting POSTs incorrectly
+ before should now be able to remove that if they depend on
+ libsoup-2.4 >= 2.3.2.
+
+ API additions:
+
+ * Added a SOUP_SESSION_USER_AGENT property to SoupSession, and
+ SOUP_SERVER_SERVER_HEADER to SoupServer, to support
+ automatically adding "User-Agent" and "Server" headers to
+ messages. (The default behavior is to do nothing, as
+ before.)
+
+ * Added several new methods to soup-forms.h. Applications that
+ are encoding a fixed set of form fields can now just pass
+ them to soup_form_encode(), rather than needing to construct
+ a GHashTable or GData list. (Likewise, the new
+ soup_uri_set_query_from_fields() behaves similarly for
+ directly updating a URI with form data.) There are also now
+ soup_form_request_new() and other related methods, to
+ directly create a GET or POST SoupMessage for submitting a
+ form query.
+
+ The original soup_form_* methods have all been renamed,
+ although #defines exist for backward compatibility.
+
+ * Added soup_message_set_chunk_allocator() and
+ soup_buffer_new_with_owner(), to give applications more
+ control over memory usage/copying when doing streaming HTTP.
+ [Wouter Cloetens, #513810].
+
+ * Added several new methods to soup-value-utils.h for working
+ with multiple array or hash table values at once:
+ soup_value_hash_new_with_vals(),
+ soup_value_hash_insert_vals(),
+ soup_value_hash_lookup_vals(),
+ soup_value_array_new_with_vals(), and
+ soup_value_array_append_vals().
+
+ This helps to simplify XML-RPC calls that send or receive
+ structs or arrays.
+
+ * Added soup_date_to_time_t().
+
+ * Added SoupMessageHeadersIterator, an iterator type for
+ SoupMessageHeaders that can be used instead of
+ soup_message_headers_foreach().
+
+ Bug fixes:
+
+ * Fixed a crash-when-idle in evolution-exchange [#437835] and
+ rhythmbox [#506552].
+
+ * Added the API version to the gtk-doc installation dir, to
+ prevent parallel-installation problems with libsoup 2.2 and
+ 2.4. [#512810, Daniel Gryniewicz].
+
+ * Fixed tests/query-test to compile correctly on Solaris.
+ [#513602, patch from Jeff Cai]
+
+ * Fixed some other minor HTTP conformance issues.
+
+ Python bindings:
+
+ * Although not present in the release tarball, there are now
+ experimental python bindings for libsoup in GNOME subversion
+ (in the python/ subdirectory of libsoup trunk). These are
+ not yet stable (and are not built by default or installed
+ even when building from svn), but comments on them are
+ welcome at libsoup-list@gnome.org
+
+Changes in libsoup from the 2.2 series to 2.3.0.1:
+
+ libsoup 2.3.0 is the first beta release of the libsoup 2.4
+ series. It is an API break from the earlier 2.2 series that
+ fixes various bugs and API warts and lays the groundwork for
+ language bindings and various new features in upcoming
+ releases.
+
+ (2.3.0.1 is identical to the 2.3.0 release in terms of code,
+ but includes this updated NEWS file which was accidentally
+ left out of the 2.3.0 tarball.)
+
+ http://library.gnome.org/devel/libsoup/unstable/libsoup-porting-2.2-2.4.html
+ goes over the API changes in detail. If you have questions not
+ answered by the porting document, please send mail to
+ libsoup-list@gnome.org.
+
+ Specific user-reported bugs fixed in this release:
+
+ * SoupURI now correctly handles URIs with complex encoded
+ queries [#266516, Jean-Yves Lefort]
+
+ * It is now possible for a SoupServer to use Digest auth
+ without needing to have the cleartext password available.
+ [#347108, Anas Nashif]
+
+ * Digest authentication now properly handles "stale=true" and
+ "nextnonce=..." [#471380, Jari Urpalainen]
+
+ * SoupServer is now subclassible [#491653, Mathias Hasselmann]
+
+ * soup_server_run_async and soup_server_quit no longer ref and
+ unref the server, as that doesn't match ordinary GObject
+ conventions [#494128, Mathias Hasselmann]
+
+ * The test programs no longer use a symbol name that conflicts
+ with Cygwin [#501631, Cygwin Ports Maintainer]
+
+ * libsoup can now handle the not-quite-HTTP responses returned
+ by Shoutcast servers [#502325, Wouter Cloetens]
+
+ * If you use libsoup while disconnected from the network, it
+ no longer caches the failed DNS results [#508593, Bradley
+ Worley]
+
+ Items from http://live.gnome.org/LibSoup/ToDo fixed:
+
+ * "Expect: 100-continue" processing now works correctly on
+ both client and server.
+
+ * SoupSessions are no longer leaked
+
+ * The XML-RPC API is improved. The SOAP API is gone...
+
+ * Added utility functions for HTML form handling
+
+ * Improved message header handling
+
+ * SoupServer now automatically adds a "Date" header
+
+==========
+
+Changes in libsoup from 2.2.103 to 2.2.104:
+
+ * soup_message_io_pause() and soup_message_io_pause() are now
+ allowed for client messages (and in particular, they don't
+ mess up when called from the "got_chunk" callback).
+ [#452280, Marco Barisione]
+
+ * Fixed some bugs in SOUP_SESSION_ASYNC_CONTEXT support that
+ would cause parts of an operation to run in the default
+ context rather than the session's context. Also fixed some
+ leaks and added a regression test. [#498509, Wouter
+ Cloetens]
+
+ * There is a new test/sample program, tests/pull-api.c,
+ showing how to implement a pull API using SoupSessionAsync.
+ (This depends on the fixes for #452280 and #498509, so it
+ won't work with older versions of libsoup.)
+
+ * Discovered "valgrind --leak-resolution=med" and fixed some
+ more memory leaks.
+
+Changes in libsoup from 2.2.102 to 2.2.103:
+
+ * Fix memory corruption in SoupSessionAsync that caused
+ rhythmbox to crash. [#484988, patch from Rob Bradford]
+
+ * Fix socket refcounting in SoupServer to fix warnings /
+ possible crash. [#459896, Emanuele Aina]
+
+Changes in libsoup from 2.2.101 to 2.2.102:
+
+ * Unbreak the build when building without SSL. Not that you
+ should be building without SSL anyway, but... (reported by
+ guenther).
+
+Changes in libsoup from 2.2.100 to 2.2.101:
+
+ * Fix build on cygwin [384498]
+
+ * Fix SSL rehandshaking on synchronous sockets [415402, Jacob
+ Berkman] and add a regression test for it.
+
+ * Fix two bugs in https tunnels over proxies that require
+ authentication (noticed by Varadhan), and add a regression
+ test for them.
+
+ * Ensure that if you queue multiple messages at once to an
+ http server that requires authentication but that you
+ haven't authenticated to yet, that all of the messages get
+ properly authenticated [271540, James Willcox]. And add a
+ regression test for it.
+
+ * Fix NTLM authentication, which got broken by the previous
+ fix. [471389, Varadhan]. Add a basic NTLM regression test
+ that doesn't really test the crypto/encoding parts, but at
+ least makes sure that the message flow is correct.
+
+ * Allow trailing whitespace after HTTP version in
+ Response-Line, for compatibility with broken servers
+ [475169, Stephane Loeuillet]. Add that case to the
+ header-parsing regression test.
+
+ * Fix crash when the session's "authenticate" handler returns
+ a username and no password when using NTLM. [480987, Wendell
+ MacKenzie]
+
+ * Use "new" glib base64 and iso8601 methods rather than
+ duplicating them. [337010, patch from Emmanuele Bassi].
+
+ * Implement soup_session_queue_message() for SoupSessionSync.
+
+ * Add G_BEGIN_DECLS / G_END_DECLS to all headers that were
+ missing them. [438776, patch from Jonathon Jongsma].
+
+ * Fix broken definition of SOUP_IS_MESSAGE_FILTER_CLASS. Noted
+ by "cascardo" on libsoup-list.
+
+ * Remove documentation of non-public MD5 methods [440092,
+ Jonathon Jongsma]. Removed a mysterious half-sentence in the
+ SoupMessage docs [458116, Marco Barisione].
+
+Changes in libsoup from 2.2.99 to 2.2.100:
+
+ * Fixed soup_headers_parse_status_line() so WebDAV response
+ parsing will work again. [406997]
+
+ * Fixed a bug in the header-parsing regression test that
+ caused the test to fail sometimes, even though the actual
+ header-parsing code was fine.
+
+Changes in libsoup from 2.2.98 to 2.2.99:
+
+ * Fixed header parsing, including a crash in SoupServer with
+ certain malformed requests [391970].
+
+ * Fixed redirection to other hosts with SoupSessionAsync.
+ [382251]
+
+ * Fixed a small memory leak pointed out by Chris Austin.
+
+Changes in libsoup from 2.2.97 to 2.2.98:
+
+ * The XML-RPC code now correctly interprets <value>foo</value>
+ as meaning the same thing as
+ <value><string>foo</string></value>. [364490] Pointed out by
+ Todd Kulesza.
+
+ * Memory leak fixes from Andrew W. Nosenko.
+
+ * A few symbols that should have been static before now are.
+ [376387] Patch from Matthias Clasen.
+
+Changes in libsoup from 2.2.96 to 2.2.97:
+
+ * Fixed SOAP and XML-RPC code to handle whitespace and
+ comments in the XML better. (Based on a patch from Andrew W.
+ Nosenko.)
+
+ * Fixed lots of typecasting/constness warnings in the code
+ (mostly via a patch from Andrew W. Nosenko)
+
+ * Fixed build on Cygwin [321827]
+
+ * Fixed libsoup-2.2.pc fields [343340] and make it get
+ uninstalled correctly [356809]. (Mikhail Zabaluev and
+ Matthew Barnes)
+
+ * Fixed some small leaks in SoupServer pointed out by Paolo
+ Borelli. [351500]
+
+Changes in libsoup from 2.2.95.1 to 2.2.96:
+
+ * SoupServer now works even if you don't explicitly set an
+ encoding for the response. (In particular, the automatic 404
+ if you request a path with no handlers now works. Problem
+ pointed out by Dennis Jacobfeuerborn.)
+
+ * WWW-Authenticate and Proxy-Authenticate responses with no
+ realm parameter are now ignored, as per RFC 2617, fixing a
+ crash pointed out by Nate Nielsen.
+
+ * Added soup_xmlrpc_message_from_string(), from Fernando
+ Herrera [348532].
+
+ * simple-httpd and "get" now support HEAD
+
+Changes in libsoup from 2.2.94 to 2.2.95.1:
+
+ * Even more fixes to XML-RPC, found by the new XML-RPC
+ regression test. This includes some API changes that I don't
+ feel guilty about, because the code totally didn't work at
+ all before.
+
+ * Fixed a bug in soup_mktime_utc()
+
+ * (2.2.95 was identical to 2.2.95.1. The only difference is
+ that the shared library version was belatedly bumped from
+ 8.2.0 to 8.3.0 to reflect the API "additions")
+
+Changes in libsoup from 2.2.93 to 2.2.94:
+
+ * Various fixes to the XML-RPC code (which apparently had not
+ actually ever worked before) from Brent Smith. [343973,
+ 344222, 344458]
+
+ * Added client and server API tutorials to the docs
+
+ * auth-test now uses a local Apache 2.2 install, if possible,
+ rather than depending on files that used to be on an old
+ Ximian web server but haven't been anywhere for a long time.
+ [311825]
+
+Changes in libsoup from 2.2.92 to 2.2.93:
+
+ * Fixed outgoing data corruption caused when SoupServer
+ started writing out a response a second time after already
+ having started once. [334469]. Also fixed 342640 and another
+ bug caused by the workaround for 334469 in 2.2.92. Based on
+ patches and analysis from William Jon McCann and Armin
+ Bauer.
+
+ * Fixed a deadlock when changing a session's proxy URI.
+ [309867 / bnc 174255, based on a patch by Veerapuram
+ Varadhan].
+
+ * Fixed https-via-proxies in the synchronous case. [bnc 174255]
+
+ * Fixed a crash in evolution-exchange [342545, fix based on an
+ analysis by Wang Xin].
+
+ * Fixed simple-proxy to not crash at startup. Oops. (Alex
+ Larsson)
+
+Changes in libsoup from 2.2.91 to 2.2.92:
+
+ * Fixed server-side digest auth to return a valid "algorithm"
+ value and client-side to not crash if it sees an invalid one
+ [328615].
+
+ * Fixed the Request-Line parsing code to not hardcode a
+ maximum URI length (to allow very long DAAP requests from
+ iTunes in Rhythmbox). [335040]
+
+ * Fixed some warnings (signed/unsigned mismatch).
+
+Changes in libsoup from 2.2.7 to 2.2.91:
+
+ * (The large version number bump is because there was an
+ internal 2.2.90 release for SUSE 10.1 alphas, which was
+ supposed to be intermediate between 2.2.7 and 2.4.0. But
+ 2.4.0 didn't end up happening, and I don't want to regress
+ the version number at this point.)
+
+ * SoupSession, SoupServer, SoupConnection, SoupSocket, and
+ SoupAddress now have an "async-context" property that allows
+ you to use the async API in a non-default GMainContext.
+ [Based on patches from Armin Bauer and Jürg Billeter.]
+
+ * SoupSession, SoupConnection, and SoupSocket now have a
+ "timeout" property to stop synchronous sockets from hanging
+ forever if the remote end is unresponsive (from Varadhan).
+
+ * Fixed some bugs in soup_date_iso8601_parse(). [324671, from
+ Emmanuele Bassi]
+
+ * More Windows build fixes from Tor.
+
+Changes in libsoup from 2.2.6.1 to 2.2.7:
+
+ * Fixed a crash when using NTLM connections [316313, probably
+ also 318252]. (Also 321208, which was a bug introduced in
+ the original fix for 316313.)
+
+ * Fixed a bug that could cause soup to suck up all available
+ CPU when a connection to a SoupServer was dropped by the
+ other side [319305, patch from Jonathan Matthew]
+
+ * Fixed the creation of struct elements in XMLRPC messages
+ [321362, patch from Sebastian Bauer]
+
+ * Plugged a small memory leak in SoupSocket (from Wang Xin).
+
+ * Fixed two compile problems, a gccism [320349, patch from
+ Roland Illig], and a strict-aliasing warning from gcc 4.1.
+
+Changes in libsoup from 2.2.6 to 2.2.6.1:
+
+ * Fixed a crash when using SoupSoapMessage
+
+Changes from 2.2.5 to 2.2.6:
+
+ * Fixed a crash when canceling a message (from Tambet Ingo)
+
+ * Fixed a bug where a connection could be leaked forever in
+ some circumstances if a request got a 30x, 401, or 407
+ response, eventually causing a hang when the session hit its
+ maximum connection limit. (Dan/Tambet)
+
+ * Fixed a memory leak. (Tambet)
+
+ * Fixed a bug that would sometimes show up when connecting to
+ a server on localhost [#312540]
+
+ * Added some API to SoupServer and SoupSocket to help fix a
+ long-standing rcd bug.
+
+Changes from 2.2.4 to 2.2.5:
+
+ * Win32 support (from Tor Lillqvist)
+
+ * Up-to-date API documentation pretty much everywhere
+
+ * Basic XMLRPC support (from Mariano Suarez-Alvarez, Fernando
+ Herrera, and Jeff Bailey)
+
+ * New HTTP timestamp-manipulation methods soup_date_parse,
+ soup_date_generate, and soup_date_iso8601_parse.
+
+ * SoupSession now handles relative URLs in the Location header
+ (in violation of RFC 2616, but in line with how some servers
+ behave.) [270688]
+
+Changes from 2.2.3 to 2.2.4:
+
+ * Fixed a problem with NTLM authentication against
+ multi-domain servers. [306877]
+
+ * Fixed DNS lookups on Solaris. [254551, 268389]
+
+Changes from 2.2.2 to 2.2.3:
+
+ * Now compiles against gnutls 1.2.0 [257811]
+
+ * Fixed a bug that could result in 100% CPU usage if an SSL
+ server closed the connection uncleanly. [273352]
+
+Changes from 2.2.1 to 2.2.2:
+
+ * The SSL validation fix from 2.2.1 [264414] is now completely
+ fixed. (Part of the fix didn't actually make it into 2.2.1)
+
+ * HTTPS certificate validation now works when using an HTTP
+ proxy. [268583]
+
+ * HTTP proxy code deals better with proxies that try to make
+ the user do HTML-form-based authentication. [268531]
+
+ * 64-bit fixes for NTLM auth code. [270323, from Michael
+ Zucchi]
+
+Changes from 2.2.0 to 2.2.1:
+
+ * Updated for a libgcrypt API change between 1.1.9x and 1.2.x
+ that caused a crash at runtime if you compiled against
+ 1.2.x. [266342]
+
+ * SSL certificate validation failure should now always result
+ in a status of SOUP_STATUS_SSL_FAILED, rather than getting
+ turned into SOUP_STATUS_IO_ERROR. [264414]
+
+
+Changes in libsoup from the 2.0 series (1.99.x versions) to 2.2:
+
+ * Most of the libsoup datatypes are now GObjects. (SoupUri
+ is currently an exception to this.)
+
+ * SoupMessage now emits signals at various stages of
+ processing. (Eg, "wrote_body", "got_headers".) (You
+ can also still use soup_message_add_*handler().)
+
+ * SoupContexts are gone; soup_message_new() now takes a URI
+ string.
+
+ * All formerly global state is now maintained by the
+ SoupSession object. (This includes the connection pool,
+ proxy server, cached authentication information, SSL
+ certificates, etc.)
+
+ * You can create a SoupSessionAsync (for 2.0-like
+ behavior) or SoupSessionSync (for blocking,
+ synchronous usage).
+
+ * You can add SoupMessageFilter objects to a session
+ to have certain processing automatically performed
+ on every message sent via that session. (Eg, setting
+ up handlers.)
+
+ * NTLM authentication is no longer supported by
+ default. You must enable it by setting the
+ SOUP_SESSION_USE_NTLM flag on the session.
+
+ * The preferred method of handling authentication is
+ now via the "authenticate" and "reauthenticate"
+ signals on SoupSession. (The old style, of encoding
+ the user and password information into the url is
+ also still supported.)
+
+ * The SOUP_ERROR_* values are now SOUP_STATUS_* (so that we
+ don't have "SOUP_ERROR_OK" and the like).
+
+ * SOUP_MESSAGE_IS_ERROR() is gone, since some cases
+ want to include 3xx responses and some don't.
+
+ * SOUP_ERROR_CANT_AUTHENTICATE and
+ SOUP_ERROR_CANT_AUTHENTICATE_PROXY are now gone,
+ since they didn't carry any information that
+ SOUP_STATUS_UNAUTHORIZED and
+ SOUP_STATUS_PROXY_UNAUTHORIZED don't.
+
+ * DNS errors now show up as the new status code
+ SOUP_STATUS_CANT_RESOLVE rather than being mixed in
+ with SOUP_ERROR_CANT_CONNECT.
+
+ * Minimal SOAP support has been added back, via
+ SoupSoapMessage/SoupSoapResponse
+
+ * The HTTP I/O state machine was completely rewritten, fixing
+ numerous crashes, leaks, and protocol errors.
+
+ * SoupUri now conforms to RFC 2396. Mostly.
+
+ * Various test programs have been added under tests/
+
+ * Removed:
+
+ * Support for OpenSSL (which was horribly buggy) and
+ Mozilla NSS (which was never finished). We only
+ support GNUTLS for SSL now.
+
+ * SOCKS support
+
+ * CGI support in SoupServer
--- /dev/null
+libsoup is an HTTP client/server library for GNOME. It uses GObjects
+and the glib main loop, to integrate well with GNOME applications.
+
+Features:
+ * Both asynchronous (GMainLoop and callback-based) and synchronous APIs
+ * Automatically caches connections
+ * SSL support
+ * Proxy support, including authentication and SSL tunneling
+ * Client support for Digest, NTLM, and Basic authentication
+ * Server support for Digest and Basic authentication
+ * XML-RPC support
+
+See the documentation in docs/reference/ and the test programs in
+tests/ for simple examples of how to use the code. The
+evolution-data-server and evolution-exchange modules in GNOME CVS have
+some more complicated examples.
+
+There is a mailing list for libsoup-related questions/discussions at
+gnome.org. Visit http://mail.gnome.org/mailman/listinfo/libsoup-list
+to subscribe or read the archives.
+
+Bugs against libsoup can be filed at the GNOME bugzilla:
+http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup
+
+More information (including the libsoup To Do list) is available at
+http://live.gnome.org/LibSoup
+
+Licensing:
+libsoup is licensed under the LGPL, see COPYING for more details.
--- /dev/null
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+REQUIRED_AUTOMAKE_VERSION=1.9
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+PKG_NAME="libsoup"
+
+(test -f $srcdir/configure.ac \
+ && test -f $srcdir/libsoup.doap \
+ && test -d $srcdir/libsoup) || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level $PKG_NAME directory"
+ exit 1
+}
+
+which gnome-autogen.sh || {
+ echo "You need to install gnome-common from the GNOME CVS"
+ exit 1
+}
+USE_GNOME2_MACROS=1 . gnome-autogen.sh
--- /dev/null
+dnl *******************************************
+dnl *** Initialize automake and set version ***
+dnl *******************************************
+
+AC_PREREQ(2.63)
+AC_INIT([libsoup],[2.35.90],[http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup])
+AC_CONFIG_SRCDIR([libsoup-2.4.pc.in])
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE([foreign 1.9])
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
+
+AC_PROG_MAKE_SET
+
+SOUP_API_VERSION=2.4
+AC_SUBST(SOUP_API_VERSION)
+
+# Increment on interface addition. Reset on removal.
+SOUP_AGE=4
+
+# Increment on interface add, remove, or change.
+SOUP_CURRENT=5
+
+# Increment on source change. Reset when CURRENT changes.
+SOUP_REVISION=0
+
+AC_SUBST(SOUP_CURRENT)
+AC_SUBST(SOUP_REVISION)
+AC_SUBST(SOUP_AGE)
+
+dnl ***************************
+dnl *** Set debugging flags ***
+dnl ***************************
+
+debug_default=minimum
+
+# Declare --enable-* args and collect ac_help strings
+AC_ARG_ENABLE(debug,
+ AS_HELP_STRING([--enable-debug=[no/minimum/yes]],
+ [turn on debugging (default=$debug_default)]),,
+ enable_debug=$debug_default)
+
+# Set the debug flags
+if test "x$enable_debug" = "xyes"; then
+ test "$cflags_set" = set || CFLAGS="$CFLAGS -g"
+ SOUP_DEBUG_FLAGS="-DG_ENABLE_DEBUG"
+else
+ if test "x$enable_debug" = "xno"; then
+ SOUP_DEBUG_FLAGS="-DG_DISABLE_ASSERT -DG_DISABLE_CHECKS"
+ fi
+fi
+
+AC_SUBST(SOUP_DEBUG_FLAGS)
+
+# Set the maintainer flags
+if test -d .git; then
+ SOUP_MAINTAINER_FLAGS="-DG_DISABLE_DEPRECATED"
+fi
+AC_SUBST(SOUP_MAINTAINER_FLAGS)
+
+dnl ***************************
+dnl *** Checks for programs ***
+dnl ***************************
+
+AC_PROG_CC
+AC_PROG_INSTALL
+
+# Initialize libtool
+LT_INIT([win32-dll])
+
+dnl ***********************
+dnl *** Checks for glib ***
+dnl ***********************
+
+AM_PATH_GLIB_2_0(2.27.5,,,gobject gthread gio)
+if test "$GLIB_LIBS" = ""; then
+ AC_MSG_ERROR(GLIB 2.27.5 or later is required to build libsoup)
+fi
+GLIB_CFLAGS="$GLIB_CFLAGS -DG_DISABLE_SINGLE_INCLUDES"
+
+PKG_CHECK_MODULES(XML, libxml-2.0)
+AC_SUBST(XML_CFLAGS)
+AC_SUBST(XML_LIBS)
+
+dnl ***********************
+dnl *** Check for Win32 ***
+dnl ***********************
+
+AC_MSG_CHECKING([for Win32])
+case "$host" in
+ *-*-mingw*)
+ os_win32=yes
+ CFLAGS="$CFLAGS -D_REENTRANT"
+ ;;
+ *)
+ os_win32=no
+ ;;
+esac
+AC_MSG_RESULT([$os_win32])
+AM_CONDITIONAL(OS_WIN32, [test $os_win32 = yes])
+
+dnl *******************
+dnl *** Misc checks ***
+dnl *******************
+AC_CHECK_FUNCS(gmtime_r)
+AC_CHECK_FUNCS(mmap)
+AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket))
+
+dnl *************************
+dnl *** SQL Lite support ***
+dnl *************************
+
+AC_ARG_ENABLE(sqllite,
+ AS_HELP_STRING([--enable-sqllite], [Enable SQL lite support ]), ,
+ enable_sqllite=no)
+
+if test "$enable_sqllite" != "no"; then
+ PKG_CHECK_MODULES(SQLITE, sqlite3, :, [AC_MSG_ERROR(dnl
+[Could not find sqlite3 devel files:
+
+$SQLITE_PKG_ERRORS
+
+Pass "--without-sqlite" to configure if you want to build libsoup
+without sql lite support.])])
+
+fi
+
+AC_SUBST(SQLITE_CFLAGS)
+AC_SUBST(SQLITE_LIBS)
+
+AM_CONDITIONAL(SQLLITE_SUPPORT, [test $enable_sqllite = yes])
+
+dnl *********************
+dnl *** GNOME support ***
+dnl *********************
+AC_MSG_CHECKING(whether to build libsoup-gnome)
+AC_ARG_WITH(gnome,
+ AS_HELP_STRING([--without-gnome], [Do not build libsoup-gnome]),
+ :, [if test $os_win32 = yes; then with_gnome=no; else with_gnome=yes; fi])
+AC_MSG_RESULT($with_gnome)
+
+if test $with_gnome != no -a $os_win32 != yes; then
+ PKG_CHECK_MODULES(GNOME_KEYRING, gnome-keyring-1, :,
+ AC_MSG_ERROR(
+[Could not find gnome-keyring devel files.
+Configure with --without-gnome if you wish to build only libsoup
+without GNOME-specific features.]))
+fi
+AC_SUBST(GNOME_KEYRING_CFLAGS)
+AC_SUBST(GNOME_KEYRING_LIBS)
+
+AM_CONDITIONAL(BUILD_LIBSOUP_GNOME, test $with_gnome != no)
+
+if test $with_gnome != no; then
+ AC_DEFINE(HAVE_GNOME, 1, [Defined if GNOME support is enabled])
+
+ PKG_CHECK_MODULES(SQLITE, sqlite3, :, [AC_MSG_ERROR(dnl
+[Could not find sqlite3 devel files:
+
+$SQLITE_PKG_ERRORS
+
+Pass "--without-gnome" to configure if you want to build libsoup
+without GNOME support.])])
+fi
+AC_SUBST(HAVE_GNOME)
+AC_SUBST(SQLITE_CFLAGS)
+AC_SUBST(SQLITE_LIBS)
+
+
+dnl ***************
+dnl *** gtk-doc ***
+dnl ***************
+GTK_DOC_CHECK([1.10])
+
+dnl *****************************
+dnl *** gobject-introspection ***
+dnl *****************************
+GOBJECT_INTROSPECTION_CHECK([0.9.5])
+
+dnl *************************************
+dnl *** Warnings to show if using GCC ***
+dnl *************************************
+
+AC_ARG_ENABLE(more-warnings,
+ AS_HELP_STRING([--disable-more-warnings], [Inhibit compiler warnings]),
+ set_more_warnings=no)
+
+if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
+ CFLAGS="$CFLAGS \
+ -Wall -Wstrict-prototypes -Wmissing-declarations \
+ -Wmissing-prototypes -Wnested-externs -Wpointer-arith \
+ -Wdeclaration-after-statement -Wformat=2 -Winit-self \
+ -Waggregate-return -Wmissing-format-attribute"
+
+ for option in -Wmissing-include-dirs -Wundef; do
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $option"
+ AC_MSG_CHECKING([whether gcc understands $option])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])],
+ [has_option=yes],
+ [has_option=no])
+ AC_MSG_RESULT($has_option)
+ if test $has_option = no; then
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ done
+fi
+
+AC_MSG_CHECKING([for glib-networking (glib TLS implementation)])
+save_CFLAGS="$CFLAGS"
+save_LIBS="$LIBS"
+CFLAGS="$CFLAGS $GLIB_CFLAGS"
+LIBS="$LIBS $GLIB_LIBS"
+AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <gio/gio.h>],
+ [g_type_init (); return !g_tls_backend_supports_tls (g_tls_backend_get_default ());])],
+ [have_glib_networking=yes],
+ [have_glib_networking=no],
+ [have_glib_networking="unknown (cross-compiling)"])
+CFLAGS="$save_CFLAGS"
+LIBS="$save_LIBS"
+AC_MSG_RESULT($have_glib_networking)
+
+AC_ARG_ENABLE(tls-check,
+ AS_HELP_STRING([--disable-tls-check], [Don't error out if glib-networking is unavailable]))
+if test "$enable_tls_check" != "no" -a "$have_glib_networking" = "no"; then
+ AC_MSG_ERROR([libsoup requires glib-networking for TLS support.
+
+If you are building a package, you can pass --disable-tls-check to
+allow building libsoup anyway (since glib-networking is not actually
+required at compile time), but you should be sure to add a runtime
+dependency on it.])
+fi
+
+dnl ******************************
+dnl *** Stuff for regression tests
+dnl ******************************
+AC_MSG_NOTICE([checking for programs needed for regression tests])
+MISSING_REGRESSION_TEST_PACKAGES=""
+
+if test "$have_glib_networking" = "no"; then
+ MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES glib-networking"
+fi
+
+AC_ARG_WITH(apache-httpd,
+ AS_HELP_STRING([--with-apache-httpd], [Path to apache httpd (for tests)]),
+ APACHE_HTTPD="$withval",
+ [AC_PATH_PROGS(APACHE_HTTPD, httpd2 httpd apache2 apache, no, ${PATH}:/usr/sbin)])
+if test "$APACHE_HTTPD" != "no"; then
+ AC_MSG_CHECKING([Apache version])
+ apache_version=`$APACHE_HTTPD -v 2>/dev/null | sed -ne 's/Server version: Apache\///p'`
+ case $apache_version in
+ 2.2.*)
+ AC_MSG_RESULT([$apache_version (ok)])
+ ;;
+ *)
+ AC_MSG_RESULT([$apache_version (ignoring)])
+ APACHE_HTTPD="no"
+ ;;
+ esac
+fi
+AC_SUBST(APACHE_HTTPD)
+AC_DEFINE_UNQUOTED(APACHE_HTTPD, "$APACHE_HTTPD", [Apache httpd])
+
+if test "$APACHE_HTTPD" != "no"; then
+ AC_MSG_CHECKING([for Apache module directory])
+ AC_ARG_WITH(apache-module-dir,
+ AS_HELP_STRING([--with-apache-module-dir], [Apache modules dirs (for tests)]),
+ apache_module_dirs="$withval",
+ [apache_prefix=`dirname \`dirname $APACHE_HTTPD\``
+ mpm=`$APACHE_HTTPD -V | sed -ne 's/^Server MPM: */-/p' | tr 'A-Z' 'a-z'`
+ # This only works with bash, but should fail harmlessly in sh
+ apache_module_dirs=`echo $apache_prefix/lib{64,}/{apache,apache2,http,http2,httpd}{$mpm,}{/modules,}`])
+ for dir in $apache_module_dirs; do
+ if test -f $dir/mod_auth_digest.so; then
+ APACHE_MODULE_DIR="$dir"
+ fi
+ if test -f $dir/mod_ssl.so; then
+ APACHE_SSL_MODULE_DIR="$dir"
+ IF_HAVE_APACHE_SSL=""
+ fi
+ if test -f $dir/mod_php5.so; then
+ APACHE_PHP_MODULE_DIR="$dir"
+ APACHE_PHP_MODULE=mod_php5.so
+ fi
+ if test -f $dir/libphp5.so; then
+ APACHE_PHP_MODULE_DIR="$dir"
+ APACHE_PHP_MODULE=libphp5.so
+ fi
+ done
+ AC_MSG_RESULT($APACHE_MODULE_DIR)
+ AC_SUBST(APACHE_MODULE_DIR)
+ AC_SUBST(APACHE_SSL_MODULE_DIR)
+ AC_SUBST(APACHE_PHP_MODULE_DIR)
+ AC_SUBST(APACHE_PHP_MODULE)
+fi
+
+if test "$APACHE_HTTPD" != "no" -a -n "$APACHE_MODULE_DIR" -a -n "$APACHE_SSL_MODULE_DIR"; then
+ AC_DEFINE(HAVE_APACHE, 1, [Whether or not apache can be used for tests])
+ have_apache=1
+ if test -z "$APACHE_PHP_MODULE_DIR"; then
+ MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES mod_php5"
+ fi
+else
+ have_apache=0
+ if test "$APACHE_HTTPD" = "no" -o -z "$APACHE_MODULE_DIR"; then
+ MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES apache"
+ else
+ MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES mod_ssl"
+ fi
+fi
+AM_CONDITIONAL(HAVE_APACHE, test $have_apache = 1)
+
+if test "$have_apache" = 1; then
+ AC_CHECK_PROGS(PHP, php php5)
+
+ AC_MSG_CHECKING([for Apache PHP module])
+ if test -f $APACHE_PHP_MODULE_DIR/$APACHE_PHP_MODULE; then
+ have_php=yes
+ IF_HAVE_PHP=""
+ else
+ have_php=no
+ IF_HAVE_PHP="#"
+ MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES php5"
+ fi
+ AC_MSG_RESULT($have_php)
+
+ if test "$have_php" = yes; then
+ AC_MSG_CHECKING([for xmlrpc-epi-php])
+ if $PHP --rf xmlrpc_server_create | grep -q "does not exist"; then
+ have_xmlrpc_epi_php=no
+ MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES php-xmlrpc"
+ else
+ have_xmlrpc_epi_php=yes
+ fi
+ AC_MSG_RESULT($have_xmlrpc_epi_php)
+ fi
+fi
+
+AC_SUBST(IF_HAVE_PHP)
+AM_CONDITIONAL(HAVE_XMLRPC_EPI_PHP, test "$have_xmlrpc_epi_php" = yes)
+
+AC_PATH_PROG(CURL, curl, no)
+if test "$CURL" != no; then
+ AC_DEFINE(HAVE_CURL, 1, [Whether or not curl can be used for tests])
+else
+ MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES curl"
+fi
+AM_CONDITIONAL(HAVE_CURL, test "$CURL" != no)
+
+AC_SUBST(MISSING_REGRESSION_TEST_PACKAGES)
+AM_CONDITIONAL(MISSING_REGRESSION_TEST_PACKAGES, test -n "$MISSING_REGRESSION_TEST_PACKAGES")
+
+dnl **********************************************************
+dnl *** path of NTLM single-sign-on helper ntlm_auth
+dnl **********************************************************
+AC_ARG_WITH(ntlm-auth,
+ AC_HELP_STRING([--with-ntlm-auth=PATH],[Where to look for ntlm_auth, path points to ntlm_auth installation (default: /usr/bin/ntlm_auth)]),
+ ntlm_auth="$withval",
+ [if test $os_win32 = yes; then ntlm_auth="no"; else ntlm_auth="/usr/bin/ntlm_auth"; fi])
+if test "$ntlm_auth" != "no"; then
+ AC_DEFINE(USE_NTLM_AUTH, 1, [Whether or not use Samba's 'winbind' daemon helper 'ntlm_auth' for NTLM single-sign-on])
+ if test "$ntlm_auth" = "yes"; then
+ dnl --with-ntlm-auth (without path) used, use default path
+ ntlm_auth="/usr/bin/ntlm_auth"
+ fi
+fi
+AC_SUBST(ntlm_auth)
+AC_DEFINE_UNQUOTED(NTLM_AUTH, "$ntlm_auth", [Samba's 'winbind' daemon helper 'ntlm_auth' which can be used for NTLM single-sign-on])
+
+dnl *************************
+dnl *** Output Everything ***
+dnl *************************
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([
+ libsoup-2.4.pc
+ libsoup-gnome-2.4.pc
+ Makefile
+ libsoup-zip
+ libsoup/Makefile
+ tests/Makefile
+ tests/httpd.conf
+ docs/Makefile
+ docs/reference/Makefile
+ ])
+AC_OUTPUT
+
+if test -n "$MISSING_REGRESSION_TEST_PACKAGES"; then
+ echo ""
+ echo Some regression tests will not be run due to missing packages:
+ echo $MISSING_REGRESSION_TEST_PACKAGES
+ echo ""
+fi
--- /dev/null
+libsoup2.4 (2.35.90-0) unstable; urgency=low
+
+ * Git : pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.35.90-0
+
+ * [Kwangtae Ko] Change OS(SLP) with OS(TIZEN)
+
+ -- seung hak lee <s.hak.lee@samsung.com> Tue, 27 Dec 2011 16:24:57 +0900
+
+libsoup2.4 (2.35.90-1slp2+5) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.35.90-1slp2+5
+
+ * [DongJae KIM] Change 'use-ssl3' param value to TRUE :: Rollback open source patch to fix paypal.com issue.
+ * [Kwangtae Ko] Add null checking code in soup_socket_is_ssl() to fix timesofindia.indiatimes.com issue.
+
+ -- seung hak lee <s.hak.lee@samsung.com> Wed, 12 Oct 2011 21:31:02 +0900
+
+libsoup2.4 (2.35.90-1slp2+4) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.35.90-1slp2+4
+
+ * Remove SLP.h frome Makefile.am
+ * Add samsung specific private function declarations to SLP.h
+
+ -- seung hak lee <s.hak.lee@samsung.com> Fri, 30 Sep 2011 10:30:09 +0900
+
+libsoup2.4 (2.35.90-1slp2+3) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.35.90-1slp2+3
+
+ * ADD SLP.h in makefile.am
+
+ -- seung hak lee <s.hak.lee@samsung.com> Thu, 29 Sep 2011 10:00:09 +0900
+
+libsoup2.4 (2.35.90-1slp2+2) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.35.90-1slp2+2
+
+ * libsoup version up 2.23.92 >> 2.25.90
+ * Samsung patchs are merged
+
+ -- seung hak lee <s.hak.lee@samsung.com> Wed, 28 Sep 2011 21:32:27 +0900
+
+libsoup2.4 (2.35.90-1slp2+1) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.35.90-1slp2+1
+
+ * libsoup version up 2.23.92 >> 2.25.90
+
+ -- seung hak lee <s.hak.lee@samsung.com> Thu, 08 Sep 2011 14:20:57 +0900
+
+libsoup2.4 (2.33.92-1slp2+12) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+12
+
+ * DonJae KIM FIX: do not append data to existing files
+ * DonJae KIM FIX: Change soup-date-is-past checking value, using Current System Year
+
+ -- seung hak lee <s.hak.lee@samsung.com> Wed, 07 Sep 2011 15:58:10 +0900
+
+libsoup2.4 (2.33.92-1slp2+11) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+11
+
+ * Remove package dependency on libgnutlsxx27 and libgnutls-iopenssl27
+
+ -- Dongwook Lee <dwmax.lee@samsung.com> Tue, 6 Sep 2011 16:38:30 +0900
+
+libsoup2.4 (2.33.92-1slp2+10) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+10
+
+ * Kwangtae Ko Fix to disable limiting the number of pending connections
+ * Kwangtae Ko Fix to play a PluginStream Video
+
+ -- seung hak lee <s.hak.lee@samsung.com> Wed, 10 Aug 2011 17:59:30 +0900
+
+libsoup2.4 (2.33.92-1slp2+9) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+9
+
+ * DongJae KIM Rollback not to accept all certificate which has unknown CA
+ * Kwangtae Ko Fix to a crash problem when the browser is closed
+
+ -- seung hak lee <s.hak.lee@samsung.com> Wed, 13 Jul 2011 15:17:32 +0900
+
+libsoup2.4 (2.33.92-1slp2+8) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+8
+
+ * Keunsoon Lee Fix to accept all certificate which has unknown CA
+ * Jaehun Lim Fix to the encoding probolm of data protocol
+
+ -- seung hak lee <s.hak.lee@samsung.com> Mon, 13 Jun 2011 17:31:57 +0900
+
+libsoup2.4 (2.33.92-1slp2+7) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+7
+
+ * DongJae KIM Fix to the soup redirection when using proxy and https.
+ * Kwangtae Ko Fix to the browser touch freezing issue on http://news.mt.co.kr.
+
+ -- seung hak lee <s.hak.lee@samsung.com> Mon, 30 May 2011 15:39:25 +0900
+
+libsoup2.4 (2.33.92-1slp2+6) unstable; urgency=low
+
+ * lucid --> unstable
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+6
+
+ -- Taesoo Jun <steve.jun@samsung.com> Wed, 25 May 2011 15:39:01 +0900
+
+libsoup2.4 (2.33.92-1slp2+5) lucid; urgency=low
+
+ * patch for UTF-8 encoding patch for cachekey
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+5
+
+ -- Taesoo Jun <steve.jun@samsung.com> Wed, 25 May 2011 15:20:56 +0900
+
+libsoup2.4 (2.33.92-1slp2+4) unstable; urgency=low
+
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+4
+
+ * Kwangtae Ko Fix to a long-delay and overcharge CPU when closing...
+ * Taesoo Jun patch for contents sniff by SLP
+
+ -- Jaehun Lim <ljaehun.lim@samsung.com> Mon, 23 May 2011 17:30:54 +0900
+
+libsoup2.4 (2.33.92-1slp2+3) unstable; urgency=low
+
+ * patch for cache entry UTF-8 encoding in pack_entry()
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+3
+
+ -- Taesoo Jun <steve.jun@samsung.com> Mon, 16 May 2011 20:53:22 +0900
+
+libsoup2.4 (2.33.92-1slp2+2) unstable; urgency=low
+
+ * add gnutls and glib-networking dependency
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+2
+
+ -- JaeHyun Kim <jeik01.kim@samsung.com> Mon, 25 Apr 2011 17:14:29 +0900
+
+libsoup2.4 (2.33.92-1slp2+1) unstable; urgency=low
+
+ * Upgrade to 2.33.92
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.33.92-1slp2+1
+
+ -- JaeHyun Kim <jeik01.kim@samsung.com> Tue, 04 Jan 2011 14:27:51 +0900
+
+libsoup2.4 (2.31.6-1slp2+2) unstable; urgency=low
+
+ * Apply SEC_CERTIFICATE patch from browser part
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.31.6-1slp2+2
+
+ -- Taesoo Jun <steve.jun@samsung.com> Tue, 04 Jan 2011 14:27:51 +0900
+
+libsoup2.4 (2.31.6-1slp2+1) unstable; urgency=low
+
+ * Roll back to version, 2.31.6
+ * Apply the ActiveSync crash patch
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.31.6-1slp2+1
+
+ -- Taesoo Jun <steve.jun@samsung.com> Thu, 30 Dec 2010 11:21:36 +0900
+
+libsoup2.4 (2.32.2-1slp2+6) unstable; urgency=low
+
+ * Push patched files by sungwon chung
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.32.2-1slp2+6
+
+ -- Taesoo Jun <steve.jun@samsung.com> Wed, 22 Dec 2010 17:14:22 +0900
+
+libsoup2.4 (2.32.2-1slp2+5) unstable; urgency=low
+
+ * Fix build break by certificate patch
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.32.2-1slp2+5
+
+ -- Taesoo Jun <steve.jun@samsung.com> Thu, 16 Dec 2010 16:31:56 +0900
+
+libsoup2.4 (2.32.2-1slp2+4) unstable; urgency=low
+
+ * Push patched files
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.32.2-1slp2+4
+
+ -- Taesoo Jun <steve.jun@samsung.com> Thu, 16 Dec 2010 11:37:18 +0900
+
+libsoup2.4 (2.32.2-1slp2+3) unstable; urgency=low
+
+ * Apply a patch about a device binding to libsoup
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.32.2-1slp2+3
+
+ -- Taesoo Jun <steve.jun@samsung.com> Wed, 15 Dec 2010 16:53:17 +0900
+
+libsoup2.4 (2.32.2-1slp2+2) unstable; urgency=low
+
+ * Add soup-debug.h into dev package
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.32.2-1slp2+2
+
+ -- Sungwon Chung <sw6508.chung@samsung.com> Fri, 10 Dec 2010 21:09:39 +0900
+
+libsoup2.4 (2.32.2-1slp2+1) unstable; urgency=low
+
+ * Upgrade libsoup to 2.32.2
+ * Git : 165.213.180.234:slp/pkgs/l/libsoup2.4
+ * Tag : libsoup2.4_2.32.2-1slp2+1
+
+ -- Sungwon Chung <sw6508.chung@samsung.com> Fri, 10 Dec 2010 15:01:52 +0900
+
+libsoup2.4 (2.30.0-9slp2+1) unstable; urgency=low
+
+ * Commit fixing dailymotion.com problem with flash applied
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-9slp2+1
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Mon, 29 Nov 2010 17:24:19 +0100
+
+libsoup2.4 (2.30.0-8slp2+2) unstable; urgency=low
+
+ * Fix for crash after sign in on mobile version of twitter.com
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-8slp2+2
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Tue, 09 Nov 2010 17:36:49 +0100
+
+libsoup2.4 (2.30.0-8slp2+1) unstable; urgency=low
+
+ * Fix for problems with gnutls_certificate_get_peers
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-8slp2+1
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Fri, 05 Nov 2010 17:45:11 +0100
+
+libsoup2.4 (2.30.0-7slp2+3) unstable; urgency=low
+
+ * Additional gnutls session checks added for certificate handling
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-7slp2+3
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Fri, 22 Oct 2010 16:55:21 +0200
+
+libsoup2.4 (2.30.0-7slp2+2) unstable; urgency=low
+
+ * Added new SoupMessage property with server certificates list
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-7slp2+2
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Mon, 18 Oct 2010 11:59:34 +0200
+
+libsoup2.4 (2.30.0-7slp2+1) unstable; urgency=low
+
+ * Added new SoupMessage property with server certificates list
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-7slp2+1
+
+ -- Jongmin Lee <jm105.lee@samsung.com> Mon, 18 Oct 2010 11:37:19 +0200
+
+libsoup2.4 (2.30.0-6slp2+3) unstable; urgency=low
+
+ * Add "Replaces" field in debian control file
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-6slp2+3
+
+ -- Jongmin Lee <jm105.lee@samsung.com> Thu, 26 Aug 2010 10:41:47 +0900
+
+libsoup2.4 (2.30.0-6slp2+2) unstable; urgency=low
+
+ * Remove "Replaces" field in debian control file
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-6slp2+2
+
+ -- Jongmin Lee <jm105.lee@samsung.com> Wed, 25 Aug 2010 14:37:06 +0900
+
+libsoup2.4 (2.30.0-6slp2+1) unstable; urgency=low
+
+ * fix for livejournal.com incorrect response headers
+ * Git : 165.213.180.234:/git/slp/pkgs/libsoup2.4
+ * Tag : libsoup2.4_2.30.0-6slp2+1
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Thu, 22 Jul 2010 10:36:13 +0200
+
+libsoup2.4 (2.30.0-5slp2+3) unstable; urgency=low
+
+ * Fix flashplayer's build break
+ * Git : 165.213.180.234:/git/slp2.0/slp2.0-pkgs/libsoup-2.4
+ * Tag : libsoup2.4_2.30.0-5slp2+3
+
+ -- Jaehyun Kim <jeik01.kim@samsung.com> Fri, 09 Jul 2010 11:06:53 +0900
+
+libsoup2.4 (2.30.0-5slp2+2) unstable; urgency=low
+
+ * Maintainer/Uploader updated
+ * Git : 165.213.180.234:/git/slp2.0/slp2.0-pkgs/libsoup-2.4
+ * Tag : libsoup2.4_2.30.0-5slp2+2
+
+ -- Jaehyun Kim <jeik01.kim@samsung.com> Thu, 08 Jul 2010 15:48:05 +0900
+
+libsoup2.4 (2.30.0-5slp2+1) unstable; urgency=low
+
+ * unneeded logging removed
+ * Git : 165.213.180.234:/git/slp2.0/slp2.0-pkgs/libsoup-2.4
+ * Tag : libsoup2.4_2.30.0-5slp2+1
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Mon, 07 Jun 2010 10:40:49 +0200
+
+libsoup2.4 (2.30.0-4slp2+1) unstable; urgency=low
+
+ * fix for crash when connection address is invalid
+ * Git : 165.213.180.234:/git/slp2.0/slp2.0-pkgs/libsoup-2.4
+ * Tag : libsoup2.4_2.30.0-4slp2+1
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Fri, 28 May 2010 17:07:07 +0200
+
+libsoup2.4 (2.30.0-3slp2+2) unstable; urgency=low
+
+ * tag description added
+ * Git : 165.213.180.234:/git/slp2.0/slp2.0-pkgs/libsoup-2.4
+ * Tag : libsoup2.4_2.30.0-3slp2+2
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Mon, 24 May 2010 14:21:57 +0200
+
+libsoup2.4 (2.30.0-3slp2+1) unstable; urgency=low
+
+ * changelog updated
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Mon, 24 May 2010 14:04:22 +0200
+
+libsoup2.4 (2.30.0-2slp2+4) unstable; urgency=low
+
+ * unneded files removed
+ * Git : 165.213.180.234:/git/slp2.0/slp2.0-pkgs/libsoup-2.4
+ * Tag : libsoup2.4_2.30.0-3slp2+1
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Mon, 24 May 2010 13:58:54 +0200
+
+libsoup2.4 (2.30.0-2slp2+3) unstable; urgency=low
+
+ * fixing tag again. upsss
+ * Git : 165.213.180.234:/git/slp2.0/slp2.0-pkgs/libsoup-2.4
+ * Tag : libsoup2.4_2.30.0-2slp2+3
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Fri, 21 May 2010 15:56:27 +0900
+
+libsoup2.4 (2.30.0-2slp2+2) unstable; urgency=low
+
+ * adding tag description
+ * Git : 165.213.180.234:/git/slp2.0/slp2.0-pkgs/libsoup-2.4
+ * Tag : 2.30.0-2slp2+2
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Fri, 21 May 2010 15:40:45 +0900
+
+libsoup2.4 (2.30.0-2slp2+1) unstable; urgency=low
+
+ * just upversioning due to build problems
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Fri, 21 May 2010 15:19:39 +0900
+
+libsoup2.4 (2.30.0-2slp2) unstable; urgency=low
+
+ * Fix for Samsung Proxy problem in following situations:
+ - when running wikipedia.org from tests/get
+ - when using Samsung3G and opening abc.go.com site
+ - when using CJP01 router + proxy value set and abc.go.com
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Fri, 21 May 2010 14:47:58 +0900
+
+libsoup2.4 (2.30.0-1slp2) unstable; urgency=low
+
+ * libsoup upversioned to 2.30.0
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Wed, 14 Apr 2010 16:29:42 +0200
+
+libsoup2.4 (2.29.5-12slp2) unstable; urgency=low
+
+ * Added PRAGMA secure_delete, when db is initialized, to clear cookie file content when cookies are deleted
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Thu, 08 Apr 2010 11:53:08 +0200
+
+libsoup2.4 (2.29.5-11slp2+14) unstable; urgency=low
+
+ * Added missing dependency
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Thu, 25 Mar 2010 12:36:01 +0100
+
+libsoup2.4 (2.29.5-11slp2+13) unstable; urgency=low
+
+ * Recompilation because of toolchain upgrade
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Thu, 25 Mar 2010 09:42:28 +0100
+
+libsoup2.4 (2.29.5-11slp2+12) unstable; urgency=low
+
+ * added option --enable-sqllite
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Wed, 24 Mar 2010 16:52:36 +0100
+
+libsoup2.4 (2.29.5-11slp2+11) unstable; urgency=low
+
+ * reversion for new package policy
+
+ -- Jaehun Lim <ljaehun.lim@samsung.com> Fri, 19 Mar 2010 17:26:50 +0900
+
+libsoup2.4 (2.29.5-10) unstable; urgency=low
+
+ * memory leaks and logging fixes
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Thu, 04 Mar 2010 14:40:54 +0100
+
+libsoup2.4 (2.29.5-9) unstable; urgency=low
+
+ * logging fixes
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Thu, 25 Feb 2010 16:45:08 +0100
+
+libsoup2.4 (2.29.5-8) unstable; urgency=low
+
+ * Soup initialization fix for binding property
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Tue, 09 Feb 2010 11:10:05 +0100
+
+libsoup2.4 (2.29.5-7) unstable; urgency=low
+
+ * Setting binding property for session and connection
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Wed, 03 Feb 2010 11:37:40 +0100
+
+libsoup2.4 (2.29.5-6) unstable; urgency=low
+
+ * Add a gnutle dependency
+
+ -- Gyuyoung Kim <gyuyoung.kim@samsung.com> Thu, 28 Jan 2010 12:10:23 +0900
+
+libsoup2.4 (2.29.5-5) unstable; urgency=low
+
+ * Conflicts removed from debian/control
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Wed, 27 Jan 2010 11:02:11 +0100
+
+libsoup2.4 (2.29.5-4) unstable; urgency=low
+
+ * Fixed package files (conflict with 2.4-1):
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Wed, 27 Jan 2010 09:31:56 +0100
+
+libsoup2.4 (2.29.5-3) unstable; urgency=low
+
+ * Fixed package files
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Wed, 27 Jan 2010 08:20:40 +0100
+
+libsoup2.4 (2.29.5-2) unstable; urgency=low
+
+ * Fixed package files.
+
+ -- Jaroslaw Staniek <j.staniek@samsung.com> Tue, 26 Jan 2010 16:49:59 +0100
+
+libsoup2.4 (2.29.5-1) UNRELEASED; urgency=low
+
+ * Initial release.
+
+ -- Lukasz Slachciak <l.slachciak@samsung.com> Mon, 25 Jan 2010 15:24:00 +0100
--- /dev/null
+Source: libsoup2.4
+Section: net
+Priority: extra
+Maintainer: Jaehyun Kim <jeik01.kim@samsung.com>, Taesoo Jun <steve.jun@samsung.com>
+Uploaders: Lukasz Slachciak <l.slachciak@samsung.com>, Jaroslaw Staniek <j.staniek@samsung.com>, Lukasz Slachciak <l.slachciak@samsung.com>, Gyuyoung Kim <gyuyoung.kim@samsung.com>, Jaehun Lim <ljaehun.lim@samsung.com>, Jihye Lim <veronica.lim@samsung.com> Jongmin Lee <jm105.lee@samsung.com>, Sungwon Chung <sw6508.chung@samsung.com>, Taesoo Jun <steve.jun@samsung.com>, Taeksu Shin <taeksu.shin@samsung.com>, Seonghyeon Kim <shyeon.kim@samsung.com>, Dongwook Lee <dwmax.lee@samsung.com>, seung hak lee <s.hak.lee@samsung.com>
+Build-Depends: debhelper (>= 5), autotools-dev, libglib2.0-dev (>= 2.21.3), pkg-config, libxml2-dev, zlib1g-dev, libsqlite3-dev, glib-networking
+Standards-Version: 0.1.0
+
+Package: libsoup2.4
+Section: libs
+Architecture: any
+Depends: zlib1g, libsqlite3-0, libglib2.0-0, libxml2, glib-networking
+Replaces: libsoup2.4-1
+Description: an HTTP library implementation in C (shared libs)
+
+Package: libsoup2.4-dev
+Section: libdevel
+Architecture: any
+Depends: libsoup2.4 (= ${binary:Version}), libglib2.0-dev (>= 2.18.1), libxml2-dev, pkg-config, zlib1g-dev, libsqlite3-dev
+Description: an HTTP library implementation in C (development files)
+
+Package: libsoup2.4-dbg
+Section: debug
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, libsoup2.4 (= ${Source-Version})
+Description: an HTTP library implementation in C (development files)
--- /dev/null
+AUTHORS
+README
--- /dev/null
+@PREFIX@/include/libsoup-2.4/libsoup/*
+@PREFIX@/lib/libsoup*.la
+@PREFIX@/lib/libsoup*.a
+@PREFIX@/lib/pkgconfig/libsoup*
--- /dev/null
+@PREFIX@/lib/libsoup-2.4.so*
--- /dev/null
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+export DH_VERBOSE=1
+
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+CFLAGS += -Wall -g
+LDFLAGS ?=
+PREFIX ?= /usr
+DATADIR ?= /opt
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0
+else
+ CFLAGS += -O2
+endif
+
+LDFLAGS += -Wl,--rpath=$(PREFIX)/lib -Wl,--as-needed
+
+config.status:
+ dh_testdir
+ # Add here commands to configure the package.
+# CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" ./autogen.sh --prefix=$(PREFIX) --without-gnome --enable-sqllite=yes
+ CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" ./autogen.sh --prefix=$(PREFIX) --without-gnome --enable-sqllite=yes --disable-tls-check
+
+build: build-stamp
+
+build-stamp: config.status
+ dh_testdir
+
+ # Add here commands to compile the package.
+ $(MAKE)
+ #docbook-to-man debian/ncurses.sgml > ncurses.1
+
+ for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
+ cat $$f > $${f%.in}; \
+ sed -i -e "s#@PREFIX@#$(PREFIX)#g" $${f%.in}; \
+ sed -i -e "s#@DATADIR@#$(DATADIR)#g" $${f%.in}; \
+ done
+
+ touch $@
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp
+
+ # Add here commands to clean up after the build process.
+ -$(MAKE) distclean
+ifneq "$(wildcard /usr/share/misc/config.sub)" ""
+ cp -f /usr/share/misc/config.sub config.sub
+endif
+ifneq "$(wildcard /usr/share/misc/config.guess)" ""
+ cp -f /usr/share/misc/config.guess config.guess
+endif
+
+ for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
+ rm -f $${f%.in}; \
+ done
+
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ # Add here commands to install the package into debian/ncurses.
+ $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs
+ dh_installdocs
+ dh_installexamples
+ dh_install --sourcedir=debian/tmp
+# dh_installmenu
+# dh_installdebconf
+# dh_installlogrotate
+# dh_installemacsen
+# dh_installpam
+# dh_installmime
+# dh_python
+# dh_installinit
+# dh_installcron
+# dh_installinfo
+ dh_installman
+ dh_link
+ dh_strip --dbg-package=libsoup2.4-dbg
+ dh_compress
+ dh_fixperms
+# dh_perl
+ dh_makeshlibs
+ dh_installdeb
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
--- /dev/null
+SUBDIRS = reference
--- /dev/null
+## Process this file with automake to produce Makefile.in
+AUTOMAKE_OPTIONS = 1.6
+
+# The name of the module
+DOC_MODULE=libsoup-2.4
+
+# The top-level SGML file.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting functions and macros.
+DOC_SOURCE_DIR=../../libsoup
+
+# Extra options to supply to gtkdoc-scan.
+SCAN_OPTIONS=--deprecated-guards=LIBSOUP_DISABLE_DEPRECATED --rebuild-types
+
+# Extra options to supply to gtkdoc-scangobj.
+SCANGOBJ_OPTIONS=--type-init-func 'g_type_init(); g_thread_init(NULL)'
+
+# Extra options to supply to gtkdoc-mkdb.
+MKDB_OPTIONS=--sgml-mode --output-format=xml
+
+# Extra options to supply to gtkdoc-fixref.
+FIXXREF_OPTIONS=
+
+# Used for dependencies.
+HFILE_GLOB=
+CFILE_GLOB=
+
+# Header files to ignore when scanning.
+IGNORE_HFILES= soup.h soup-marshal.h soup-enum-types.h \
+ soup-message-private.h soup-session-private.h \
+ soup-auth-basic.h soup-auth-digest.h soup-auth-ntlm.h \
+ soup-connection.h soup-connection-ntlm.h \
+ soup-dns.h soup-auth-manager.h soup-auth-manager-ntlm.h \
+ soup-message-queue.h soup-path-map.h soup-ssl.h \
+ soup-proxy-resolver.h soup-proxy-resolver-gnome.h \
+ soup-proxy-resolver-static.h
+
+# Images to copy into HTML directory.
+HTML_IMAGES =
+
+# Extra XML files that are included by $(DOC_MAIN_SGML_FILE).
+content_files = \
+ build-howto.xml \
+ client-howto.xml \
+ server-howto.xml \
+ porting-2.2-2.4.xml
+
+# Other files to distribute.
+extra_files =
+
+
+# CFLAGS and LDFLAGS for compiling scan program. Only needed
+# if $(DOC_MODULE).types is non-empty.
+GTKDOC_CFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ $(GLIB_CFLAGS) \
+ $(XML_CFLAGS) \
+ $(GNUTLS_CFLAGS)
+
+GTKDOC_LIBS = $(top_builddir)/libsoup/libsoup-gnome-2.4.la
+
+# include common portion ...
+include $(top_srcdir)/gtk-doc.make
+
+# kludges
+$(srcdir)/tmpl/*.sgml:
+
+clean: clean-am
+ rm -rf tmpl
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<refentry id="libsoup-build-howto">
+<refmeta>
+<refentrytitle>Compiling with libsoup</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBSOUP Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Compiling with libsoup</refname><refpurpose>Notes on compiling</refpurpose>
+</refnamediv>
+
+<refsect2>
+<title>Using pkg-config</title>
+
+<para>
+Like other GNOME libraries, <application>libsoup</application> uses
+<application>pkg-config</application> to provide compiler options. The
+package name is "<literal>libsoup-2.4</literal>". So in your
+<literal>configure</literal> script, you might specify something like:
+</para>
+
+<informalexample><programlisting>
+PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26])
+AC_SUBST(LIBSOUP_CFLAGS)
+AC_SUBST(LIBSOUP_LIBS)
+</programlisting></informalexample>
+
+<para>
+The "<literal>2.4</literal>" in the package name is the "API version"
+(indicating "the version of the <application>libsoup</application> API
+that first appeared in version 2.4") and is essentially just part of
+the package name.
+</para>
+
+<para>
+If you are using any of the GNOME-specific features of
+<application>libsoup</application> (such as automatic proxy
+configuration), you must require
+"<literal>libsoup-gnome-2.4</literal>" instead:
+</para>
+
+<informalexample><programlisting>
+PKG_CHECK_MODULES(LIBSOUP, [libsoup-gnome-2.4 >= 2.26])
+AC_SUBST(LIBSOUP_CFLAGS)
+AC_SUBST(LIBSOUP_LIBS)
+</programlisting></informalexample>
+
+<para>
+You can also make <application>libsoup-gnome</application> an optional
+dependency:
+</para>
+
+<informalexample><programlisting>
+PKG_CHECK_MODULES(LIBSOUP_GNOME,
+ [libsoup-gnome-2.4 >= 2.26],
+ [LIBSOUP_CFLAGS="$LIBSOUP_GNOME_CFLAGS"
+ LIBSOUP_LIBS="$LIBSOUP_GNOME_LIBS"
+ AC_DEFINE(HAVE_LIBSOUP_GNOME, 1, [Have libsoup-gnome])],
+ [PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26])])
+AC_SUBST(LIBSOUP_CFLAGS)
+AC_SUBST(LIBSOUP_LIBS)
+</programlisting></informalexample>
+
+<para>
+This will allow the application to be built with either plain
+<application>libsoup</application> or with
+<application>libsoup-gnome</application>, and it will define the C
+preprocessor symbol <literal>HAVE_LIBSOUP_GNOME</literal> if
+<application>libsoup-gnome</application> features are available.
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Headers</title>
+
+<para>
+Code using <application>libsoup</application> should do:
+</para>
+
+<informalexample><programlisting>
+#include <libsoup/soup.h>
+</programlisting></informalexample>
+
+<para>
+or, for <application>libsoup-gnome</application>:
+</para>
+
+<informalexample><programlisting>
+#include <libsoup/soup-gnome.h>
+</programlisting></informalexample>
+
+<para>
+Including individual headers besides the two main header files is not
+recommended. You may include both <literal>soup.h</literal> and
+<literal>soup-gnome.h</literal> (though this is not required; the
+latter automatically includes the former).
+</para>
+
+</refsect2>
+
+</refentry>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<refentry id="libsoup-client-howto">
+<refmeta>
+<refentrytitle>Soup Client Basics</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBSOUP Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Soup Client Basics</refname><refpurpose>Client-side tutorial</refpurpose>
+</refnamediv>
+
+<refsect2>
+<title>Creating a <type>SoupSession</type></title>
+
+<para>
+The first step in using the client API is to create a <link
+linkend="SoupSession"><type>SoupSession</type></link>. The session object
+encapsulates all of the state that <application>libsoup</application>
+is keeping on behalf of your program; cached HTTP connections,
+authentication information, etc.
+</para>
+
+<para>
+There are two subclasses of <link
+linkend="SoupSession"><type>SoupSession</type></link> that you can use, with
+slightly different behavior:
+</para>
+
+<itemizedlist>
+ <listitem><para>
+ <link linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>,
+ which uses callbacks and the glib main loop to provide
+ asynchronous I/O.
+ </para></listitem>
+
+ <listitem><para>
+ <link linkend="SoupSessionSync"><type>SoupSessionSync</type></link>,
+ which uses blocking I/O rather than callbacks, making it more
+ suitable for threaded applications.
+ </para></listitem>
+</itemizedlist>
+
+<para>
+If you want to do a mix of mainloop-based and blocking I/O, you will
+need to create two different session objects.
+</para>
+
+<para>
+When you create the session (with <link
+linkend="soup-session-async-new-with-options"><function>soup_session_async_new_with_options</function></link>
+or <link
+linkend="soup-session-sync-new-with-options"><function>soup_session_sync_new_with_options</function></link>),
+you can specify various additional options:
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><link linkend="SOUP-SESSION-MAX-CONNS--CAPS"><literal>SOUP_SESSION_MAX_CONNS</literal></link></term>
+ <listitem><para>
+ Allows you to set the maximum total number of connections
+ the session will have open at one time. (Once it reaches
+ this limit, it will either close idle connections, or
+ wait for existing connections to free up before starting
+ new requests.)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SESSION-MAX-CONNS-PER-HOST--CAPS"><literal>SOUP_SESSION_MAX_CONNS_PER_HOST</literal></link></term>
+ <listitem><para>
+ Allows you to set the maximum total number of connections
+ the session will have open <emphasis>to a single
+ host</emphasis> at one time.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SESSION-USE-NTLM--CAPS"><literal>SOUP_SESSION_USE_NTLM</literal></link></term>
+ <listitem><para>
+ If <literal>TRUE</literal>, then Microsoft NTLM
+ authentication will be used if available (and will be
+ preferred to HTTP Basic or Digest authentication).
+ If <literal>FALSE</literal>, NTLM authentication won't be
+ used, even if it's the only authentication type available.
+ (NTLM works differently from the standard HTTP
+ authentication types, so it needs to be handled
+ specially.)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SESSION-SSL-CA-FILE--CAPS"><literal>SOUP_SESSION_SSL_CA_FILE</literal></link></term>
+ <listitem><para>
+ Points to a file containing certificates for recognized
+ SSL Certificate Authorities. If this is set, then HTTPS
+ connections will be checked against these authorities, and
+ rejected if they can't be verified. (Otherwise all SSL
+ certificates will be accepted automatically.)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SESSION-ASYNC-CONTEXT--CAPS"><literal>SOUP_SESSION_ASYNC_CONTEXT</literal></link></term>
+ <listitem><para>
+ A <link
+ linkend="GMainContext"><type>GMainContext</type></link>
+ which the session will use for asynchronous operations.
+ This can be set if you want to use a
+ <type>SoupSessionAsync</type> in a thread other than the
+ main thread.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SESSION-ADD-FEATURE--CAPS"><literal>SOUP_SESSION_ADD_FEATURE</literal></link> and <link linkend="SOUP-SESSION-ADD-FEATURE-BY-TYPE--CAPS"><literal>SOUP_SESSION_ADD_FEATURE_BY_TYPE</literal></link></term>
+ <listitem><para>
+ These allow you to specify <link
+ linkend="SoupSessionFeature"><type>SoupSessionFeature</type></link>s
+ (discussed <link linkend="session-features">below</link>)
+ to add at construct-time.
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+If you don't need to specify any options, you can just use <link
+linkend="soup-session-async-new"><function>soup_session_async_new</function></link> or
+<link linkend="soup-session-sync-new"><function>soup_session_sync_new</function></link>,
+which take no arguments.
+</para>
+
+</refsect2>
+
+<refsect2 id="session-features">
+<title>Session features</title>
+
+<para>
+Additional session functionality is provided as <link
+linkend="SoupSessionFeature"><type>SoupSessionFeature</type></link>s,
+which can be added to a session, via the <link
+linkend="SOUP-SESSION-ADD-FEATURE--CAPS"><literal>SOUP_SESSION_ADD_FEATURE</literal></link>
+and <link
+linkend="SOUP-SESSION-ADD-FEATURE-BY-TYPE--CAPS"><literal>SOUP_SESSION_ADD_FEATURE_BY_TYPE</literal></link>
+options at session-construction-time, or afterward via the <link
+linkend="soup-session-add-feature"><function>soup_session_add_feature</function></link>
+and <link
+linkend="soup-session-add-feature-by-type"><function>soup_session_add_feature_by_type</function></link>
+functions. Some of the features available in
+<application>libsoup</application> are:
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><link linkend="SoupLogger"><type>SoupLogger</type></link></term>
+ <listitem><para>
+ A debugging aid, which logs all of libsoup's HTTP traffic
+ to <literal>stdout</literal> (or another place you specify).
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SoupCookieJar"><type>SoupCookieJar</type></link> and <link linkend="SoupCookieJarText"><type>SoupCookieJarText</type></link></term>
+ <listitem><para>
+ Support for HTTP cookies. <type>SoupCookieJar</type>
+ provides non-persistent cookie storage, while
+ <type>SoupCookieJarText</type> uses a text file to keep
+ track of cookies between sessions.
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+And in <application>libsoup-gnome</application>:
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><link linkend="SOUP-TYPE-PROXY-RESOLVER-GNOME--CAPS"><type>SoupProxyResolverGNOME</type></link></term>
+ <listitem><para>
+ A feature that automatically determines the correct HTTP
+ proxy to use for requests.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SoupCookieJarSqlite"><type>SoupCookieJarSqlite</type></link></term>
+ <listitem><para>
+ Support for HTTP cookies stored in an
+ <application>SQLite</application> database.
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+Use the "add_feature_by_type" property/function to add features that
+don't require any configuration (such as <link
+linkend="SOUP-TYPE-PROXY-RESOLVER-GNOME--CAPS"><type>SoupProxyResolverGNOME</type></link>),
+and the "add_feature" property/function to add features that must be
+constructed first (such as <link
+linkend="SoupLogger"><type>SoupLogger</type></link>). For example, an
+application might do something like the following:
+</para>
+
+<informalexample><programlisting>
+ session = soup_session_async_new_with_options (
+#ifdef HAVE_LIBSOUP_GNOME
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
+#endif
+ NULL);
+ if (debug_level) {
+ SoupLogger *logger;
+
+ logger = soup_logger_new (debug_level, -1);
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
+ g_object_unref (logger);
+ }
+</programlisting></informalexample>
+
+</refsect2>
+
+<refsect2>
+<title>Creating and Sending SoupMessages</title>
+
+<para>
+Once you have a session, you do HTTP traffic using <link
+linkend="SoupMessage"><type>SoupMessage</type></link>. In the simplest
+case, you only need to create the message and it's ready to send:
+</para>
+
+<informalexample><programlisting>
+ SoupMessage *msg;
+
+ msg = soup_message_new ("GET", "http://example.com/");
+</programlisting></informalexample>
+
+<para>
+In more complicated cases, you can use various <link
+linkend="SoupMessage">SoupMessage</link>, <link
+linkend="SoupMessageHeaders">SoupMessageHeaders</link>, and <link
+linkend="SoupMessageBody">SoupMessageBody</link> methods to set the
+request headers and body of the message:
+</para>
+
+<informalexample><programlisting>
+ SoupMessage *msg;
+
+ msg = soup_message_new ("POST", "http://example.com/form.cgi");
+ soup_message_set_request (msg, "application/x-www-form-urlencoded",
+ SOUP_MEMORY_COPY, formdata, strlen (formdata));
+ soup_message_headers_append (msg->request_headers, "Referer", referring_url);
+</programlisting></informalexample>
+
+<para>
+(Although this is a bad example, because
+<application>libsoup</application> actually has convenience methods
+for dealing with <link linkend="libsoup-24-HTML-Form-Support">HTML
+forms</link>, as well as <link
+linkend="libsoup-24-XMLRPC-Support">XML-RPC</link>.)
+</para>
+
+<para>
+You can also use <link
+linkend="soup-message-set-flags"><function>soup_message_set_flags</function></link>
+to change some default behaviors. For example, by default,
+<type>SoupSession</type> automatically handles responses from the
+server that redirect to another URL. If you would like to handle these
+yourself, you can set the <link linkend="SOUP-MESSAGE-NO-REDIRECT--CAPS"><literal>SOUP_MESSAGE_NO_REDIRECT</literal></link>
+flag.
+</para>
+
+<refsect3>
+<title>Sending a Message Synchronously</title>
+
+<para>
+To send a message and wait for the response, use <link
+linkend="soup-session-send-message"><function>soup_session_send_message</function></link>:
+</para>
+
+<informalexample><programlisting>
+ guint status;
+
+ status = soup_session_send_message (session, msg);
+</programlisting></informalexample>
+
+<para>
+(If you use <function>soup_session_send_message</function> with a
+<link linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>,
+it will run the main loop itself until the message is complete.)
+</para>
+
+<para>
+The return value from <function>soup_session_send_message</function>
+is a <link linkend="soup-status">libsoup status code</link>,
+indicating either a transport error that prevented the message from
+being sent, or the HTTP status that was returned by the server in
+response to the message. (The status is also available as
+<literal>msg->status_code</literal>.)
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>Sending a Message Asynchronously</title>
+
+<para>
+To send a message asynchronously, use <link
+linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>:
+</para>
+
+<informalexample><programlisting>
+ ...
+ soup_session_queue_message (session, msg, my_callback, my_callback_data);
+ ...
+}
+
+static void
+my_callback (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ /* Handle the response here */
+}
+</programlisting></informalexample>
+
+<para>
+The message will be added to the session's queue, and eventually (when
+control is returned back to the main loop), it will be sent and the
+response be will be read. When the message is complete,
+<literal>callback</literal> will be invoked, along with the data you
+passed to <function>soup_session_queue_message</function>.
+</para>
+
+<para>
+<link
+linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>
+steals a reference to the message object, and unrefs it after the last
+callback is invoked on it. So in the usual case, messages sent
+asynchronously will be automatically freed for you without you needing
+to do anything. (Of course, this wouldn't work when using the synchronous
+API, since you will usually need continue working with the message
+after calling <link
+linkend="soup-session-send-message"><function>soup_session_send_message</function></link>,
+so in that case, you must unref it explicitly when you are done with
+it.)
+</para>
+
+<para>
+(If you use <link
+linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>
+with a <link
+linkend="SoupSessionSync"><type>SoupSessionSync</type></link>, the
+message will be sent in another thread, with the callback eventually
+being invoked in the session's <link linkend="SOUP-SESSION-ASYNC-CONTEXT--CAPS"><literal>SOUP_SESSION_ASYNC_CONTEXT</literal></link>.)
+</para>
+
+</refsect3>
+
+</refsect2>
+
+<refsect2>
+<title>Processing the Response</title>
+
+<para>
+Once you have received the response from the server, synchronously or
+asynchronously, you can look at the response fields in the
+<literal>SoupMessage</literal> to decide what to do next. The
+<structfield>status_code</structfield> and
+<structfield>reason_phrase</structfield> fields contain the numeric
+status and textual status response from the server.
+<structfield>response_headers</structfield> contains the response
+headers, which you can investigate using <link
+linkend="soup-message-headers-get"><function>soup_message_headers_get</function></link> and
+<link
+ linkend="soup-message-headers-foreach"><function>soup_message_headers_foreach</function></link>.
+The response body (if any) is in the
+<structfield>response_body</structfield> field.
+</para>
+
+<para>
+<link
+linkend="SoupMessageHeaders"><type>SoupMessageHeaders</type></link>
+automatically parses several important headers in
+<structfield>response_headers</structfield> for you and provides
+specialized accessors for them. Eg, <link
+linkend="soup-message-headers-get-content-type"><function>soup_message_headers_get_content_type</function></link>.
+There are several generic methods such as <link
+linkend="soup-header-parse-param-list"><function>soup_header_parse_param_list</function></link>
+(for parsing an attribute-list-type header) and <link
+linkend="soup-header-contains"><function>soup_header_contains</function></link>
+(for quickly testing if a list-type header contains a particular
+token). These handle the various syntactical oddities of parsing HTTP
+headers much better than functions like
+<function>g_strsplit</function> or <function>strstr</function>.
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Intermediate/Automatic Processing</title>
+
+<para>
+You can also connect to various <type>SoupMessage</type> signals to do
+processing at intermediate stages of HTTP I/O. Eg, the <link
+linkend="SoupMessage-got-chunk"><literal>got-chunk</literal></link>
+signal is emitted as each piece of the response body is read (allowing
+you to provide progress information when receiving a large response,
+for example). <type>SoupMessage</type> also provides two convenience
+methods, <link
+linkend="soup-message-add-header-handler"><function>soup_message_add_header_handler</function></link>,
+and <link
+linkend="soup-message-add-status-code-handler"><function>soup_message_add_status_code_handler</function></link>,
+which allow you to set up a signal handler that will only be invoked
+for messages with certain response headers or status codes.
+<type>SoupSession</type> uses this internally to handle authentication
+and redirection.
+</para>
+
+<para>
+When using the synchronous API, the callbacks and signal handlers will
+be invoked during the call to <link
+linkend="soup-session-send-message"><function>soup_session_send_message</function></link>.
+</para>
+
+<para>
+To automatically set up handlers on all messages sent via a session,
+you can connect to the session's <link
+linkend="SoupSession-request-started"><literal>request_started</literal></link>
+signal, and add handlers to each message from there.
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Handling Authentication</title>
+
+<para>
+<type>SoupSession</type> handles most of the details of HTTP
+authentication for you. If it receives a 401 ("Unauthorized") or 407
+("Proxy Authentication Required") response, the session will emit the
+<link linkend="SoupSession-authenticate">authenticate</link> signal,
+providing you with a <link
+linkend="SoupAuth"><type>SoupAuth</type></link> object indicating the
+authentication type ("Basic", "Digest", or "NTLM") and the realm name
+provided by the server. If you have a username and password available
+(or can generate one), call <link
+linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>
+to give the information to libsoup. The session will automatically
+requeue the message and try it again with that authentication
+information. (If you don't call
+<function>soup_auth_authenticate</function>, the session will just
+return the message to the application with its 401 or 407 status.)
+</para>
+
+<para>
+If the server doesn't accept the username and password provided, the
+session will emit <link
+linkend="SoupSession-authenticate">authenticate</link> again, with the
+<literal>retrying</literal> parameter set to <literal>TRUE</literal>. This lets the
+application know that the information it provided earlier was
+incorrect, and gives it a chance to try again. If this
+username/password pair also doesn't work, the session will contine to
+emit <literal>authenticate</literal> again and again until the
+provided username/password successfully authenticates, or until the
+signal handler fails to call <link
+linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>,
+at which point <application>libsoup</application> will allow the
+message to fail (with status 401 or 407).
+</para>
+
+<para>
+If you need to handle authentication asynchronously (eg, to pop up a
+password dialog without recursively entering the main loop), you can
+do that as well. Just call <link
+linkend="soup-session-pause-message"><function>soup_session_pause_message</function></link>
+on the message before returning from the signal handler, and
+<function>g_object_ref</function> the <type>SoupAuth</type>. Then,
+later on, after calling <function>soup_auth_authenticate</function>
+(or deciding not to), call <link
+linkend="soup-session-unpause-message"><function>soup_session_unpause_message</function></link>
+to resume the paused message.
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Multi-threaded usage</title>
+
+<para>
+The only explicitly thread-safe operations in
+<application>libsoup</application> are <link
+linkend="SoupSessionSync"><type>SoupSessionSync</type></link>'s
+implementations of the <link
+linkend="SoupSession"><type>SoupSession</type></link> methods. So
+after creating a <type>SoupSessionSync</type>, you can call <link
+linkend="soup-session-send-message"><function>soup_session_send_message</function></link>
+and <link
+linkend="soup-session-cancel-message"><function>soup_session_cancel_message</function></link>
+on it from any thread. But, eg, while the session is processing a
+message, you should not call any <link
+linkend="SoupMessage"><type>SoupMessage</type></link> methods on it
+from any thread other than the one in which it is being sent. (That
+is, you should not call any <type>SoupMessage</type> methods on it
+except from a message or session callback or signal handler.)
+</para>
+
+<para>
+All other objects (including <link
+linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>)
+should only be used from a single thread, with objects that are also
+only be used from that thread. (And in particular, if you set a
+non-default <link
+linkend="GMainContext"><type>GMainContext</type></link> on a session,
+socket, etc, then you can only use that object from the thread in
+which that <type>GMainContext</type> is running.)
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Sample Programs</title>
+
+<para>
+A few sample programs are available in the
+<application>libsoup</application> sources:
+</para>
+
+<itemizedlist>
+ <listitem><para>
+ <emphasis role="bold"><literal>get</literal></emphasis> is a simple command-line
+ HTTP GET utility using the asynchronous API.
+ </para></listitem>
+
+ <listitem><para>
+ <emphasis role="bold"><literal>getbug</literal></emphasis> is a trivial
+ demonstration of the <link
+ linkend="libsoup-XMLRPC-Support">XMLRPC</link> interface.
+ (<emphasis
+ role="bold"><literal>xmlrpc-test</literal></emphasis> provides
+ a slightly more complicated example.)
+ </para></listitem>
+
+ <listitem><para>
+ <emphasis role="bold"><literal>auth-test</literal></emphasis> shows how to use
+ authentication handlers and status-code handlers, although in
+ a fairly unusual way.
+ </para></listitem>
+
+ <listitem><para>
+ <emphasis role="bold"><literal>simple-proxy</literal></emphasis> uses both the
+ client and server APIs to create a simple (and not very
+ RFC-compliant) proxy server. It shows how to use the <link
+ linkend="SoupMessageFlags"><literal>SOUP_MESSAGE_OVERWRITE_CHUNKS</literal></link>
+ flag when reading a message to save memory by processing each
+ chunk of the message as it is read, rather than accumulating
+ them all into a single buffer to process all at the end.
+ </para></listitem>
+</itemizedlist>
+
+<para>
+More complicated examples are available in GNOME CVS. The <ulink
+url="http://live.gnome.org/LibSoup"><application>libsoup</application>
+pages</ulink> on the GNOME wiki include a <ulink
+url="http://live.gnome.org/LibSoup/Users">list of applications using
+<application>libsoup</application></ulink>.
+</para>
+
+</refsect2>
+
+</refentry>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<book id="index" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <bookinfo>
+ <title>libsoup Reference Manual</title>
+ </bookinfo>
+
+ <chapter>
+ <title>Tutorial</title>
+ <xi:include href="build-howto.xml"/>
+ <xi:include href="client-howto.xml"/>
+ <xi:include href="server-howto.xml"/>
+ <xi:include href="porting-2.2-2.4.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Core API</title>
+ <xi:include href="xml/soup-auth.xml"/>
+ <xi:include href="xml/soup-auth-domain.xml"/>
+ <xi:include href="xml/soup-auth-domain-basic.xml"/>
+ <xi:include href="xml/soup-auth-domain-digest.xml"/>
+ <xi:include href="xml/soup-cookie.xml"/>
+ <xi:include href="xml/soup-message.xml"/>
+ <xi:include href="xml/soup-message-headers.xml"/>
+ <xi:include href="xml/soup-message-body.xml"/>
+ <xi:include href="xml/soup-method.xml"/>
+ <xi:include href="xml/soup-multipart.xml"/>
+ <xi:include href="xml/soup-server.xml"/>
+ <xi:include href="xml/soup-session.xml"/>
+ <xi:include href="xml/soup-session-async.xml"/>
+ <xi:include href="xml/soup-session-sync.xml"/>
+ <xi:include href="xml/soup-status.xml"/>
+ <xi:include href="xml/soup-uri.xml"/>
+ <xi:include href="xml/soup-misc.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Additional Features</title>
+ <xi:include href="xml/soup-session-feature.xml"/>
+ <xi:include href="xml/soup-content-sniffer.xml"/>
+ <xi:include href="xml/soup-cookie-jar.xml"/>
+ <xi:include href="xml/soup-cookie-jar-text.xml"/>
+ <xi:include href="xml/soup-logger.xml"/>
+ <xi:include href="xml/soup-proxy-uri-resolver.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Web Services APIs</title>
+ <xi:include href="xml/soup-forms.xml"/>
+ <xi:include href="xml/soup-xmlrpc.xml"/>
+ <xi:include href="xml/soup-value-utils.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>GNOME integration</title>
+ <xi:include href="xml/soup-gnome-features.xml"/>
+ <xi:include href="xml/soup-cookie-jar-sqlite.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Low-level Networking API</title>
+ <xi:include href="xml/soup-address.xml"/>
+ <xi:include href="xml/soup-socket.xml"/>
+ </chapter>
+
+ <index>
+ <title>Index</title>
+ </index>
+
+ <xi:include href="xml/annotation-glossary.xml">
+ <xi:fallback />
+ </xi:include>
+
+</book>
--- /dev/null
+<FUNCTION>
+<NAME>soup_address_get_sockaddr</NAME>
+<RETURNS>struct sockaddr *</RETURNS>
+SoupAddress *addr,
+int *len
+</FUNCTION>
--- /dev/null
+<INCLUDE>libsoup/soup.h</INCLUDE>
+<SECTION>
+<FILE>soup-message</FILE>
+<TITLE>SoupMessage</TITLE>
+SoupMessage
+<SUBSECTION>
+soup_message_new
+soup_message_new_from_uri
+soup_message_set_request
+soup_message_set_response
+<SUBSECTION>
+SoupHTTPVersion
+soup_message_set_http_version
+soup_message_get_http_version
+soup_message_get_uri
+soup_message_set_uri
+soup_message_get_address
+<SUBSECTION>
+soup_message_set_status
+soup_message_set_status_full
+soup_message_is_keepalive
+<SUBSECTION>
+soup_message_add_header_handler
+soup_message_add_status_code_handler
+<SUBSECTION>
+SoupMessageFlags
+soup_message_set_flags
+soup_message_get_flags
+SoupChunkAllocator
+soup_message_set_chunk_allocator
+<SUBSECTION>
+soup_message_disable_feature
+<SUBSECTION>
+SOUP_MESSAGE_METHOD
+SOUP_MESSAGE_URI
+SOUP_MESSAGE_HTTP_VERSION
+SOUP_MESSAGE_FLAGS
+SOUP_MESSAGE_STATUS_CODE
+SOUP_MESSAGE_REASON_PHRASE
+SOUP_MESSAGE_SERVER_SIDE
+<SUBSECTION Standard>
+SOUP_MESSAGE
+SOUP_IS_MESSAGE
+SOUP_TYPE_MESSAGE
+soup_message_get_type
+SOUP_MESSAGE_CLASS
+SOUP_IS_MESSAGE_CLASS
+SOUP_MESSAGE_GET_CLASS
+SoupMessageClass
+<SUBSECTION Private>
+soup_message_wrote_informational
+soup_message_wrote_headers
+soup_message_wrote_chunk
+soup_message_wrote_body_data
+soup_message_wrote_body
+soup_message_got_informational
+soup_message_got_headers
+soup_message_content_sniffed
+soup_message_got_chunk
+soup_message_got_body
+soup_message_finished
+soup_message_restarted
+</SECTION>
+
+<SECTION>
+<FILE>soup-method</FILE>
+SOUP_METHOD_OPTIONS
+SOUP_METHOD_GET
+SOUP_METHOD_HEAD
+SOUP_METHOD_PUT
+SOUP_METHOD_POST
+SOUP_METHOD_DELETE
+SOUP_METHOD_TRACE
+SOUP_METHOD_CONNECT
+<SUBSECTION>
+SOUP_METHOD_PROPFIND
+SOUP_METHOD_PROPPATCH
+SOUP_METHOD_MKCOL
+SOUP_METHOD_COPY
+SOUP_METHOD_MOVE
+SOUP_METHOD_LOCK
+SOUP_METHOD_UNLOCK
+</SECTION>
+
+<SECTION>
+<FILE>soup-message-headers</FILE>
+<TITLE>SoupMessageHeaders</TITLE>
+SoupMessageHeaders
+SoupMessageHeadersType
+soup_message_headers_new
+soup_message_headers_free
+<SUBSECTION>
+soup_message_headers_append
+soup_message_headers_replace
+soup_message_headers_remove
+soup_message_headers_clear
+soup_message_headers_get_one
+soup_message_headers_get_list
+soup_message_headers_get
+<SUBSECTION>
+SoupMessageHeadersForeachFunc
+soup_message_headers_foreach
+<SUBSECTION>
+SoupMessageHeadersIter
+soup_message_headers_iter_init
+soup_message_headers_iter_next
+<SUBSECTION>
+SoupEncoding
+soup_message_headers_get_encoding
+soup_message_headers_set_encoding
+soup_message_headers_get_content_length
+soup_message_headers_set_content_length
+<SUBSECTION>
+SoupExpectation
+soup_message_headers_get_expectations
+soup_message_headers_set_expectations
+<SUBSECTION>
+soup_message_headers_get_content_type
+soup_message_headers_set_content_type
+<SUBSECTION>
+soup_message_headers_get_content_disposition
+soup_message_headers_set_content_disposition
+<SUBSECTION>
+SoupRange
+soup_message_headers_get_ranges
+soup_message_headers_set_ranges
+soup_message_headers_set_range
+soup_message_headers_free_ranges
+soup_message_headers_get_content_range
+soup_message_headers_set_content_range
+<SUBSECTION Standard>
+SOUP_TYPE_MESSAGE_HEADERS
+soup_message_headers_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-message-body</FILE>
+<TITLE>SoupMessageBody</TITLE>
+SoupBuffer
+SoupMemoryUse
+soup_buffer_new
+soup_buffer_new_subbuffer
+soup_buffer_new_with_owner
+soup_buffer_get_owner
+soup_buffer_copy
+soup_buffer_free
+<SUBSECTION>
+SoupMessageBody
+soup_message_body_new
+soup_message_body_free
+<SUBSECTION>
+soup_message_body_set_accumulate
+soup_message_body_get_accumulate
+<SUBSECTION>
+soup_message_body_append
+soup_message_body_append_buffer
+soup_message_body_truncate
+soup_message_body_complete
+soup_message_body_flatten
+soup_message_body_get_chunk
+<SUBSECTION>
+soup_message_body_got_chunk
+soup_message_body_wrote_chunk
+<SUBSECTION Standard>
+SOUP_TYPE_BUFFER
+soup_buffer_get_type
+SOUP_TYPE_MESSAGE_BODY
+soup_message_body_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-status</FILE>
+SOUP_STATUS_IS_TRANSPORT_ERROR
+SOUP_STATUS_IS_INFORMATIONAL
+SOUP_STATUS_IS_SUCCESSFUL
+SOUP_STATUS_IS_REDIRECTION
+SOUP_STATUS_IS_CLIENT_ERROR
+SOUP_STATUS_IS_SERVER_ERROR
+SoupKnownStatusCode
+soup_status_get_phrase
+soup_status_proxify
+<SUBSECTION>
+SOUP_HTTP_ERROR
+<SUBSECTION Private>
+soup_http_error_quark
+</SECTION>
+
+<SECTION>
+<FILE>soup-server</FILE>
+<TITLE>SoupServer</TITLE>
+SoupServer
+soup_server_new
+soup_server_is_https
+soup_server_get_port
+soup_server_get_listener
+soup_server_run
+soup_server_run_async
+soup_server_quit
+soup_server_get_async_context
+<SUBSECTION>
+SoupServerCallback
+soup_server_add_handler
+soup_server_remove_handler
+<SUBSECTION>
+SoupClientContext
+soup_client_context_get_socket
+soup_client_context_get_address
+soup_client_context_get_host
+soup_client_context_get_auth_domain
+soup_client_context_get_auth_user
+<SUBSECTION>
+soup_server_add_auth_domain
+soup_server_remove_auth_domain
+<SUBSECTION>
+soup_server_pause_message
+soup_server_unpause_message
+<SUBSECTION>
+SOUP_SERVER_PORT
+SOUP_SERVER_INTERFACE
+SOUP_SERVER_SSL_CERT_FILE
+SOUP_SERVER_SSL_KEY_FILE
+SOUP_SERVER_ASYNC_CONTEXT
+SOUP_SERVER_RAW_PATHS
+SOUP_SERVER_SERVER_HEADER
+<SUBSECTION Standard>
+SOUP_SERVER
+SOUP_IS_SERVER
+SOUP_TYPE_SERVER
+soup_server_get_type
+SOUP_SERVER_CLASS
+SOUP_IS_SERVER_CLASS
+SOUP_SERVER_GET_CLASS
+SoupServerClass
+SOUP_TYPE_CLIENT_CONTEXT
+soup_client_context_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-auth-domain</FILE>
+<TITLE>SoupAuthDomain</TITLE>
+SoupAuthDomain
+<SUBSECTION>
+soup_auth_domain_add_path
+soup_auth_domain_remove_path
+SoupAuthDomainFilter
+soup_auth_domain_set_filter
+soup_auth_domain_get_realm
+<SUBSECTION>
+SoupAuthDomainGenericAuthCallback
+soup_auth_domain_set_generic_auth_callback
+soup_auth_domain_check_password
+<SUBSECTION>
+soup_auth_domain_covers
+soup_auth_domain_accepts
+soup_auth_domain_challenge
+<SUBSECTION>
+SOUP_AUTH_DOMAIN_REALM
+SOUP_AUTH_DOMAIN_PROXY
+SOUP_AUTH_DOMAIN_ADD_PATH
+SOUP_AUTH_DOMAIN_REMOVE_PATH
+SOUP_AUTH_DOMAIN_FILTER
+SOUP_AUTH_DOMAIN_FILTER_DATA
+SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK
+SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA
+<SUBSECTION Private>
+soup_auth_domain_try_generic_auth_callback
+<SUBSECTION Standard>
+SOUP_AUTH_DOMAIN
+SOUP_IS_AUTH_DOMAIN
+SOUP_TYPE_AUTH_DOMAIN
+soup_auth_domain_get_type
+SOUP_AUTH_DOMAIN_CLASS
+SOUP_IS_AUTH_DOMAIN_CLASS
+SOUP_AUTH_DOMAIN_GET_CLASS
+SoupAuthDomainClass
+</SECTION>
+
+<SECTION>
+<FILE>soup-auth-domain-basic</FILE>
+<TITLE>SoupAuthDomainBasic</TITLE>
+SoupAuthDomainBasic
+soup_auth_domain_basic_new
+<SUBSECTION>
+SoupAuthDomainBasicAuthCallback
+soup_auth_domain_basic_set_auth_callback
+<SUBSECTION>
+SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK
+SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA
+<SUBSECTION Standard>
+SOUP_AUTH_DOMAIN_BASIC
+SOUP_IS_AUTH_DOMAIN_BASIC
+SOUP_TYPE_AUTH_DOMAIN_BASIC
+soup_auth_domain_basic_get_type
+SOUP_AUTH_DOMAIN_BASIC_CLASS
+SOUP_IS_AUTH_DOMAIN_BASIC_CLASS
+SOUP_AUTH_DOMAIN_BASIC_GET_CLASS
+SoupAuthDomainBasicClass
+</SECTION>
+
+<SECTION>
+<FILE>soup-auth-domain-digest</FILE>
+<TITLE>SoupAuthDomainDigest</TITLE>
+SoupAuthDomainDigest
+soup_auth_domain_digest_new
+<SUBSECTION>
+SoupAuthDomainDigestAuthCallback
+soup_auth_domain_digest_set_auth_callback
+soup_auth_domain_digest_encode_password
+<SUBSECTION>
+SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK
+SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA
+<SUBSECTION Standard>
+SOUP_AUTH_DOMAIN_DIGEST
+SOUP_IS_AUTH_DOMAIN_DIGEST
+SOUP_TYPE_AUTH_DOMAIN_DIGEST
+soup_auth_domain_digest_get_type
+SOUP_AUTH_DOMAIN_DIGEST_CLASS
+SOUP_IS_AUTH_DOMAIN_DIGEST_CLASS
+SOUP_AUTH_DOMAIN_DIGEST_GET_CLASS
+SoupAuthDomainDigestClass
+</SECTION>
+
+<SECTION>
+<FILE>soup-address</FILE>
+<TITLE>SoupAddress</TITLE>
+SoupAddress
+SoupAddressFamily
+SOUP_ADDRESS_ANY_PORT
+soup_address_new
+soup_address_new_from_sockaddr
+soup_address_new_any
+<SUBSECTION>
+SoupAddressCallback
+soup_address_resolve_async
+soup_address_resolve_sync
+soup_address_is_resolved
+<SUBSECTION>
+soup_address_get_name
+soup_address_get_sockaddr
+soup_address_get_physical
+soup_address_get_port
+<SUBSECTION>
+soup_address_equal_by_name
+soup_address_hash_by_name
+soup_address_equal_by_ip
+soup_address_hash_by_ip
+<SUBSECTION>
+SOUP_ADDRESS_FAMILY
+SOUP_ADDRESS_NAME
+SOUP_ADDRESS_PHYSICAL
+SOUP_ADDRESS_PORT
+SOUP_ADDRESS_SOCKADDR
+<SUBSECTION Standard>
+SOUP_ADDRESS
+SOUP_IS_ADDRESS
+SOUP_TYPE_ADDRESS
+soup_address_get_type
+SOUP_ADDRESS_CLASS
+SOUP_IS_ADDRESS_CLASS
+SOUP_ADDRESS_GET_CLASS
+SoupAddressClass
+<SUBSECTION Private>
+AF_INET6
+</SECTION>
+
+<SECTION>
+<FILE>soup-session</FILE>
+<TITLE>SoupSession</TITLE>
+SoupSession
+<SUBSECTION>
+SoupSessionCallback
+soup_session_queue_message
+soup_session_requeue_message
+soup_session_send_message
+soup_session_cancel_message
+soup_session_abort
+<SUBSECTION>
+soup_session_pause_message
+soup_session_unpause_message
+<SUBSECTION>
+soup_session_get_async_context
+<SUBSECTION>
+soup_session_add_feature
+soup_session_add_feature_by_type
+soup_session_remove_feature
+soup_session_remove_feature_by_type
+soup_session_get_features
+soup_session_get_feature
+soup_session_get_feature_for_message
+<SUBSECTION>
+SOUP_SESSION_PROXY_URI
+SOUP_SESSION_MAX_CONNS
+SOUP_SESSION_MAX_CONNS_PER_HOST
+SOUP_SESSION_USE_NTLM
+SOUP_SESSION_SSL_CA_FILE
+SOUP_SESSION_ASYNC_CONTEXT
+SOUP_SESSION_TIMEOUT
+SOUP_SESSION_IDLE_TIMEOUT
+SOUP_SESSION_USER_AGENT
+SOUP_SESSION_ADD_FEATURE
+SOUP_SESSION_ADD_FEATURE_BY_TYPE
+SOUP_SESSION_REMOVE_FEATURE_BY_TYPE
+<SUBSECTION Standard>
+SOUP_IS_SESSION
+SOUP_IS_SESSION_CLASS
+SOUP_SESSION
+SOUP_SESSION_CLASS
+SOUP_SESSION_GET_CLASS
+SOUP_TYPE_SESSION
+SoupSessionClass
+soup_session_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-session-async</FILE>
+<TITLE>SoupSessionAsync</TITLE>
+SoupSessionAsync
+soup_session_async_new
+soup_session_async_new_with_options
+<SUBSECTION Standard>
+SOUP_IS_SESSION_ASYNC
+SOUP_IS_SESSION_ASYNC_CLASS
+SOUP_SESSION_ASYNC
+SOUP_SESSION_ASYNC_CLASS
+SOUP_SESSION_ASYNC_GET_CLASS
+SOUP_TYPE_SESSION_ASYNC
+SoupSessionAsyncClass
+soup_session_async_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-session-sync</FILE>
+<TITLE>SoupSessionSync</TITLE>
+SoupSessionSync
+soup_session_sync_new
+soup_session_sync_new_with_options
+<SUBSECTION Standard>
+SOUP_IS_SESSION_SYNC
+SOUP_IS_SESSION_SYNC_CLASS
+SOUP_SESSION_SYNC
+SOUP_SESSION_SYNC_CLASS
+SOUP_SESSION_SYNC_GET_CLASS
+SOUP_TYPE_SESSION_SYNC
+SoupSessionSyncClass
+soup_session_sync_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-session-feature</FILE>
+<TITLE>SoupSessionFeature</TITLE>
+SoupSessionFeature
+SoupSessionFeatureInterface
+<SUBSECTION Standard>
+soup_session_feature_get_type
+SOUP_SESSION_FEATURE
+SOUP_SESSION_FEATURE_CLASS
+SOUP_SESSION_FEATURE_GET_CLASS
+SOUP_IS_SESSION_FEATURE
+SOUP_IS_SESSION_FEATURE_CLASS
+SOUP_TYPE_SESSION_FEATURE
+<SUBSECTION Private>
+soup_session_feature_attach
+soup_session_feature_detach
+</SECTION>
+
+<SECTION>
+<FILE>soup-auth</FILE>
+<TITLE>SoupAuth</TITLE>
+SoupAuth
+soup_auth_new
+soup_auth_update
+<SUBSECTION>
+soup_auth_is_for_proxy
+soup_auth_get_scheme_name
+soup_auth_get_host
+soup_auth_get_realm
+soup_auth_get_info
+<SUBSECTION>
+soup_auth_authenticate
+soup_auth_is_authenticated
+<SUBSECTION>
+soup_auth_get_authorization
+soup_auth_get_protection_space
+soup_auth_free_protection_space
+<SUBSECTION>
+SOUP_AUTH_SCHEME_NAME
+SOUP_AUTH_REALM
+SOUP_AUTH_HOST
+SOUP_AUTH_IS_FOR_PROXY
+SOUP_AUTH_IS_AUTHENTICATED
+<SUBSECTION Standard>
+SOUP_AUTH
+SOUP_IS_AUTH
+SOUP_TYPE_AUTH
+soup_auth_get_type
+SOUP_AUTH_CLASS
+SOUP_IS_AUTH_CLASS
+SOUP_AUTH_GET_CLASS
+SoupAuthClass
+</SECTION>
+
+<SECTION>
+<FILE>soup-socket</FILE>
+<TITLE>SoupSocket</TITLE>
+SoupSocket
+soup_socket_new
+<SUBSECTION>
+SoupSocketCallback
+soup_socket_connect_async
+soup_socket_connect_sync
+<SUBSECTION>
+soup_socket_listen
+<SUBSECTION>
+soup_socket_start_ssl
+soup_socket_start_proxy_ssl
+soup_socket_is_ssl
+<SUBSECTION>
+soup_socket_disconnect
+soup_socket_is_connected
+<SUBSECTION>
+soup_socket_get_local_address
+soup_socket_get_remote_address
+<SUBSECTION>
+SoupSocketIOStatus
+soup_socket_read
+soup_socket_read_until
+soup_socket_write
+<SUBSECTION>
+SOUP_SSL_ERROR
+SoupSSLError
+<SUBSECTION>
+SOUP_SOCKET_LOCAL_ADDRESS
+SOUP_SOCKET_REMOTE_ADDRESS
+SOUP_SOCKET_FLAG_NONBLOCKING
+SOUP_SOCKET_IS_SERVER
+SOUP_SOCKET_SSL_CREDENTIALS
+SOUP_SOCKET_ASYNC_CONTEXT
+SOUP_SOCKET_TIMEOUT
+<SUBSECTION Standard>
+SOUP_SOCKET
+SOUP_IS_SOCKET
+SOUP_TYPE_SOCKET
+soup_socket_get_type
+SOUP_SOCKET_CLASS
+SOUP_IS_SOCKET_CLASS
+SOUP_SOCKET_GET_CLASS
+SoupSocketClass
+<SUBSECTION Private>
+soup_ssl_error_quark
+</SECTION>
+
+<SECTION>
+<FILE>soup-uri</FILE>
+<TITLE>SoupURI</TITLE>
+SoupURI
+soup_uri_new_with_base
+soup_uri_new
+soup_uri_to_string
+<SUBSECTION>
+soup_uri_copy
+soup_uri_equal
+soup_uri_free
+<SUBSECTION>
+soup_uri_encode
+soup_uri_decode
+soup_uri_normalize
+<SUBSECTION>
+SOUP_URI_SCHEME_HTTP
+SOUP_URI_SCHEME_HTTPS
+soup_uri_uses_default_port
+SOUP_URI_VALID_FOR_HTTP
+<SUBSECTION>
+soup_uri_set_scheme
+soup_uri_set_user
+soup_uri_set_password
+soup_uri_set_host
+soup_uri_set_port
+soup_uri_set_path
+soup_uri_set_query
+soup_uri_set_query_from_form
+soup_uri_set_query_from_fields
+soup_uri_set_fragment
+<SUBSECTION Standard>
+SOUP_TYPE_URI
+soup_uri_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-misc</FILE>
+<TITLE>Soup Miscellaneous Utilities</TITLE>
+SoupDate
+SoupDateFormat
+soup_date_new
+soup_date_new_from_string
+soup_date_new_from_time_t
+soup_date_new_from_now
+soup_date_to_string
+soup_date_to_time_t
+soup_date_to_timeval
+soup_date_is_past
+soup_date_free
+<SUBSECTION>
+soup_headers_parse_request
+soup_headers_parse_response
+soup_headers_parse_status_line
+soup_headers_parse
+<SUBSECTION>
+soup_header_parse_list
+soup_header_parse_quality_list
+soup_header_free_list
+soup_header_contains
+soup_header_parse_param_list
+soup_header_parse_semi_param_list
+soup_header_free_param_list
+soup_header_g_string_append_param
+<SUBSECTION>
+soup_str_case_equal
+soup_str_case_hash
+<SUBSECTION>
+soup_add_completion
+soup_add_idle
+soup_add_io_watch
+soup_add_timeout
+<SUBSECTION>
+soup_ssl_supported
+<SUBSECTION Private>
+soup_date_copy
+SOUP_TYPE_DATE
+soup_date_get_type
+soup_form_decode_urlencoded
+soup_form_encode_urlencoded
+soup_form_encode_urlencoded_list
+</SECTION>
+
+<SECTION>
+<FILE>soup-forms</FILE>
+<TITLE>HTML Form Support</TITLE>
+<SUBSECTION>
+SOUP_FORM_MIME_TYPE_MULTIPART
+SOUP_FORM_MIME_TYPE_URLENCODED
+soup_form_decode
+soup_form_decode_multipart
+soup_form_encode
+soup_form_encode_datalist
+soup_form_encode_hash
+soup_form_encode_valist
+soup_form_request_new
+soup_form_request_new_from_datalist
+soup_form_request_new_from_hash
+soup_form_request_new_from_multipart
+</SECTION>
+
+<SECTION>
+<FILE>soup-xmlrpc</FILE>
+<TITLE>XMLRPC Support</TITLE>
+<SUBSECTION>
+soup_xmlrpc_build_method_call
+soup_xmlrpc_request_new
+soup_xmlrpc_parse_method_response
+soup_xmlrpc_extract_method_response
+<SUBSECTION>
+soup_xmlrpc_parse_method_call
+soup_xmlrpc_extract_method_call
+soup_xmlrpc_build_method_response
+soup_xmlrpc_build_fault
+soup_xmlrpc_set_response
+soup_xmlrpc_set_fault
+<SUBSECTION>
+SOUP_XMLRPC_ERROR
+SoupXMLRPCError
+SOUP_XMLRPC_FAULT
+SoupXMLRPCFault
+<SUBSECTION Private>
+soup_xmlrpc_error_quark
+soup_xmlrpc_fault_quark
+</SECTION>
+
+<SECTION>
+<FILE>soup-value-utils</FILE>
+<TITLE>GValue Support</TITLE>
+soup_value_hash_new
+soup_value_hash_new_with_vals
+soup_value_hash_insert_value
+soup_value_hash_insert
+soup_value_hash_insert_vals
+soup_value_hash_lookup
+soup_value_hash_lookup_vals
+<SUBSECTION>
+soup_value_array_from_args
+soup_value_array_to_args
+soup_value_array_new
+soup_value_array_new_with_vals
+soup_value_array_insert
+soup_value_array_append
+soup_value_array_append_vals
+soup_value_array_get_nth
+<SUBSECTION>
+SOUP_VALUE_SETV
+SOUP_VALUE_GETV
+<SUBSECTION>
+SOUP_TYPE_BYTE_ARRAY
+<SUBSECTION Private>
+soup_byte_array_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-logger</FILE>
+<TITLE>SoupLogger</TITLE>
+SoupLogger
+SoupLoggerLogLevel
+soup_logger_new
+soup_logger_attach
+soup_logger_detach
+<SUBSECTION>
+SoupLoggerFilter
+soup_logger_set_request_filter
+soup_logger_set_response_filter
+<SUBSECTION>
+SoupLoggerPrinter
+soup_logger_set_printer
+<SUBSECTION Standard>
+SoupLoggerClass
+soup_logger_get_type
+SOUP_IS_LOGGER
+SOUP_IS_LOGGER_CLASS
+SOUP_LOGGER
+SOUP_LOGGER_CLASS
+SOUP_LOGGER_GET_CLASS
+SOUP_TYPE_LOGGER
+</SECTION>
+
+<SECTION>
+<FILE>soup-cookie</FILE>
+<TITLE>SoupCookie</TITLE>
+SoupCookie
+soup_cookie_new
+soup_cookie_parse
+soup_cookie_copy
+soup_cookie_equal
+soup_cookie_free
+<SUBSECTION>
+soup_cookie_set_name
+soup_cookie_set_value
+soup_cookie_set_domain
+soup_cookie_set_path
+soup_cookie_set_max_age
+SOUP_COOKIE_MAX_AGE_ONE_HOUR
+SOUP_COOKIE_MAX_AGE_ONE_DAY
+SOUP_COOKIE_MAX_AGE_ONE_WEEK
+SOUP_COOKIE_MAX_AGE_ONE_YEAR
+soup_cookie_set_expires
+soup_cookie_set_secure
+soup_cookie_set_http_only
+<SUBSECTION>
+soup_cookie_applies_to_uri
+<SUBSECTION>
+soup_cookie_to_cookie_header
+soup_cookie_to_set_cookie_header
+<SUBSECTION>
+soup_cookies_from_request
+soup_cookies_from_response
+soup_cookies_to_request
+soup_cookies_to_response
+soup_cookies_to_cookie_header
+soup_cookies_free
+<SUBSECTION Standard>
+SOUP_TYPE_COOKIE
+soup_cookie_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-cookie-jar</FILE>
+<TITLE>SoupCookieJar</TITLE>
+SoupCookieJar
+soup_cookie_jar_new
+soup_cookie_jar_save
+soup_cookie_jar_get_cookies
+soup_cookie_jar_set_cookie
+<SUBSECTION>
+soup_cookie_jar_add_cookie
+soup_cookie_jar_delete_cookie
+soup_cookie_jar_all_cookies
+<SUBSECTION>
+SOUP_COOKIE_JAR_READ_ONLY
+<SUBSECTION Standard>
+SoupCookieJarClass
+SOUP_COOKIE_JAR
+SOUP_COOKIE_JAR_CLASS
+SOUP_COOKIE_JAR_GET_CLASS
+SOUP_IS_COOKIE_JAR
+SOUP_IS_COOKIE_JAR_CLASS
+SOUP_TYPE_COOKIE_JAR
+soup_cookie_jar_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-multipart</FILE>
+<TITLE>SoupMultipart</TITLE>
+SoupMultipart
+soup_multipart_new
+soup_multipart_new_from_message
+soup_multipart_free
+<SUBSECTION>
+soup_multipart_get_length
+soup_multipart_get_part
+soup_multipart_append_part
+soup_multipart_append_form_string
+soup_multipart_append_form_file
+soup_multipart_to_message
+<SUBSECTION Standard>
+SOUP_TYPE_MULTIPART
+soup_multipart_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-cookie-jar-text</FILE>
+<TITLE>SoupCookieJarText</TITLE>
+SoupCookieJarText
+soup_cookie_jar_text_new
+<SUBSECTION>
+SOUP_COOKIE_JAR_TEXT_FILENAME
+<SUBSECTION Standard>
+SoupCookieJarTextClass
+SOUP_COOKIE_JAR_TEXT
+SOUP_COOKIE_JAR_TEXT_CLASS
+SOUP_COOKIE_JAR_TEXT_GET_CLASS
+SOUP_TYPE_COOKIE_JAR_TEXT
+SOUP_IS_COOKIE_JAR_TEXT
+SOUP_IS_COOKIE_JAR_TEXT_CLASS
+soup_cookie_jar_text_get_type
+</SECTION>
+
+<SECTION>
+<INCLUDE>libsoup/soup-gnome.h</INCLUDE>
+<FILE>soup-cookie-jar-sqlite</FILE>
+<TITLE>SoupCookieJarSqlite</TITLE>
+SoupCookieJarSqlite
+soup_cookie_jar_sqlite_new
+<SUBSECTION>
+SOUP_COOKIE_JAR_SQLITE_FILENAME
+<SUBSECTION Standard>
+SoupCookieJarSqliteClass
+SOUP_COOKIE_JAR_SQLITE
+SOUP_COOKIE_JAR_SQLITE_CLASS
+SOUP_COOKIE_JAR_SQLITE_GET_CLASS
+SOUP_TYPE_COOKIE_JAR_SQLITE
+SOUP_IS_COOKIE_JAR_SQLITE
+SOUP_IS_COOKIE_JAR_SQLITE_CLASS
+soup_cookie_jar_sqlite_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-proxy-uri-resolver</FILE>
+<TITLE>SoupProxyURIResolver</TITLE>
+SoupProxyURIResolver
+SoupProxyURIResolverCallback
+soup_proxy_uri_resolver_get_proxy_uri_async
+soup_proxy_uri_resolver_get_proxy_uri_sync
+<SUBSECTION Standard>
+SoupProxyURIResolverInterface
+SOUP_IS_PROXY_URI_RESOLVER
+SOUP_IS_PROXY_URI_RESOLVER_CLASS
+SOUP_PROXY_URI_RESOLVER
+SOUP_PROXY_URI_RESOLVER_CLASS
+SOUP_PROXY_URI_RESOLVER_GET_CLASS
+SOUP_TYPE_PROXY_URI_RESOLVER
+soup_proxy_uri_resolver_get_type
+</SECTION>
+
+<SECTION>
+<INCLUDE>libsoup/soup-gnome.h</INCLUDE>
+<FILE>soup-gnome-features</FILE>
+SOUP_TYPE_GNOME_FEATURES_2_26
+SOUP_TYPE_PROXY_RESOLVER_GNOME
+<SUBSECTION Standard>
+soup_proxy_resolver_gnome_get_type
+soup_gnome_features_2_26_get_type
+</SECTION>
+
+<SECTION>
+<FILE>soup-content-sniffer</FILE>
+<TITLE>SoupContentSniffer</TITLE>
+SoupContentSniffer
+soup_content_sniffer_new
+soup_content_sniffer_sniff
+<SUBSECTION Standard>
+SOUP_CONTENT_SNIFFER
+SOUP_CONTENT_SNIFFER_CLASS
+SOUP_CONTENT_SNIFFER_GET_CLASS
+SOUP_IS_CONTENT_SNIFFER
+SOUP_IS_CONTENT_SNIFFER_CLASS
+SOUP_TYPE_CONTENT_SNIFFER
+SoupContentSnifferClass
+SoupContentSnifferPrivate
+soup_content_sniffer_get_type
+</SECTION>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<refentry id="libsoup-porting-2.2-2.4">
+<refmeta>
+<refentrytitle>libsoup 2.2 to 2.4 porting notes</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBSOUP Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Porting notes</refname><refpurpose>Notes on porting from libsoup 2.2 to 2.4</refpurpose>
+</refnamediv>
+
+<refsect2>
+<title>Overview</title>
+
+<para>
+After many API-compatible releases in the 2.2 series,
+<application>libsoup</application> has now changed its API and bumped
+its version number to 2.4. Changes were made for a variety of reasons:
+</para>
+
+<itemizedlist>
+ <listitem><para>
+ To fix bugs and add features that couldn't be done ABI-compatibly.
+ </para></listitem>
+
+ <listitem><para>
+ To make it easier to generate bindings for libsoup for
+ languages other than C.
+ </para></listitem>
+
+ <listitem><para>
+ To clean up ugly/confusing old APIs
+ </para></listitem>
+
+ <listitem><para>
+ To be more glib/gobject/gtk-like in general.
+ </para></listitem>
+</itemizedlist>
+
+</refsect2>
+
+<refsect2>
+<title>SoupMessage</title>
+
+<para>
+<link linkend="SoupMessage"><type>SoupMessage</type></link> has had a
+number of API changes made, mostly to increase its
+language-bindability.
+</para>
+
+<refsect3>
+<title>SoupMessageHeaders</title>
+
+<para>
+ <type>SoupMessage</type>'s
+ <structfield>request_headers</structfield> and
+ <structfield>response_headers</structfield> fields are now an
+ opaque type (<link
+ linkend="SoupMessageHeaders"><type>SoupMessageHeaders</type></link>)
+ rather than being <type>GHashTables</type>. The method names have
+ changed slightly to reflect this:
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><function>soup_message_add_header</function></term>
+ <listitem><para>
+ → <link linkend="soup-message-headers-append"><function>soup_message_headers_append</function></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><function>soup_message_get_header</function></term>
+ <listitem><para>
+ → <link linkend="soup-message-headers-get"><function>soup_message_headers_get</function></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><function>soup_message_foreach_header</function></term>
+ <listitem><para>
+ → <link linkend="soup-message-headers-foreach"><function>soup_message_headers_foreach</function></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><function>soup_message_remove_header</function></term>
+ <listitem><para>
+ → <link linkend="soup-message-headers-remove"><function>soup_message_headers_remove</function></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><function>soup_message_clear_headers</function></term>
+ <listitem><para>
+ → <link linkend="soup-message-headers-clear"><function>soup_message_headers_clear</function></link>
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+ <function>soup_message_get_header_list</function> has no equivalent;
+ if multiple copies of a header are present,
+ <function>soup_message_headers_get</function> will return all of
+ them, concatenated together and separated by commas; RFC 2616 says
+ that the two forms (multiple headers, and a single header with
+ comma-separated values) are equivalent; this change to libsoup
+ ensures that applications will treat them as equivalent.
+</para>
+
+<para>
+ In addition, certain important header fields now have
+ dedicated get/set methods:
+</para>
+
+<itemizedlist>
+ <listitem><para>
+ <link linkend="soup-message-headers-get-encoding"><function>soup_message_headers_get_encoding</function></link> / <link linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>
+ </para></listitem>
+
+ <listitem><para>
+ <link linkend="soup-message-headers-get-content-length"><function>soup_message_headers_get_content_length</function></link> / <link linkend="soup-message-headers-set-content-length"><function>soup_message_headers_set_content_length</function></link>
+ </para></listitem>
+
+ <listitem><para>
+ <link linkend="soup-message-headers-get-expectations"><function>soup_message_headers_get_expectations</function></link> / <link linkend="soup-message-headers-set-expectations"><function>soup_message_headers_set_expectations</function></link>
+ </para></listitem>
+</itemizedlist>
+
+<para>
+ (<literal>soup_message_headers_set_expectation(msg, SOUP_EXPECTATION_CONTINUE)</literal>
+ replaces the <literal>SOUP_MESSAGE_EXPECT_CONTINUE</literal>
+ message flag).
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>SoupMessageBody</title>
+
+<para>
+ Similarly, the <structfield>request_body</structfield> and
+ <structfield>response</structfield> fields (renamed from
+ <structfield>request</structfield> and <structfield>response</structfield>) are
+ now a new type, <link
+ linkend="SoupMessageBody"><type>SoupMessageBody</type></link>,
+ implemented in terms of <link
+ linkend="SoupBuffer"><type>SoupBuffer</type></link>, a refcounted
+ memory buffer type with clearer semantics than the old
+ <type>SoupDataBuffer</type>/<type>SoupOwnership</type>.
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><literal>SOUP_BUFFER_STATIC</literal></term>
+ <listitem><para>
+ → <link
+ linkend="SOUP-MEMORY-STATIC:CAPS"><literal>SOUP_MEMORY_STATIC</literal></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>SOUP_BUFFER_SYSTEM_OWNED</literal></term>
+ <listitem><para>
+ → <link
+ linkend="SOUP-MEMORY-TAKE:CAPS"><literal>SOUP_MEMORY_TAKE</literal></link>
+ (meaning <application>libsoup</application>
+ should take ownership of the memory from your).
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>SOUP_BUFFER_USER_OWNED</literal></term>
+ <listitem><para>
+ → <link
+ linkend="SOUP-MEMORY-COPY:CAPS"><literal>SOUP_MEMORY_COPY</literal></link>
+ (meaning <application>libsoup</application>
+ should make a copy of the memory, because you
+ can't make any guarantees about how long it will
+ last.)
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+ A fourth <type>SoupMemoryUse</type> value is also available: <link
+ linkend="SOUP-MEMORY-TEMPORARY:CAPS"><literal>SOUP_MEMORY_TEMPORARY</literal></link>,
+ which helps to avoid extra copies in some cases.
+ <literal>SOUP_MEMORY_TEMPORARY</literal> means that the memory
+ will last at least as long as the object you are handing it to (a
+ <type>SoupBuffer</type>, <type>SoupMessageBody</type>, or
+ <type>SoupMessage</type>), and so doesn't need to be copied right
+ away, but that if anyone makes a copy of the buffer,
+ <application>libsoup</application> needs to make a new copy of the
+ memory for them at that point, since the original pointer may not
+ remain valid for the lifetime of the new copy.
+</para>
+
+<para>
+ (In the future, there may be additional <type>SoupBuffer</type>
+ and <type>SoupMessageBody</type> methods to work directly with
+ mmapped memory, splicing to file descriptors, etc.)
+</para>
+
+<para>
+ <link
+ linkend="soup-message-set-request"><function>soup_message_set_request</function></link>
+ and <link
+ linkend="soup-message-set-response"><function>soup_message_set_response</function></link>
+ still work roughly like they used to.
+</para>
+
+<para>
+ Unlike the old <structfield>request</structfield> and
+ <structfield>response</structfield> fields, the new
+ <structfield>request_body</structfield> and
+ <structfield>response_body</structfield> fields are not guaranteed
+ to be filled in at all times. (In particular, the
+ <structfield>response_body</structfield> is not filled in until it
+ has been fully read, although you can use <link
+ linkend="soup-message-body-get-chunk"><function>soup_message_body_get_chunk</function></link>
+ to iterate through the chunks before that point if you need to.)
+</para>
+
+<para>
+ When <structfield>request_body</structfield> and
+ <structfield>response_body</structfield> <emphasis>are</emphasis>
+ filled in, they are <literal>'\0'</literal>-terminated for your
+ processing convenience. (The terminating 0 byte is not included in
+ their length.)
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>Chunked encoding</title>
+
+<para>
+ The prototype of the <link
+ linkend="SoupMessage-got-chunk"><literal>SoupMessage::got_chunk</literal></link>
+ signal has been changed; it now includes the chunk as a
+ <type>SoupBuffer</type> parameter (rather than storing the chunk
+ data in <literal>msg->response</literal> as in 2.2). <link
+ linkend="SoupMessageFlags"><literal>SOUP_MESSAGE_OVERWRITE_CHUNKS</literal></link>
+ is now somewhat poorly named, but still has essentially the same
+ semantics: if you set it, each chunk will be discarded after it is
+ read, and <literal>msg->response_body</literal> will not be filled
+ in with the complete response at the end of message processing.
+</para>
+
+<para>
+ The API for sending chunked responses from a
+ <type>SoupServer</type> is also slightly different now:
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><function>soup_server_message_set_encoding</function></term>
+ <listitem><para>
+ → <link
+ linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><function>soup_message_add_chunk</function></term>
+ <listitem><para>
+ → <link
+ linkend="soup-message-body-append"><function>soup_message_body_append</function></link>
+ or <link
+ linkend="soup-message-body-append-buffer"><function>soup_message_body_append_buffer</function></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><function>soup_message_add_final_chunk</function></term>
+ <listitem><para>
+ → <link
+ linkend="soup-message-body-complete"><function>soup_message_body_complete</function></link>
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+ Since the new chunk-sending APIs require you to explicitly pass
+ the
+ <structfield>request_headers</structfield>/<structfield>request_body</structfield>
+ fields, rather than just assuming you're talking about the
+ response body, in theory it is now possible to use chunked
+ encoding with the request as well. As of the 2.3.0 release this
+ has not yet been tested.
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>Methods</title>
+
+<para>
+ <type>SoupMessage</type>'s
+ <structfield>method</structfield> field is now an interned
+ string, and you can compare the method directly against
+ the defines such as <link
+ linkend="SOUP-METHOD-GET:CAPS"><literal>SOUP_METHOD_GET</literal></link>
+ (eg, in a <type>SoupServer</type> request handler).
+ <function>soup_method_get_id</function> and the
+ <literal>SOUP_METHOD_ID_*</literal> macros are now gone.
+</para>
+</refsect3>
+
+<refsect3>
+<title>Handlers</title>
+
+<para>
+ <link
+ linkend="soup-message-add-header-handler"><function>soup_message_add_header_handler</function></link>
+ and <link
+ linkend="soup-message-add-status-code-handler"><function>soup_message_add_status_code_handler</function></link>
+ are now just clever wrappers around
+ <function>g_signal_connect</function>. In particular, you now pass
+ a signal name to them rather than a <type>SoupHandlerPhase</type>,
+ and you remove them with the normal signal handler remove methods.
+ However, they still retain the special behavior that if the
+ message has been cancelled or requeued when the time comes for the
+ handler to run, then the handler will be skipped. (Use plain
+ <function>g_signal_connect</function> if you don't want that
+ behavior.)
+</para>
+</refsect3>
+
+<refsect3>
+<title>I/O-related <type>SoupMessage</type> methods</title>
+
+<para>
+ <function>soup_message_io_pause</function> and
+ <function>soup_message_io_unpause</function> have been moved to
+ <type>SoupSession</type> and <type>SoupServer</type>, to better
+ reflect the fact that the session/server control the I/O, and
+ <type>SoupMessage</type> is merely acted-upon by them.
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><function>soup_message_io_pause</function></term>
+ <listitem><para>
+ → <link linkend="soup-session-pause-message"><function>soup_session_pause_message</function></link> / <link linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><function>soup_message_io_unpause</function></term>
+ <listitem><para>
+ → <link linkend="soup-session-unpause-message"><function>soup_session_unpause_message</function></link> / <link linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+ <literal>msg->status</literal> (the I/O status) is now
+ gone as well, because (a) it's really an internal state of
+ <type>SoupSession</type>, and (b) it's too easy to confuse
+ with <literal>msg->status_code</literal> (the HTTP status)
+ anyway. Code that used to check if status was
+ <literal>SOUP_MESSAGE_STATUS_FINISHED</literal> needs to
+ be rewritten to track whether or not the <link
+ linkend="SoupMessage-finished"><literal>finished</literal></link>
+ signal has been emitted.
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>HTTP-Version</title>
+
+<para>
+ <type>SoupHttpVersion</type> is now <link
+ linkend="SoupHTTPVersion"><type>SoupHTTPVersion</type></link>
+</para>
+</refsect3>
+
+</refsect2>
+
+<refsect2>
+<title>SoupSession</title>
+
+<refsect3>
+<title><function>soup_session_queue_message</function> callback</title>
+
+<para>
+ <link
+ linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>'s
+ callback parameter now includes the <type>SoupSession</type> as a
+ parameter, reflecting the fact that it is a
+ <type>SoupSession</type> callback, not a <type>SoupMessage</type>
+ callback. (It has also been renamed, from
+ <type>SoupMessageCallbackFn</type> to <link
+ linkend="SoupSessionCallback"><type>SoupSessionCallback</type></link>.)
+</para>
+</refsect3>
+
+<refsect3>
+<title>Authentication</title>
+
+<para>
+ <type>SoupSession</type>'s <literal>authenticate</literal> and
+ <literal>reauthenticate</literal> signals have been merged into a
+ single <link
+ linkend="SoupSession-authenticate"><literal>authenticate</literal></link>
+ signal with a <parameter>retrying</parameter> parameter to indicate if
+ it's the second (or later) try. Also, the signal now includes a
+ <link linkend="SoupAuth"><type>SoupAuth</type></link> directly,
+ and you authenticate by calling <link
+ linkend="soup-auth-authenticate"><function>soup_auth_authenticate</function></link>
+ on the auth (rather than passing back a username and password from
+ the signal handler).
+</para>
+</refsect3>
+
+<refsect3>
+<title><type>SoupLogger</type></title>
+
+<para>
+<link linkend="libsoup-SoupLogger"><type>SoupLogger</type></link> is a
+new object that copies the behavior of
+<application>evolution-exchange</application>'s
+<literal>E2K_DEBUG</literal> and its clones. That is, it causes a
+<type>SoupSession</type> to start logging some or all of its HTTP
+traffic to stdout, for debugging purposes.
+</para>
+</refsect3>
+
+<refsect3>
+<title><type>SoupMessageFilter</type></title>
+
+<para>
+ <type>SoupMessageFilter</type> is gone; code that used to use it
+ can now connect to the <link
+ linkend="SoupSession-request-started"><literal>SoupSession::request-started</literal></link>
+ signal to get a chance to act on each message as it is sent.
+ (This is how <type>SoupLogger</type> works.)
+</para>
+</refsect3>
+
+<refsect3>
+<title>Internal types</title>
+
+<para>
+ The <type>SoupConnection</type> and <type>SoupMessageQueue</type>
+ types (which should always have been internal to
+ <type>SoupSession</type>) have been removed from the public API.
+</para>
+</refsect3>
+
+</refsect2>
+
+<refsect2>
+<title>SoupURI</title>
+
+<type>SoupUri</type> has been renamed <link
+linkend="SoupURI"><type>SoupURI</type></link>, and its behavior has
+changed in a few ways:
+
+<itemizedlist>
+ <listitem>
+ <para>
+ It no longer fully-decodes %-encoded URI components. This
+ is necessary to ensure that complicated URIs (eg, URIs
+ that include other URIs as query parameters) can be
+ round-tripped correctly. This corresponds to the old
+ <structfield>broken_encoding</structfield> behavior, but
+ that flag no longer exists, since it is the default and
+ there's no way to turn it off.
+ </para>
+
+ <para>
+ In theory, this is an ABI-breaking change, especially for
+ <link
+ linkend="SoupServer"><type>SoupServer</type></link>s.
+ However, it is unlikely to actually break anything. (And
+ in the <type>SoupServer</type> case, servers now
+ fully-decode the <structfield>path</structfield> component
+ themselves unless you set the <link
+ linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link>
+ flag on the server, so the behavior should still be the
+ same.
+ </para>
+ </listitem>
+
+ <listitem><para>
+ It uses the RFC3986 parsing rules, including support for IPv6 literal
+ addresses.
+ </para></listitem>
+
+ <listitem>
+ <para>
+ The field formerly called
+ <structfield>protocol</structfield> is now
+ <structfield>scheme</structfield>, to match the spec, and
+ it's an interned string rather than a quark. The names of
+ the predefined values have changed to match:
+ </para>
+
+ <variablelist>
+ <varlistentry>
+ <term><function>SOUP_PROTOCOL_HTTP</function></term>
+ <listitem><para>
+ → <link linkend="SOUP-URI-SCHEME-HTTP:CAPS"><literal>SOUP_URI_SCHEME_HTTP</literal></link>
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><function>SOUP_PROTOCOL_HTTPS</function></term>
+ <listitem><para>
+ → <link linkend="SOUP-URI-SCHEME-HTTPS:CAPS"><literal>SOUP_URI_SCHEME_HTTPS</literal></link>
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+</itemizedlist>
+
+<para>
+<link linkend="soup-uri-decode"><function>soup_uri_decode</function></link>
+now returns a new string rather than modifying its input string in
+place. The new method <link
+linkend="soup-uri-normalize"><function>soup_uri_normalize</function></link>,
+which removes some, but not all, %-encoding, behaves similarly.
+</para>
+
+<para>
+Finally, <type>SoupURI</type> (as well as most other struct types in
+<application>libsoup</application>) now uses the glib "slice"
+allocator, so any code that uses <link
+linkend="g-new"><function>g_new</function></link> to create
+<type>SoupURI</type>s is wrong. If you want to create a URI "by hand",
+you can call <link
+linkend="soup-uri-new"><function>soup_uri_new</function></link>,
+passing <literal>NULL</literal>, and you will get back an empty
+<type>SoupURI</type>. There are also now methods that can be used to
+set its fields (eg, <link
+linkend="soup-uri-set-scheme"><function>soup_uri_set_scheme</function></link>,
+<link
+linkend="soup-uri-set-path"><function>soup_uri_set_path</function></link>,
+etc) rather than mucking with the fields directly.
+</para>
+
+<refsect3>
+<title>Forms</title>
+
+<para>
+Related to <type>SoupURI</type>, there are some new helper methods for
+dealing with HTML forms. <link
+linkend="soup-form-decode-urlencoded">soup_form_decode_urlencoded</link>
+decodes a URI <structfield>query</structfield> component (or an
+<literal>application/x-www-form-urlencoded</literal> request body)
+into a <type>GHashTable</type>. <link
+linkend="soup-form-encode-urlencoded">soup_form_encode_urlencoded</link>
+reverses the process, allowing you to fill in a
+<literal>uri->query</literal> with a properly-encoded form dataset.
+(<type>SoupURI</type> also provides <link
+linkend="soup-uri-set-query-from-form"><function>soup_uri_set_query_from_form</function></link>
+to help with this.)
+</para>
+</refsect3>
+
+</refsect2>
+
+
+<refsect2>
+<title>XML-RPC and SOAP</title>
+
+<refsect3>
+<title>SOAP</title>
+<para>
+SOAP support has been removed; the existing methods covered only a
+teeny tiny subset of SOAP, which was really only useful to a single
+application. (The code that was formerly in libsoup has been moved to
+that application.). If you were using this code, you can resurrect a
+libsoup-2.4-compatible version of it from revision 1016 of libsoup
+svn.
+</para>
+</refsect3>
+
+<refsect3>
+<title>XML-RPC</title>
+<para>
+The XML-RPC code has been completely rewritten to make it simpler to
+implement XML-RPC clients and servers. (Note: the server-side code has
+not been heavily tested yet.) The <link
+linkend="libsoup-XMLRPC-Support">new XML-RPC API</link> makes use of
+<type>GValue</type>s, with the following type mappings:
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><literal>int</literal></term>
+ <listitem><para>
+ → <type>int</type> (<link linkend="G-TYPE-INT:CAPS"><literal>G_TYPE_INT</literal></link>)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>boolean</literal></term>
+ <listitem><para>
+ → <type>gboolean</type> (<link linkend="G-TYPE-BOOLEAN:CAPS"><literal>G_TYPE_BOOLEAN</literal></link>)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>string</literal></term>
+ <listitem><para>
+ → <type>char *</type> (<link linkend="G-TYPE-STRING:CAPS"><literal>G_TYPE_STRING</literal></link>)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>double</literal></term>
+ <listitem><para>
+ → <type>double</type> (<link linkend="G-TYPE-DOUBLE:CAPS"><literal>G_TYPE_DOUBLE</literal></link>)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>dateTime.iso8601</literal></term>
+ <listitem><para>
+ → <link linkend="SoupDate"><type>SoupDate</type></link> (<link linkend="SOUP-TYPE-DATE:CAPS"><literal>SOUP_TYPE_DATE</literal></link>)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>base64</literal></term>
+ <listitem><para>
+ → <type>GByteArray</type> (<link linkend="SOUP-TYPE-BYTE-ARRAY:CAPS"><literal>SOUP_TYPE_BYTE_ARRAY</literal></link>)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>struct</literal></term>
+ <listitem><para>
+ → <type>GHashTable</type> (<link linkend="G-TYPE-HASH-TABLE:CAPS"><literal>G_TYPE_HASH_TABLE</literal></link>)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>array</literal></term>
+ <listitem><para>
+ → <type>GValueArray</type> (<link linkend="G-TYPE-VALUE-ARRAY:CAPS"><literal>G_TYPE_VALUE_ARRAY</literal></link>)
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+<type>SoupDate</type> is discussed below.
+<literal>SOUP_TYPE_BYTE_ARRAY</literal> is just a new
+<type>GType</type> value defined by <application>libsoup</application>
+to represent <type>GByteArray</type>s, which glib does not define a
+<type>GType</type> for.
+</para>
+
+<para>
+<application>libsoup</application> provides some additional <link
+linkend="libsoup-GValue-Support"><type>GValue</type> support
+methods</link> for working with
+<type>GValueArray</type>s, and <type>GHashTable</type>s of
+<type>GValue</type>s, for the XML-RPC <literal>struct</literal> and
+<literal>array</literal> types. Eg, you can use <link
+linkend="soup-value-hash-new"><function>soup_value_hash_new</function></link>
+to create a <type>GHashTable</type> to use with the XML-RPC methods,
+and <link
+linkend="soup-value-hash-insert"><function>soup_value_hash_insert</function></link>
+to add values to it without needing to muck with <type>GValue</type>s
+directly.
+</para>
+
+<para>
+The <literal>getbug</literal> and <literal>xmlrpc-test</literal>
+programs in the <application>libsoup</application> sources provide
+examples of how to use the new API. (Beware that
+<literal>xmlrpc-test</literal>'s use of the API is a little
+complicated because of the way it sends all calls through a single
+<literal>do_xmlrpc</literal> method.)
+</para>
+
+</refsect3>
+</refsect2>
+
+<refsect2>
+<title>SoupServer</title>
+
+<refsect3>
+<title>SoupServer handlers</title>
+
+<para>
+ The prototypes for <link
+ linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>,
+ and for the <link
+ linkend="SoupServerCallback"><type>SoupServer</type>
+ handlers</link> themselves have changed:
+</para>
+
+<informalexample><programlisting>
+typedef void (*SoupServerCallback) (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data);
+
+void soup_server_add_handler (SoupServer *server,
+ const char *path,
+ SoupServerCallback callback,
+ gpointer data,
+ GDestroyNotify destroy);
+</programlisting></informalexample>
+
+<para>
+ <function>soup_server_add_handler</function> no longer takes a
+ <type>SoupServerAuthContext</type> (see the discussion of server
+ authentication below), and the order of the final two arguments
+ has been swapped. (Additionally, <type>SoupServerCallbackFn</type>
+ has been renamed to <type>SoupServerCallback</type>, and the old
+ <parameter>unregister</parameter> parameter of type
+ <type>SoupServerUnregisterFn</type> is now a standard
+ <type>GDestroyNotify</type>. The change to
+ <type>GDestroyNotify</type> and the swapping of the final two
+ arguments is to make the method conform to standard glib/gtk
+ practices.)
+</para>
+
+<para>
+ In <type>SoupServerCallback</type>, several bits of data that used
+ to be part of the <parameter>context</parameter> argument are now
+ provided directly, and <parameter>context</parameter> specifically
+ only contains more specifically-client-related information (such
+ as the <type>SoupSocket</type> that the request arrived on, and
+ information about authentication).
+</para>
+
+<para>
+ <parameter>path</parameter> is the fully %-decoded path component
+ of <parameter>msg</parameter>'s URI, and
+ <parameter>query</parameter> is a hash table containing
+ <parameter>msg</parameter>'s URI's
+ <structfield>query</structfield> component decoded with <link
+ linkend="soup-form-decode-urlencoded">soup_form_decode_urlencoded</link>.
+ These are provided for your convenience; if you need the raw
+ query, you can get it out of <parameter>msg</parameter>'s URI
+ directly. If you need the raw path, you'll need to set the <link
+ linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link>
+ property on the server, which actually changes the behavior of the
+ server with respect to how paths are matched; see the
+ documentation for details.
+</para>
+</refsect3>
+
+<refsect3>
+<title>Server-side authentication</title>
+
+<para>
+ <type>SoupServer</type> authentication has been completely
+ rewritten, with <type>SoupServerAuthContext</type> being replaced
+ with <link
+ linkend="SoupAuthDomain"><type>SoupAuthDomain</type></link>. Among
+ other improvements, you no longer need to have the cleartext
+ password available to check against. See the
+ <type>SoupAuthDomain</type> documentation, the <link
+ linkend="libsoup-server-howto">server tutorial</link>, and
+ <literal>tests/server-auth-test.c</literal>.
+</para>
+</refsect3>
+
+<refsect3>
+<title><literal>Expect: 100-continue</literal> and other early <type>SoupMessage</type> processing</title>
+
+<para>
+ <type>SoupServer</type> now handles
+ "<literal>Expect: 100-continue</literal>" correctly. In
+ particular, if the client passes that header, and your server
+ requires authentication, then authentication will be checked
+ before reading the request body.
+</para>
+
+<para>
+ If you want to do additional pre-request-body handling, you can
+ connect to <type>SoupServer</type>'s <link
+ linkend="SoupServer-request-started"><literal>request_started</literal></link>
+ signal, and connect to the request's <link
+ linkend="SoupMessage-got-headers"><literal>got_headers</literal></link>
+ signal from there. (See the description of
+ <literal>request_started</literal> for information about other
+ related <type>SoupServer</type> signals.)
+</para>
+</refsect3>
+
+<refsect3>
+<title>Date header</title>
+
+<para>
+ <type>SoupServer</type> now automatically sets the
+ <literal>Date</literal> header on all responses, as required by
+ RFC 2616.
+</para>
+</refsect3>
+
+<refsect3>
+<title>SoupServerMessage</title>
+
+<para>
+ <type>SoupServerMessage</type> is now merged into
+ <type>SoupMessage</type>.
+ <function>soup_server_message_set_encoding</function> is replaced
+ with <link
+ linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link>
+ as described in the section on <type>SoupMessage</type> above.
+</para>
+</refsect3>
+
+<refsect3>
+<title><function>soup_server_run</function> / <function>soup_server_quit</function></title>
+
+<para>
+ <link
+ linkend="soup-server-run"><function>soup_server_run</function></link>
+ and <link
+ linkend="soup-server-run-async"><function>soup_server_run_async</function></link>
+ no longer <function>g_object_ref</function> the server, and
+ <link
+ linkend="soup-server-quit"><function>soup_server_quit</function></link>
+ no longer unrefs it.
+</para>
+</refsect3>
+
+</refsect2>
+
+<refsect2>
+<title>Miscellaneous</title>
+
+<refsect3>
+<title>SoupDate</title>
+
+<para>
+ The new <link linkend="SoupDate"><type>SoupDate</type></link> type
+ replaces the old <function>soup_date_*</function> methods, and has
+ an improved (more liberal) date parser.
+</para>
+</refsect3>
+
+<refsect3>
+<title>Header parsing</title>
+
+<para>
+ <literal>soup-headers.h</literal> now has a few additional methods
+ for parsing list-type headers.
+</para>
+</refsect3>
+
+<refsect3>
+<title>SoupAddress, SoupSocket</title>
+
+<para>
+ <type>SoupSocket</type> has had various simplifications made to
+ reflect the fact that this is specifically libsoup's socket
+ implementation, not some random generic socket API.
+</para>
+
+<para>
+ Various <type>SoupAddress</type> and <type>SoupSocket</type>
+ methods now take arguments of the new <link
+ linkend="GCancellable"><type>GCancellable</type></link> type, from
+ libgio. When porting old code, you can just pass
+ <literal>NULL</literal> for these. (<link
+ linkend="soup-address-resolve-async"><function>soup_address_resolve_async</function></link>
+ also takes another new argument, a <link
+ linkend="GMainContext"><type>GMainContext</type></link> that
+ you'll want to pass <literal>NULL</literal> for.) If you pass a
+ <type>GCancellable</type>, you can use it to cleanly cancel the
+ address resolution / socket operation.
+</para>
+
+</refsect3>
+
+<refsect3>
+<title>Base64 methods</title>
+
+<para>
+ The deprecated base64 methods are now gone; use glib's base64
+ methods instead.
+</para>
+</refsect3>
+
+</refsect2>
+
+</refentry>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<refentry id="libsoup-server-howto">
+<refmeta>
+<refentrytitle>Soup Server Basics</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBSOUP Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Soup Server Basics</refname><refpurpose>Server-side tutorial</refpurpose>
+</refnamediv>
+
+<refsect2>
+<title>Creating a SoupSession</title>
+
+<para>
+As with the client API, there is a single object that will encapsulate
+most of your interactions with libsoup. In this case, <link
+linkend="SoupServer"><type>SoupServer</type></link>.
+</para>
+
+<para>
+You create the server with <link
+linkend="soup-server-new"><function>soup_server_new</function></link>,
+and as with the <type>SoupSession</type> constructor, you can specify
+various additional options:
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><link linkend="SOUP-SERVER-PORT--CAPS"><literal>SOUP_SERVER_PORT</literal></link></term>
+ <listitem><para>
+ The TCP port to listen on. If <literal>0</literal> (or
+ left unspecified), some unused port will be selected for
+ you. (You can find out what port by calling <link
+ linkend="soup-server-get-port"><function>soup_server_get_port</function></link>.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SERVER-INTERFACE--CAPS"><literal>SOUP_SERVER_INTERFACE</literal></link></term>
+ <listitem><para>
+ A <link linkend="SoupAddress"><type>SoupAddress</type></link>,
+ specifying the IP address of the network interface to run
+ the server on. If <literal>NULL</literal> (or left
+ unspecified), the server will listen on all interfaces.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SERVER-SSL-CERT-FILE--CAPS"><literal>SOUP_SERVER_SSL_CERT_FILE</literal></link></term>
+ <listitem><para>
+ Points to a file containing an SSL certificate to use. If
+ this is set, then the server will speak HTTPS; otherwise
+ it will speak HTTP.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SERVER-SSL-KEY-FILE--CAPS"><literal>SOUP_SERVER_SSL_KEY_FILE</literal></link></term>
+ <listitem><para>
+ Points to a file containing the private key for the
+ <literal>SOUP_SERVER_SSL_CERT_FILE</literal>. (It may
+ point to the same file.)
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SERVER-ASYNC-CONTEXT--CAPS"><literal>SOUP_SERVER_ASYNC_CONTEXT</literal></link></term>
+ <listitem><para>
+ A <link linkend="GMainContext"><type>GMainContext</type></link> which
+ the server will use for asynchronous operations. This can
+ be set if you want to use a SoupServer in a thread
+ other than the main thread.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SOUP-SERVER-RAW-PATHS--CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link></term>
+ <listitem><para>
+ Set this to <literal>TRUE</literal> if you don't want
+ <application>libsoup</application> to decode %-encoding
+ in the Request-URI. (Eg, because you need to treat
+ <literal>"/foo/bar"</literal> and
+ <literal>"/foo%2Fbar"</literal> as different paths.
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+</refsect2>
+
+<refsect2>
+<title>Adding Handlers</title>
+
+<para>
+By default, <link linkend="SoupServer"><type>SoupServer</type></link>
+returns "404 Not Found" in response to all requests (except ones that
+it can't parse, which get "400 Bad Request"). To override this
+behavior, call <link
+linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>
+to set a callback to handle certain URI paths.
+</para>
+
+<informalexample><programlisting>
+ soup_server_add_handler (server, "/foo", server_callback,
+ data, destroy_notify);
+</programlisting></informalexample>
+
+<para>
+The <literal>"/foo"</literal> indicates the base path for this
+handler. When a request comes in, if there is a handler registered for
+exactly the path in the request's <literal>Request-URI</literal>, then
+that handler will be called. Otherwise
+<application>libsoup</application> will strip path components one by
+one until it finds a matching handler. So for example, a request of
+the form
+"<literal>GET /foo/bar/baz.html?a=1&b=2 HTTP/1.1</literal>"
+would look for handlers for "<literal>/foo/bar/baz.html</literal>",
+"<literal>/foo/bar</literal>", and "<literal>/foo</literal>". If a
+handler has been registered with a <literal>NULL</literal> base path,
+then it is used as the default handler for any request that doesn't
+match any other handler.
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Responding to Requests</title>
+
+<para>
+A handler callback looks something like this:
+</para>
+
+<informalexample><programlisting>
+static void
+server_callback (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
+{
+ ...
+}
+</programlisting></informalexample>
+
+<para>
+<literal>msg</literal> is the request that has been received and
+<literal>user_data</literal> is the data that was passed to <link
+linkend="soup-server-add-handler"><function>soup_server_add_handler</function></link>.
+<literal>path</literal> is the path (from <literal>msg</literal>'s
+URI), and <literal>query</literal> contains the result of parsing the
+URI query field. (It is <literal>NULL</literal> if there was no
+query.) <literal>client</literal> is a <link
+linkend="SoupClientContext"><type>SoupClientContext</type></link>,
+which contains additional information about the client (including its
+IP address, and whether or not it used HTTP authentication).
+</para>
+
+<para>
+By default, <application>libsoup</application> assumes that you have
+completely finished processing the message when you return from the
+callback, and that it can therefore begin sending the response. If you
+are not ready to send a response immediately (eg, you have to contact
+another server, or wait for data from a database), you must call <link
+linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>
+on the message before returning from the callback. This will delay
+sending a response until you call <link
+linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>.
+(You must also connect to the <link
+linkend="SoupMessage-finished">finished</link> signal on the message
+in this case, so that you can break off processing if the client
+unexpectedly disconnects before you start sending the data.)
+</para>
+
+<para>
+To set the response status, call <link
+linkend="soup-message-set-status"><function>soup_message_set_status</function></link>
+or <link
+linkend="soup-message-set-status-full"><function>soup_message_set_status_full</function></link>.
+If the response requires a body, you must decide whether to use
+<literal>Content-Length</literal> encoding (the default), or
+<literal>chunked</literal> encoding.
+</para>
+
+<refsect3>
+<title>Responding with <literal>Content-Length</literal>
+Encoding</title>
+
+<para>
+This is the simpler way to set a response body, if you have all of the
+data available at once.
+</para>
+
+<informalexample><programlisting>
+static void
+server_callback (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
+{
+ MyServerData *server_data = user_data;
+ const char *mime_type;
+ GByteArray *body;
+
+ if (msg->method != SOUP_METHOD_GET) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ /* This is somewhat silly. Presumably your server will do
+ * something more interesting.
+ */
+ body = g_hash_table_lookup (server_data->bodies, path);
+ mime_type = g_hash_table_lookup (server_data->mime_types, path);
+ if (!body || !mime_type) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, mime_type, SOUP_MEMORY_COPY,
+ body->data, body->len);
+}
+</programlisting></informalexample>
+
+</refsect3>
+
+<refsect3>
+<title>Responding with <literal>chunked</literal> Encoding</title>
+
+<para>
+If you want to supply the response body in chunks as it becomes
+available, use <literal>chunked</literal> encoding instead. In this
+case, first call <link
+linkend="soup-message-headers-set-encoding"><function>soup_message_headers_set_encoding</function></link> <literal>(msg->response_headers, <link
+linkend="SoupEncoding">SOUP_ENCODING_CHUNKED</link>)</literal>
+to tell <application>libsoup</application> that you'll be using
+chunked encoding. Then call <link
+linkend="soup-message-body-append"><function>soup_message_body_append</function></link>
+(or <link
+linkend="soup-message-body-append-buffer"><function>soup_message_body_append_buffer</function></link>)
+on <literal>msg->response_body</literal> with each chunk of the
+response body as it becomes available, and call <link
+linkend="soup-message-body-complete"><function>soup_message_body_complete</function></link>
+when the response is complete. After each of these calls, you must
+also call <link
+linkend="soup-server-unpause-message"><function>soup_server_unpause_message</function></link>
+to cause the chunk to be sent. (You do not normally need to call <link
+linkend="soup-server-pause-message"><function>soup_server_pause_message</function></link>,
+because I/O is automatically paused when doing a
+<literal>chunked</literal> transfer if no chunks are available.)
+</para>
+
+<para>
+When using chunked encoding, you must also connect to the <link
+linkend="SoupMessage-finished">finished</link> signal on the message,
+so that you will be notified if the client disconnects between two
+chunks; <type>SoupServer</type> will unref the message if that
+happens, so you must stop adding new chunks to the response at that
+point. (An alternate possibility is to write each new chunk only when
+the <link linkend="SoupMessage-wrote-chunk">wrote_chunk</link> signal
+is emitted indicating that the previous one was written successfully.)
+</para>
+
+<para>
+The <emphasis role="bold"><literal>simple-proxy</literal></emphasis>
+example in the <literal>tests/</literal> directory gives an example of
+using <literal>chunked</literal> encoding.
+</para>
+
+</refsect3>
+</refsect2>
+
+
+<refsect2>
+<title>Handling Authentication</title>
+
+<para>
+To have <link linkend="SoupServer"><type>SoupServer</type></link>
+handle HTTP authentication for you, create a <link
+linkend="SoupAuthDomainBasic"><type>SoupAuthDomainBasic</type></link>
+or <link
+linkend="SoupAuthDomainDigest"><type>SoupAuthDomainDigest</type></link>,
+and pass it to <link
+linkend="soup-server-add-auth-domain"><function>soup_server_add_auth_domain</function></link>:
+</para>
+
+<informalexample><programlisting>
+ SoupAuthDomain *domain;
+
+ domain = soup_auth_domain_basic_new (
+ SOUP_AUTH_DOMAIN_REALM, "My Realm",
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA, auth_data,
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/foo",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/bar/private",
+ NULL);
+ soup_server_add_auth_domain (server, domain);
+ g_object_unref (domain);
+</programlisting></informalexample>
+
+<para>
+Then, every request under one of the auth domain's paths will be
+passed to the <literal>auth_callback</literal> first before being
+passed to the <literal>server_callback</literal>:
+</para>
+
+<informalexample><programlisting>
+static gboolean
+auth_callback (SoupAuthDomain *domain, SoupMessage *msg,
+ const char *username, const char *password,
+ gpointer user_data)
+{
+ MyServerData *server_data = user_data;
+ MyUserData *user;
+
+ user = my_server_data_lookup_user (server_data, username);
+ if (!user)
+ return FALSE;
+
+ /* FIXME: Don't do this. Keeping a cleartext password database
+ * is bad.
+ */
+ return strcmp (password, user->password) == 0;
+}
+</programlisting></informalexample>
+
+<para>
+The <link
+linkend="SoupAuthDomainBasicAuthCallback"><type>SoupAuthDomainBasicAuthCallback</type></link>
+is given the username and password from the
+<literal>Authorization</literal> header and must determine, in some
+server-specific manner, whether or not to accept them. (In this
+example we compare the password against a cleartext password database,
+but it would be better to store the password somehow encoded, as in
+the UNIX password database. Alternatively, you may need to delegate
+the password check to PAM or some other service.)
+</para>
+
+<para>
+If you are using Digest authentication, note that <link
+linkend="SoupAuthDomainDigestAuthCallback"><type>SoupAuthDomainDigestAuthCallback</type></link>
+works completely differently (since the server doesn't receive the
+cleartext password from the client in that case, so there's no way to
+compare it directly). See the documentation for <link
+linkend="SoupAuthDomainDigest"><type>SoupAuthDomainDigest</type></link>
+for more details.
+</para>
+
+<para>
+You can have multiple <type>SoupAuthDomain</type>s attached to a
+<literal>SoupServer</literal>, either in separate parts of the path
+hierarchy, or overlapping. (Eg, you might want to accept either Basic
+or Digest authentication for a given path.) When more than one auth
+domain covers a given path, the request will be accepted if the user
+authenticates successfully against <emphasis>any</emphasis> of the
+domains.
+</para>
+
+<para>
+If you want to require authentication for some requests under a
+certain path, but not all of them (eg, you want to authenticate
+<literal>PUT</literal>s, but not <literal>GET</literal>s), use a
+<link
+linkend="SoupAuthDomainFilter"><type>SoupAuthDomainFilter</type></link>.
+</para>
+
+</refsect2>
+
+</refentry>
--- /dev/null
+rfc1945 - HTTP/1.0
+rfc2068 - HTTP/1.1 (mostly obsoleted original specification)
+rfc2109 - HTTP State Management Mechanism
+rfc2145 - Use and Interpretation of HTTP Version Numbers
+rfc2324 - Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)
+rfc2388 - Returning Values from Forms: multipart/form-data
+rfc2518 - HTTP Extensions for Distributed Authoring -- WEBDAV
+rfc2616 - HTTP/1.1 (revised) [plus errata]
+rfc2617 - HTTP Authentication: Basic and Digest Access Authentication [plus errata]
+rfc2817 - Upgrading to TLS Within HTTP/1.1
+rfc2818 - HTTP Over TLS
+rfc2965 - HTTP State Management Mechanism (allegedly obsoletes 2109)
+rfc3986 - Uniform Resource Identifiers (URI): Generic Syntax
--- /dev/null
+
+
+
+
+
+
+Network Working Group T. Berners-Lee
+Request for Comments: 1945 MIT/LCS
+Category: Informational R. Fielding
+ UC Irvine
+ H. Frystyk
+ MIT/LCS
+ May 1996
+
+
+ Hypertext Transfer Protocol -- HTTP/1.0
+
+Status of This Memo
+
+ This memo provides information for the Internet community. This memo
+ does not specify an Internet standard of any kind. Distribution of
+ this memo is unlimited.
+
+IESG Note:
+
+ The IESG has concerns about this protocol, and expects this document
+ to be replaced relatively soon by a standards track document.
+
+Abstract
+
+ The Hypertext Transfer Protocol (HTTP) is an application-level
+ protocol with the lightness and speed necessary for distributed,
+ collaborative, hypermedia information systems. It is a generic,
+ stateless, object-oriented protocol which can be used for many tasks,
+ such as name servers and distributed object management systems,
+ through extension of its request methods (commands). A feature of
+ HTTP is the typing of data representation, allowing systems to be
+ built independently of the data being transferred.
+
+ HTTP has been in use by the World-Wide Web global information
+ initiative since 1990. This specification reflects common usage of
+ the protocol referred to as "HTTP/1.0".
+
+Table of Contents
+
+ 1. Introduction .............................................. 4
+ 1.1 Purpose .............................................. 4
+ 1.2 Terminology .......................................... 4
+ 1.3 Overall Operation .................................... 6
+ 1.4 HTTP and MIME ........................................ 8
+ 2. Notational Conventions and Generic Grammar ................ 8
+ 2.1 Augmented BNF ........................................ 8
+ 2.2 Basic Rules .......................................... 10
+ 3. Protocol Parameters ....................................... 12
+
+
+
+Berners-Lee, et al Informational [Page 1]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ 3.1 HTTP Version ......................................... 12
+ 3.2 Uniform Resource Identifiers ......................... 14
+ 3.2.1 General Syntax ................................ 14
+ 3.2.2 http URL ...................................... 15
+ 3.3 Date/Time Formats .................................... 15
+ 3.4 Character Sets ....................................... 17
+ 3.5 Content Codings ...................................... 18
+ 3.6 Media Types .......................................... 19
+ 3.6.1 Canonicalization and Text Defaults ............ 19
+ 3.6.2 Multipart Types ............................... 20
+ 3.7 Product Tokens ....................................... 20
+ 4. HTTP Message .............................................. 21
+ 4.1 Message Types ........................................ 21
+ 4.2 Message Headers ...................................... 22
+ 4.3 General Header Fields ................................ 23
+ 5. Request ................................................... 23
+ 5.1 Request-Line ......................................... 23
+ 5.1.1 Method ........................................ 24
+ 5.1.2 Request-URI ................................... 24
+ 5.2 Request Header Fields ................................ 25
+ 6. Response .................................................. 25
+ 6.1 Status-Line .......................................... 26
+ 6.1.1 Status Code and Reason Phrase ................. 26
+ 6.2 Response Header Fields ............................... 28
+ 7. Entity .................................................... 28
+ 7.1 Entity Header Fields ................................. 29
+ 7.2 Entity Body .......................................... 29
+ 7.2.1 Type .......................................... 29
+ 7.2.2 Length ........................................ 30
+ 8. Method Definitions ........................................ 30
+ 8.1 GET .................................................. 31
+ 8.2 HEAD ................................................. 31
+ 8.3 POST ................................................. 31
+ 9. Status Code Definitions ................................... 32
+ 9.1 Informational 1xx .................................... 32
+ 9.2 Successful 2xx ....................................... 32
+ 9.3 Redirection 3xx ...................................... 34
+ 9.4 Client Error 4xx ..................................... 35
+ 9.5 Server Error 5xx ..................................... 37
+ 10. Header Field Definitions .................................. 37
+ 10.1 Allow ............................................... 38
+ 10.2 Authorization ....................................... 38
+ 10.3 Content-Encoding .................................... 39
+ 10.4 Content-Length ...................................... 39
+ 10.5 Content-Type ........................................ 40
+ 10.6 Date ................................................ 40
+ 10.7 Expires ............................................. 41
+ 10.8 From ................................................ 42
+
+
+
+Berners-Lee, et al Informational [Page 2]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ 10.9 If-Modified-Since ................................... 42
+ 10.10 Last-Modified ....................................... 43
+ 10.11 Location ............................................ 44
+ 10.12 Pragma .............................................. 44
+ 10.13 Referer ............................................. 44
+ 10.14 Server .............................................. 45
+ 10.15 User-Agent .......................................... 46
+ 10.16 WWW-Authenticate .................................... 46
+ 11. Access Authentication ..................................... 47
+ 11.1 Basic Authentication Scheme ......................... 48
+ 12. Security Considerations ................................... 49
+ 12.1 Authentication of Clients ........................... 49
+ 12.2 Safe Methods ........................................ 49
+ 12.3 Abuse of Server Log Information ..................... 50
+ 12.4 Transfer of Sensitive Information ................... 50
+ 12.5 Attacks Based On File and Path Names ................ 51
+ 13. Acknowledgments ........................................... 51
+ 14. References ................................................ 52
+ 15. Authors' Addresses ........................................ 54
+ Appendix A. Internet Media Type message/http ................ 55
+ Appendix B. Tolerant Applications ........................... 55
+ Appendix C. Relationship to MIME ............................ 56
+ C.1 Conversion to Canonical Form ......................... 56
+ C.2 Conversion of Date Formats ........................... 57
+ C.3 Introduction of Content-Encoding ..................... 57
+ C.4 No Content-Transfer-Encoding ......................... 57
+ C.5 HTTP Header Fields in Multipart Body-Parts ........... 57
+ Appendix D. Additional Features ............................. 57
+ D.1 Additional Request Methods ........................... 58
+ D.1.1 PUT ........................................... 58
+ D.1.2 DELETE ........................................ 58
+ D.1.3 LINK .......................................... 58
+ D.1.4 UNLINK ........................................ 58
+ D.2 Additional Header Field Definitions .................. 58
+ D.2.1 Accept ........................................ 58
+ D.2.2 Accept-Charset ................................ 59
+ D.2.3 Accept-Encoding ............................... 59
+ D.2.4 Accept-Language ............................... 59
+ D.2.5 Content-Language .............................. 59
+ D.2.6 Link .......................................... 59
+ D.2.7 MIME-Version .................................. 59
+ D.2.8 Retry-After ................................... 60
+ D.2.9 Title ......................................... 60
+ D.2.10 URI ........................................... 60
+
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 3]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+1. Introduction
+
+1.1 Purpose
+
+ The Hypertext Transfer Protocol (HTTP) is an application-level
+ protocol with the lightness and speed necessary for distributed,
+ collaborative, hypermedia information systems. HTTP has been in use
+ by the World-Wide Web global information initiative since 1990. This
+ specification reflects common usage of the protocol referred too as
+ "HTTP/1.0". This specification describes the features that seem to be
+ consistently implemented in most HTTP/1.0 clients and servers. The
+ specification is split into two sections. Those features of HTTP for
+ which implementations are usually consistent are described in the
+ main body of this document. Those features which have few or
+ inconsistent implementations are listed in Appendix D.
+
+ Practical information systems require more functionality than simple
+ retrieval, including search, front-end update, and annotation. HTTP
+ allows an open-ended set of methods to be used to indicate the
+ purpose of a request. It builds on the discipline of reference
+ provided by the Uniform Resource Identifier (URI) [2], as a location
+ (URL) [4] or name (URN) [16], for indicating the resource on which a
+ method is to be applied. Messages are passed in a format similar to
+ that used by Internet Mail [7] and the Multipurpose Internet Mail
+ Extensions (MIME) [5].
+
+ HTTP is also used as a generic protocol for communication between
+ user agents and proxies/gateways to other Internet protocols, such as
+ SMTP [12], NNTP [11], FTP [14], Gopher [1], and WAIS [8], allowing
+ basic hypermedia access to resources available from diverse
+ applications and simplifying the implementation of user agents.
+
+1.2 Terminology
+
+ This specification uses a number of terms to refer to the roles
+ played by participants in, and objects of, the HTTP communication.
+
+ connection
+
+ A transport layer virtual circuit established between two
+ application programs for the purpose of communication.
+
+ message
+
+ The basic unit of HTTP communication, consisting of a structured
+ sequence of octets matching the syntax defined in Section 4 and
+ transmitted via the connection.
+
+
+
+
+Berners-Lee, et al Informational [Page 4]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ request
+
+ An HTTP request message (as defined in Section 5).
+
+ response
+
+ An HTTP response message (as defined in Section 6).
+
+ resource
+
+ A network data object or service which can be identified by a
+ URI (Section 3.2).
+
+ entity
+
+ A particular representation or rendition of a data resource, or
+ reply from a service resource, that may be enclosed within a
+ request or response message. An entity consists of
+ metainformation in the form of entity headers and content in the
+ form of an entity body.
+
+ client
+
+ An application program that establishes connections for the
+ purpose of sending requests.
+
+ user agent
+
+ The client which initiates a request. These are often browsers,
+ editors, spiders (web-traversing robots), or other end user
+ tools.
+
+ server
+
+ An application program that accepts connections in order to
+ service requests by sending back responses.
+
+ origin server
+
+ The server on which a given resource resides or is to be created.
+
+ proxy
+
+ An intermediary program which acts as both a server and a client
+ for the purpose of making requests on behalf of other clients.
+ Requests are serviced internally or by passing them, with
+ possible translation, on to other servers. A proxy must
+ interpret and, if necessary, rewrite a request message before
+
+
+
+Berners-Lee, et al Informational [Page 5]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ forwarding it. Proxies are often used as client-side portals
+ through network firewalls and as helper applications for
+ handling requests via protocols not implemented by the user
+ agent.
+
+ gateway
+
+ A server which acts as an intermediary for some other server.
+ Unlike a proxy, a gateway receives requests as if it were the
+ origin server for the requested resource; the requesting client
+ may not be aware that it is communicating with a gateway.
+ Gateways are often used as server-side portals through network
+ firewalls and as protocol translators for access to resources
+ stored on non-HTTP systems.
+
+ tunnel
+
+ A tunnel is an intermediary program which is acting as a blind
+ relay between two connections. Once active, a tunnel is not
+ considered a party to the HTTP communication, though the tunnel
+ may have been initiated by an HTTP request. The tunnel ceases to
+ exist when both ends of the relayed connections are closed.
+ Tunnels are used when a portal is necessary and the intermediary
+ cannot, or should not, interpret the relayed communication.
+
+ cache
+
+ A program's local store of response messages and the subsystem
+ that controls its message storage, retrieval, and deletion. A
+ cache stores cachable responses in order to reduce the response
+ time and network bandwidth consumption on future, equivalent
+ requests. Any client or server may include a cache, though a
+ cache cannot be used by a server while it is acting as a tunnel.
+
+ Any given program may be capable of being both a client and a server;
+ our use of these terms refers only to the role being performed by the
+ program for a particular connection, rather than to the program's
+ capabilities in general. Likewise, any server may act as an origin
+ server, proxy, gateway, or tunnel, switching behavior based on the
+ nature of each request.
+
+1.3 Overall Operation
+
+ The HTTP protocol is based on a request/response paradigm. A client
+ establishes a connection with a server and sends a request to the
+ server in the form of a request method, URI, and protocol version,
+ followed by a MIME-like message containing request modifiers, client
+ information, and possible body content. The server responds with a
+
+
+
+Berners-Lee, et al Informational [Page 6]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ status line, including the message's protocol version and a success
+ or error code, followed by a MIME-like message containing server
+ information, entity metainformation, and possible body content.
+
+ Most HTTP communication is initiated by a user agent and consists of
+ a request to be applied to a resource on some origin server. In the
+ simplest case, this may be accomplished via a single connection (v)
+ between the user agent (UA) and the origin server (O).
+
+ request chain ------------------------>
+ UA -------------------v------------------- O
+ <----------------------- response chain
+
+ A more complicated situation occurs when one or more intermediaries
+ are present in the request/response chain. There are three common
+ forms of intermediary: proxy, gateway, and tunnel. A proxy is a
+ forwarding agent, receiving requests for a URI in its absolute form,
+ rewriting all or parts of the message, and forwarding the reformatted
+ request toward the server identified by the URI. A gateway is a
+ receiving agent, acting as a layer above some other server(s) and, if
+ necessary, translating the requests to the underlying server's
+ protocol. A tunnel acts as a relay point between two connections
+ without changing the messages; tunnels are used when the
+ communication needs to pass through an intermediary (such as a
+ firewall) even when the intermediary cannot understand the contents
+ of the messages.
+
+ request chain -------------------------------------->
+ UA -----v----- A -----v----- B -----v----- C -----v----- O
+ <------------------------------------- response chain
+
+ The figure above shows three intermediaries (A, B, and C) between the
+ user agent and origin server. A request or response message that
+ travels the whole chain must pass through four separate connections.
+ This distinction is important because some HTTP communication options
+ may apply only to the connection with the nearest, non-tunnel
+ neighbor, only to the end-points of the chain, or to all connections
+ along the chain. Although the diagram is linear, each participant may
+ be engaged in multiple, simultaneous communications. For example, B
+ may be receiving requests from many clients other than A, and/or
+ forwarding requests to servers other than C, at the same time that it
+ is handling A's request.
+
+ Any party to the communication which is not acting as a tunnel may
+ employ an internal cache for handling requests. The effect of a cache
+ is that the request/response chain is shortened if one of the
+ participants along the chain has a cached response applicable to that
+ request. The following illustrates the resulting chain if B has a
+
+
+
+Berners-Lee, et al Informational [Page 7]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ cached copy of an earlier response from O (via C) for a request which
+ has not been cached by UA or A.
+
+ request chain ---------->
+ UA -----v----- A -----v----- B - - - - - - C - - - - - - O
+ <--------- response chain
+
+ Not all responses are cachable, and some requests may contain
+ modifiers which place special requirements on cache behavior. Some
+ HTTP/1.0 applications use heuristics to describe what is or is not a
+ "cachable" response, but these rules are not standardized.
+
+ On the Internet, HTTP communication generally takes place over TCP/IP
+ connections. The default port is TCP 80 [15], but other ports can be
+ used. This does not preclude HTTP from being implemented on top of
+ any other protocol on the Internet, or on other networks. HTTP only
+ presumes a reliable transport; any protocol that provides such
+ guarantees can be used, and the mapping of the HTTP/1.0 request and
+ response structures onto the transport data units of the protocol in
+ question is outside the scope of this specification.
+
+ Except for experimental applications, current practice requires that
+ the connection be established by the client prior to each request and
+ closed by the server after sending the response. Both clients and
+ servers should be aware that either party may close the connection
+ prematurely, due to user action, automated time-out, or program
+ failure, and should handle such closing in a predictable fashion. In
+ any case, the closing of the connection by either or both parties
+ always terminates the current request, regardless of its status.
+
+1.4 HTTP and MIME
+
+ HTTP/1.0 uses many of the constructs defined for MIME, as defined in
+ RFC 1521 [5]. Appendix C describes the ways in which the context of
+ HTTP allows for different use of Internet Media Types than is
+ typically found in Internet mail, and gives the rationale for those
+ differences.
+
+2. Notational Conventions and Generic Grammar
+
+2.1 Augmented BNF
+
+ All of the mechanisms specified in this document are described in
+ both prose and an augmented Backus-Naur Form (BNF) similar to that
+ used by RFC 822 [7]. Implementors will need to be familiar with the
+ notation in order to understand this specification. The augmented BNF
+ includes the following constructs:
+
+
+
+
+Berners-Lee, et al Informational [Page 8]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ name = definition
+
+ The name of a rule is simply the name itself (without any
+ enclosing "<" and ">") and is separated from its definition by
+ the equal character "=". Whitespace is only significant in that
+ indentation of continuation lines is used to indicate a rule
+ definition that spans more than one line. Certain basic rules
+ are in uppercase, such as SP, LWS, HT, CRLF, DIGIT, ALPHA, etc.
+ Angle brackets are used within definitions whenever their
+ presence will facilitate discerning the use of rule names.
+
+ "literal"
+
+ Quotation marks surround literal text. Unless stated otherwise,
+ the text is case-insensitive.
+
+ rule1 | rule2
+
+ Elements separated by a bar ("I") are alternatives,
+ e.g., "yes | no" will accept yes or no.
+
+ (rule1 rule2)
+
+ Elements enclosed in parentheses are treated as a single
+ element. Thus, "(elem (foo | bar) elem)" allows the token
+ sequences "elem foo elem" and "elem bar elem".
+
+ *rule
+
+ The character "*" preceding an element indicates repetition. The
+ full form is "<n>*<m>element" indicating at least <n> and at
+ most <m> occurrences of element. Default values are 0 and
+ infinity so that "*(element)" allows any number, including zero;
+ "1*element" requires at least one; and "1*2element" allows one
+ or two.
+
+ [rule]
+
+ Square brackets enclose optional elements; "[foo bar]" is
+ equivalent to "*1(foo bar)".
+
+ N rule
+
+ Specific repetition: "<n>(element)" is equivalent to
+ "<n>*<n>(element)"; that is, exactly <n> occurrences of
+ (element). Thus 2DIGIT is a 2-digit number, and 3ALPHA is a
+ string of three alphabetic characters.
+
+
+
+
+Berners-Lee, et al Informational [Page 9]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ #rule
+
+ A construct "#" is defined, similar to "*", for defining lists
+ of elements. The full form is "<n>#<m>element" indicating at
+ least <n> and at most <m> elements, each separated by one or
+ more commas (",") and optional linear whitespace (LWS). This
+ makes the usual form of lists very easy; a rule such as
+ "( *LWS element *( *LWS "," *LWS element ))" can be shown as
+ "1#element". Wherever this construct is used, null elements are
+ allowed, but do not contribute to the count of elements present.
+ That is, "(element), , (element)" is permitted, but counts as
+ only two elements. Therefore, where at least one element is
+ required, at least one non-null element must be present. Default
+ values are 0 and infinity so that "#(element)" allows any
+ number, including zero; "1#element" requires at least one; and
+ "1#2element" allows one or two.
+
+ ; comment
+
+ A semi-colon, set off some distance to the right of rule text,
+ starts a comment that continues to the end of line. This is a
+ simple way of including useful notes in parallel with the
+ specifications.
+
+ implied *LWS
+
+ The grammar described by this specification is word-based.
+ Except where noted otherwise, linear whitespace (LWS) can be
+ included between any two adjacent words (token or
+ quoted-string), and between adjacent tokens and delimiters
+ (tspecials), without changing the interpretation of a field. At
+ least one delimiter (tspecials) must exist between any two
+ tokens, since they would otherwise be interpreted as a single
+ token. However, applications should attempt to follow "common
+ form" when generating HTTP constructs, since there exist some
+ implementations that fail to accept anything beyond the common
+ forms.
+
+2.2 Basic Rules
+
+ The following rules are used throughout this specification to
+ describe basic parsing constructs. The US-ASCII coded character set
+ is defined by [17].
+
+ OCTET = <any 8-bit sequence of data>
+ CHAR = <any US-ASCII character (octets 0 - 127)>
+ UPALPHA = <any US-ASCII uppercase letter "A".."Z">
+ LOALPHA = <any US-ASCII lowercase letter "a".."z">
+
+
+
+Berners-Lee, et al Informational [Page 10]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ ALPHA = UPALPHA | LOALPHA
+ DIGIT = <any US-ASCII digit "0".."9">
+ CTL = <any US-ASCII control character
+ (octets 0 - 31) and DEL (127)>
+ CR = <US-ASCII CR, carriage return (13)>
+ LF = <US-ASCII LF, linefeed (10)>
+ SP = <US-ASCII SP, space (32)>
+ HT = <US-ASCII HT, horizontal-tab (9)>
+ <"> = <US-ASCII double-quote mark (34)>
+
+ HTTP/1.0 defines the octet sequence CR LF as the end-of-line marker
+ for all protocol elements except the Entity-Body (see Appendix B for
+ tolerant applications). The end-of-line marker within an Entity-Body
+ is defined by its associated media type, as described in Section 3.6.
+
+ CRLF = CR LF
+
+ HTTP/1.0 headers may be folded onto multiple lines if each
+ continuation line begins with a space or horizontal tab. All linear
+ whitespace, including folding, has the same semantics as SP.
+
+ LWS = [CRLF] 1*( SP | HT )
+
+ However, folding of header lines is not expected by some
+ applications, and should not be generated by HTTP/1.0 applications.
+
+ The TEXT rule is only used for descriptive field contents and values
+ that are not intended to be interpreted by the message parser. Words
+ of *TEXT may contain octets from character sets other than US-ASCII.
+
+ TEXT = <any OCTET except CTLs,
+ but including LWS>
+
+ Recipients of header field TEXT containing octets outside the US-
+ ASCII character set may assume that they represent ISO-8859-1
+ characters.
+
+ Hexadecimal numeric characters are used in several protocol elements.
+
+ HEX = "A" | "B" | "C" | "D" | "E" | "F"
+ | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
+
+ Many HTTP/1.0 header field values consist of words separated by LWS
+ or special characters. These special characters must be in a quoted
+ string to be used within a parameter value.
+
+ word = token | quoted-string
+
+
+
+
+Berners-Lee, et al Informational [Page 11]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ token = 1*<any CHAR except CTLs or tspecials>
+
+ tspecials = "(" | ")" | "<" | ">" | "@"
+ | "," | ";" | ":" | "\" | <">
+ | "/" | "[" | "]" | "?" | "="
+ | "{" | "}" | SP | HT
+
+ Comments may be included in some HTTP header fields by surrounding
+ the comment text with parentheses. Comments are only allowed in
+ fields containing "comment" as part of their field value definition.
+ In all other fields, parentheses are considered part of the field
+ value.
+
+ comment = "(" *( ctext | comment ) ")"
+ ctext = <any TEXT excluding "(" and ")">
+
+ A string of text is parsed as a single word if it is quoted using
+ double-quote marks.
+
+ quoted-string = ( <"> *(qdtext) <"> )
+
+ qdtext = <any CHAR except <"> and CTLs,
+ but including LWS>
+
+ Single-character quoting using the backslash ("\") character is not
+ permitted in HTTP/1.0.
+
+3. Protocol Parameters
+
+3.1 HTTP Version
+
+ HTTP uses a "<major>.<minor>" numbering scheme to indicate versions
+ of the protocol. The protocol versioning policy is intended to allow
+ the sender to indicate the format of a message and its capacity for
+ understanding further HTTP communication, rather than the features
+ obtained via that communication. No change is made to the version
+ number for the addition of message components which do not affect
+ communication behavior or which only add to extensible field values.
+ The <minor> number is incremented when the changes made to the
+ protocol add features which do not change the general message parsing
+ algorithm, but which may add to the message semantics and imply
+ additional capabilities of the sender. The <major> number is
+ incremented when the format of a message within the protocol is
+ changed.
+
+ The version of an HTTP message is indicated by an HTTP-Version field
+ in the first line of the message. If the protocol version is not
+ specified, the recipient must assume that the message is in the
+
+
+
+Berners-Lee, et al Informational [Page 12]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ simple HTTP/0.9 format.
+
+ HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+
+ Note that the major and minor numbers should be treated as separate
+ integers and that each may be incremented higher than a single digit.
+ Thus, HTTP/2.4 is a lower version than HTTP/2.13, which in turn is
+ lower than HTTP/12.3. Leading zeros should be ignored by recipients
+ and never generated by senders.
+
+ This document defines both the 0.9 and 1.0 versions of the HTTP
+ protocol. Applications sending Full-Request or Full-Response
+ messages, as defined by this specification, must include an HTTP-
+ Version of "HTTP/1.0".
+
+ HTTP/1.0 servers must:
+
+ o recognize the format of the Request-Line for HTTP/0.9 and
+ HTTP/1.0 requests;
+
+ o understand any valid request in the format of HTTP/0.9 or
+ HTTP/1.0;
+
+ o respond appropriately with a message in the same protocol
+ version used by the client.
+
+ HTTP/1.0 clients must:
+
+ o recognize the format of the Status-Line for HTTP/1.0 responses;
+
+ o understand any valid response in the format of HTTP/0.9 or
+ HTTP/1.0.
+
+ Proxy and gateway applications must be careful in forwarding requests
+ that are received in a format different than that of the
+ application's native HTTP version. Since the protocol version
+ indicates the protocol capability of the sender, a proxy/gateway must
+ never send a message with a version indicator which is greater than
+ its native version; if a higher version request is received, the
+ proxy/gateway must either downgrade the request version or respond
+ with an error. Requests with a version lower than that of the
+ application's native format may be upgraded before being forwarded;
+ the proxy/gateway's response to that request must follow the server
+ requirements listed above.
+
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 13]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+3.2 Uniform Resource Identifiers
+
+ URIs have been known by many names: WWW addresses, Universal Document
+ Identifiers, Universal Resource Identifiers [2], and finally the
+ combination of Uniform Resource Locators (URL) [4] and Names (URN)
+ [16]. As far as HTTP is concerned, Uniform Resource Identifiers are
+ simply formatted strings which identify--via name, location, or any
+ other characteristic--a network resource.
+
+3.2.1 General Syntax
+
+ URIs in HTTP can be represented in absolute form or relative to some
+ known base URI [9], depending upon the context of their use. The two
+ forms are differentiated by the fact that absolute URIs always begin
+ with a scheme name followed by a colon.
+
+ URI = ( absoluteURI | relativeURI ) [ "#" fragment ]
+
+ absoluteURI = scheme ":" *( uchar | reserved )
+
+ relativeURI = net_path | abs_path | rel_path
+
+ net_path = "//" net_loc [ abs_path ]
+ abs_path = "/" rel_path
+ rel_path = [ path ] [ ";" params ] [ "?" query ]
+
+ path = fsegment *( "/" segment )
+ fsegment = 1*pchar
+ segment = *pchar
+
+ params = param *( ";" param )
+ param = *( pchar | "/" )
+
+ scheme = 1*( ALPHA | DIGIT | "+" | "-" | "." )
+ net_loc = *( pchar | ";" | "?" )
+ query = *( uchar | reserved )
+ fragment = *( uchar | reserved )
+
+ pchar = uchar | ":" | "@" | "&" | "=" | "+"
+ uchar = unreserved | escape
+ unreserved = ALPHA | DIGIT | safe | extra | national
+
+ escape = "%" HEX HEX
+ reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"
+ extra = "!" | "*" | "'" | "(" | ")" | ","
+ safe = "$" | "-" | "_" | "."
+ unsafe = CTL | SP | <"> | "#" | "%" | "<" | ">"
+ national = <any OCTET excluding ALPHA, DIGIT,
+
+
+
+Berners-Lee, et al Informational [Page 14]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ reserved, extra, safe, and unsafe>
+
+ For definitive information on URL syntax and semantics, see RFC 1738
+ [4] and RFC 1808 [9]. The BNF above includes national characters not
+ allowed in valid URLs as specified by RFC 1738, since HTTP servers
+ are not restricted in the set of unreserved characters allowed to
+ represent the rel_path part of addresses, and HTTP proxies may
+ receive requests for URIs not defined by RFC 1738.
+
+3.2.2 http URL
+
+ The "http" scheme is used to locate network resources via the HTTP
+ protocol. This section defines the scheme-specific syntax and
+ semantics for http URLs.
+
+ http_URL = "http:" "//" host [ ":" port ] [ abs_path ]
+
+ host = <A legal Internet host domain name
+ or IP address (in dotted-decimal form),
+ as defined by Section 2.1 of RFC 1123>
+
+ port = *DIGIT
+
+ If the port is empty or not given, port 80 is assumed. The semantics
+ are that the identified resource is located at the server listening
+ for TCP connections on that port of that host, and the Request-URI
+ for the resource is abs_path. If the abs_path is not present in the
+ URL, it must be given as "/" when used as a Request-URI (Section
+ 5.1.2).
+
+ Note: Although the HTTP protocol is independent of the transport
+ layer protocol, the http URL only identifies resources by their
+ TCP location, and thus non-TCP resources must be identified by
+ some other URI scheme.
+
+ The canonical form for "http" URLs is obtained by converting any
+ UPALPHA characters in host to their LOALPHA equivalent (hostnames are
+ case-insensitive), eliding the [ ":" port ] if the port is 80, and
+ replacing an empty abs_path with "/".
+
+3.3 Date/Time Formats
+
+ HTTP/1.0 applications have historically allowed three different
+ formats for the representation of date/time stamps:
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+
+
+Berners-Lee, et al Informational [Page 15]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ The first format is preferred as an Internet standard and represents
+ a fixed-length subset of that defined by RFC 1123 [6] (an update to
+ RFC 822 [7]). The second format is in common use, but is based on the
+ obsolete RFC 850 [10] date format and lacks a four-digit year.
+ HTTP/1.0 clients and servers that parse the date value should accept
+ all three formats, though they must never generate the third
+ (asctime) format.
+
+ Note: Recipients of date values are encouraged to be robust in
+ accepting date values that may have been generated by non-HTTP
+ applications, as is sometimes the case when retrieving or posting
+ messages via proxies/gateways to SMTP or NNTP.
+
+ All HTTP/1.0 date/time stamps must be represented in Universal Time
+ (UT), also known as Greenwich Mean Time (GMT), without exception.
+ This is indicated in the first two formats by the inclusion of "GMT"
+ as the three-letter abbreviation for time zone, and should be assumed
+ when reading the asctime format.
+
+ HTTP-date = rfc1123-date | rfc850-date | asctime-date
+
+ rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+ rfc850-date = weekday "," SP date2 SP time SP "GMT"
+ asctime-date = wkday SP date3 SP time SP 4DIGIT
+
+ date1 = 2DIGIT SP month SP 4DIGIT
+ ; day month year (e.g., 02 Jun 1982)
+ date2 = 2DIGIT "-" month "-" 2DIGIT
+ ; day-month-year (e.g., 02-Jun-82)
+ date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
+ ; month day (e.g., Jun 2)
+
+ time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ ; 00:00:00 - 23:59:59
+
+ wkday = "Mon" | "Tue" | "Wed"
+ | "Thu" | "Fri" | "Sat" | "Sun"
+
+ weekday = "Monday" | "Tuesday" | "Wednesday"
+ | "Thursday" | "Friday" | "Saturday" | "Sunday"
+
+ month = "Jan" | "Feb" | "Mar" | "Apr"
+ | "May" | "Jun" | "Jul" | "Aug"
+ | "Sep" | "Oct" | "Nov" | "Dec"
+
+ Note: HTTP requirements for the date/time stamp format apply
+ only to their usage within the protocol stream. Clients and
+ servers are not required to use these formats for user
+
+
+
+Berners-Lee, et al Informational [Page 16]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ presentation, request logging, etc.
+
+3.4 Character Sets
+
+ HTTP uses the same definition of the term "character set" as that
+ described for MIME:
+
+ The term "character set" is used in this document to refer to a
+ method used with one or more tables to convert a sequence of
+ octets into a sequence of characters. Note that unconditional
+ conversion in the other direction is not required, in that not all
+ characters may be available in a given character set and a
+ character set may provide more than one sequence of octets to
+ represent a particular character. This definition is intended to
+ allow various kinds of character encodings, from simple single-
+ table mappings such as US-ASCII to complex table switching methods
+ such as those that use ISO 2022's techniques. However, the
+ definition associated with a MIME character set name must fully
+ specify the mapping to be performed from octets to characters. In
+ particular, use of external profiling information to determine the
+ exact mapping is not permitted.
+
+ Note: This use of the term "character set" is more commonly
+ referred to as a "character encoding." However, since HTTP and
+ MIME share the same registry, it is important that the terminology
+ also be shared.
+
+ HTTP character sets are identified by case-insensitive tokens. The
+ complete set of tokens are defined by the IANA Character Set registry
+ [15]. However, because that registry does not define a single,
+ consistent token for each character set, we define here the preferred
+ names for those character sets most likely to be used with HTTP
+ entities. These character sets include those registered by RFC 1521
+ [5] -- the US-ASCII [17] and ISO-8859 [18] character sets -- and
+ other names specifically recommended for use within MIME charset
+ parameters.
+
+ charset = "US-ASCII"
+ | "ISO-8859-1" | "ISO-8859-2" | "ISO-8859-3"
+ | "ISO-8859-4" | "ISO-8859-5" | "ISO-8859-6"
+ | "ISO-8859-7" | "ISO-8859-8" | "ISO-8859-9"
+ | "ISO-2022-JP" | "ISO-2022-JP-2" | "ISO-2022-KR"
+ | "UNICODE-1-1" | "UNICODE-1-1-UTF-7" | "UNICODE-1-1-UTF-8"
+ | token
+
+ Although HTTP allows an arbitrary token to be used as a charset
+ value, any token that has a predefined value within the IANA
+ Character Set registry [15] must represent the character set defined
+
+
+
+Berners-Lee, et al Informational [Page 17]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ by that registry. Applications should limit their use of character
+ sets to those defined by the IANA registry.
+
+ The character set of an entity body should be labelled as the lowest
+ common denominator of the character codes used within that body, with
+ the exception that no label is preferred over the labels US-ASCII or
+ ISO-8859-1.
+
+3.5 Content Codings
+
+ Content coding values are used to indicate an encoding transformation
+ that has been applied to a resource. Content codings are primarily
+ used to allow a document to be compressed or encrypted without losing
+ the identity of its underlying media type. Typically, the resource is
+ stored in this encoding and only decoded before rendering or
+ analogous usage.
+
+ content-coding = "x-gzip" | "x-compress" | token
+
+ Note: For future compatibility, HTTP/1.0 applications should
+ consider "gzip" and "compress" to be equivalent to "x-gzip"
+ and "x-compress", respectively.
+
+ All content-coding values are case-insensitive. HTTP/1.0 uses
+ content-coding values in the Content-Encoding (Section 10.3) header
+ field. Although the value describes the content-coding, what is more
+ important is that it indicates what decoding mechanism will be
+ required to remove the encoding. Note that a single program may be
+ capable of decoding multiple content-coding formats. Two values are
+ defined by this specification:
+
+ x-gzip
+ An encoding format produced by the file compression program
+ "gzip" (GNU zip) developed by Jean-loup Gailly. This format is
+ typically a Lempel-Ziv coding (LZ77) with a 32 bit CRC.
+
+ x-compress
+ The encoding format produced by the file compression program
+ "compress". This format is an adaptive Lempel-Ziv-Welch coding
+ (LZW).
+
+ Note: Use of program names for the identification of
+ encoding formats is not desirable and should be discouraged
+ for future encodings. Their use here is representative of
+ historical practice, not good design.
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 18]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+3.6 Media Types
+
+ HTTP uses Internet Media Types [13] in the Content-Type header field
+ (Section 10.5) in order to provide open and extensible data typing.
+
+ media-type = type "/" subtype *( ";" parameter )
+ type = token
+ subtype = token
+
+ Parameters may follow the type/subtype in the form of attribute/value
+ pairs.
+
+ parameter = attribute "=" value
+ attribute = token
+ value = token | quoted-string
+
+ The type, subtype, and parameter attribute names are case-
+ insensitive. Parameter values may or may not be case-sensitive,
+ depending on the semantics of the parameter name. LWS must not be
+ generated between the type and subtype, nor between an attribute and
+ its value. Upon receipt of a media type with an unrecognized
+ parameter, a user agent should treat the media type as if the
+ unrecognized parameter and its value were not present.
+
+ Some older HTTP applications do not recognize media type parameters.
+ HTTP/1.0 applications should only use media type parameters when they
+ are necessary to define the content of a message.
+
+ Media-type values are registered with the Internet Assigned Number
+ Authority (IANA [15]). The media type registration process is
+ outlined in RFC 1590 [13]. Use of non-registered media types is
+ discouraged.
+
+3.6.1 Canonicalization and Text Defaults
+
+ Internet media types are registered with a canonical form. In
+ general, an Entity-Body transferred via HTTP must be represented in
+ the appropriate canonical form prior to its transmission. If the body
+ has been encoded with a Content-Encoding, the underlying data should
+ be in canonical form prior to being encoded.
+
+ Media subtypes of the "text" type use CRLF as the text line break
+ when in canonical form. However, HTTP allows the transport of text
+ media with plain CR or LF alone representing a line break when used
+ consistently within the Entity-Body. HTTP applications must accept
+ CRLF, bare CR, and bare LF as being representative of a line break in
+ text media received via HTTP.
+
+
+
+
+Berners-Lee, et al Informational [Page 19]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ In addition, if the text media is represented in a character set that
+ does not use octets 13 and 10 for CR and LF respectively, as is the
+ case for some multi-byte character sets, HTTP allows the use of
+ whatever octet sequences are defined by that character set to
+ represent the equivalent of CR and LF for line breaks. This
+ flexibility regarding line breaks applies only to text media in the
+ Entity-Body; a bare CR or LF should not be substituted for CRLF
+ within any of the HTTP control structures (such as header fields and
+ multipart boundaries).
+
+ The "charset" parameter is used with some media types to define the
+ character set (Section 3.4) of the data. When no explicit charset
+ parameter is provided by the sender, media subtypes of the "text"
+ type are defined to have a default charset value of "ISO-8859-1" when
+ received via HTTP. Data in character sets other than "ISO-8859-1" or
+ its subsets must be labelled with an appropriate charset value in
+ order to be consistently interpreted by the recipient.
+
+ Note: Many current HTTP servers provide data using charsets other
+ than "ISO-8859-1" without proper labelling. This situation reduces
+ interoperability and is not recommended. To compensate for this,
+ some HTTP user agents provide a configuration option to allow the
+ user to change the default interpretation of the media type
+ character set when no charset parameter is given.
+
+3.6.2 Multipart Types
+
+ MIME provides for a number of "multipart" types -- encapsulations of
+ several entities within a single message's Entity-Body. The multipart
+ types registered by IANA [15] do not have any special meaning for
+ HTTP/1.0, though user agents may need to understand each type in
+ order to correctly interpret the purpose of each body-part. An HTTP
+ user agent should follow the same or similar behavior as a MIME user
+ agent does upon receipt of a multipart type. HTTP servers should not
+ assume that all HTTP clients are prepared to handle multipart types.
+
+ All multipart types share a common syntax and must include a boundary
+ parameter as part of the media type value. The message body is itself
+ a protocol element and must therefore use only CRLF to represent line
+ breaks between body-parts. Multipart body-parts may contain HTTP
+ header fields which are significant to the meaning of that part.
+
+3.7 Product Tokens
+
+ Product tokens are used to allow communicating applications to
+ identify themselves via a simple product token, with an optional
+ slash and version designator. Most fields using product tokens also
+ allow subproducts which form a significant part of the application to
+
+
+
+Berners-Lee, et al Informational [Page 20]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ be listed, separated by whitespace. By convention, the products are
+ listed in order of their significance for identifying the
+ application.
+
+ product = token ["/" product-version]
+ product-version = token
+
+ Examples:
+
+ User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+
+ Server: Apache/0.8.4
+
+ Product tokens should be short and to the point -- use of them for
+ advertizing or other non-essential information is explicitly
+ forbidden. Although any token character may appear in a product-
+ version, this token should only be used for a version identifier
+ (i.e., successive versions of the same product should only differ in
+ the product-version portion of the product value).
+
+4. HTTP Message
+
+4.1 Message Types
+
+ HTTP messages consist of requests from client to server and responses
+ from server to client.
+
+ HTTP-message = Simple-Request ; HTTP/0.9 messages
+ | Simple-Response
+ | Full-Request ; HTTP/1.0 messages
+ | Full-Response
+
+ Full-Request and Full-Response use the generic message format of RFC
+ 822 [7] for transferring entities. Both messages may include optional
+ header fields (also known as "headers") and an entity body. The
+ entity body is separated from the headers by a null line (i.e., a
+ line with nothing preceding the CRLF).
+
+ Full-Request = Request-Line ; Section 5.1
+ *( General-Header ; Section 4.3
+ | Request-Header ; Section 5.2
+ | Entity-Header ) ; Section 7.1
+ CRLF
+ [ Entity-Body ] ; Section 7.2
+
+ Full-Response = Status-Line ; Section 6.1
+ *( General-Header ; Section 4.3
+ | Response-Header ; Section 6.2
+
+
+
+Berners-Lee, et al Informational [Page 21]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ | Entity-Header ) ; Section 7.1
+ CRLF
+ [ Entity-Body ] ; Section 7.2
+
+ Simple-Request and Simple-Response do not allow the use of any header
+ information and are limited to a single request method (GET).
+
+ Simple-Request = "GET" SP Request-URI CRLF
+
+ Simple-Response = [ Entity-Body ]
+
+ Use of the Simple-Request format is discouraged because it prevents
+ the server from identifying the media type of the returned entity.
+
+4.2 Message Headers
+
+ HTTP header fields, which include General-Header (Section 4.3),
+ Request-Header (Section 5.2), Response-Header (Section 6.2), and
+ Entity-Header (Section 7.1) fields, follow the same generic format as
+ that given in Section 3.1 of RFC 822 [7]. Each header field consists
+ of a name followed immediately by a colon (":"), a single space (SP)
+ character, and the field value. Field names are case-insensitive.
+ Header fields can be extended over multiple lines by preceding each
+ extra line with at least one SP or HT, though this is not
+ recommended.
+
+ HTTP-header = field-name ":" [ field-value ] CRLF
+
+ field-name = token
+ field-value = *( field-content | LWS )
+
+ field-content = <the OCTETs making up the field-value
+ and consisting of either *TEXT or combinations
+ of token, tspecials, and quoted-string>
+
+ The order in which header fields are received is not significant.
+ However, it is "good practice" to send General-Header fields first,
+ followed by Request-Header or Response-Header fields prior to the
+ Entity-Header fields.
+
+ Multiple HTTP-header fields with the same field-name may be present
+ in a message if and only if the entire field-value for that header
+ field is defined as a comma-separated list [i.e., #(values)]. It must
+ be possible to combine the multiple header fields into one "field-
+ name: field-value" pair, without changing the semantics of the
+ message, by appending each subsequent field-value to the first, each
+ separated by a comma.
+
+
+
+
+Berners-Lee, et al Informational [Page 22]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+4.3 General Header Fields
+
+ There are a few header fields which have general applicability for
+ both request and response messages, but which do not apply to the
+ entity being transferred. These headers apply only to the message
+ being transmitted.
+
+ General-Header = Date ; Section 10.6
+ | Pragma ; Section 10.12
+
+ General header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields may be given the semantics of general
+ header fields if all parties in the communication recognize them to
+ be general header fields. Unrecognized header fields are treated as
+ Entity-Header fields.
+
+5. Request
+
+ A request message from a client to a server includes, within the
+ first line of that message, the method to be applied to the resource,
+ the identifier of the resource, and the protocol version in use. For
+ backwards compatibility with the more limited HTTP/0.9 protocol,
+ there are two valid formats for an HTTP request:
+
+ Request = Simple-Request | Full-Request
+
+ Simple-Request = "GET" SP Request-URI CRLF
+
+ Full-Request = Request-Line ; Section 5.1
+ *( General-Header ; Section 4.3
+ | Request-Header ; Section 5.2
+ | Entity-Header ) ; Section 7.1
+ CRLF
+ [ Entity-Body ] ; Section 7.2
+
+ If an HTTP/1.0 server receives a Simple-Request, it must respond with
+ an HTTP/0.9 Simple-Response. An HTTP/1.0 client capable of receiving
+ a Full-Response should never generate a Simple-Request.
+
+5.1 Request-Line
+
+ The Request-Line begins with a method token, followed by the
+ Request-URI and the protocol version, and ending with CRLF. The
+ elements are separated by SP characters. No CR or LF are allowed
+ except in the final CRLF sequence.
+
+ Request-Line = Method SP Request-URI SP HTTP-Version CRLF
+
+
+
+Berners-Lee, et al Informational [Page 23]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ Note that the difference between a Simple-Request and the Request-
+ Line of a Full-Request is the presence of the HTTP-Version field and
+ the availability of methods other than GET.
+
+5.1.1 Method
+
+ The Method token indicates the method to be performed on the resource
+ identified by the Request-URI. The method is case-sensitive.
+
+ Method = "GET" ; Section 8.1
+ | "HEAD" ; Section 8.2
+ | "POST" ; Section 8.3
+ | extension-method
+
+ extension-method = token
+
+ The list of methods acceptable by a specific resource can change
+ dynamically; the client is notified through the return code of the
+ response if a method is not allowed on a resource. Servers should
+ return the status code 501 (not implemented) if the method is
+ unrecognized or not implemented.
+
+ The methods commonly used by HTTP/1.0 applications are fully defined
+ in Section 8.
+
+5.1.2 Request-URI
+
+ The Request-URI is a Uniform Resource Identifier (Section 3.2) and
+ identifies the resource upon which to apply the request.
+
+ Request-URI = absoluteURI | abs_path
+
+ The two options for Request-URI are dependent on the nature of the
+ request.
+
+ The absoluteURI form is only allowed when the request is being made
+ to a proxy. The proxy is requested to forward the request and return
+ the response. If the request is GET or HEAD and a prior response is
+ cached, the proxy may use the cached message if it passes any
+ restrictions in the Expires header field. Note that the proxy may
+ forward the request on to another proxy or directly to the server
+ specified by the absoluteURI. In order to avoid request loops, a
+ proxy must be able to recognize all of its server names, including
+ any aliases, local variations, and the numeric IP address. An example
+ Request-Line would be:
+
+ GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.0
+
+
+
+
+Berners-Lee, et al Informational [Page 24]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ The most common form of Request-URI is that used to identify a
+ resource on an origin server or gateway. In this case, only the
+ absolute path of the URI is transmitted (see Section 3.2.1,
+ abs_path). For example, a client wishing to retrieve the resource
+ above directly from the origin server would create a TCP connection
+ to port 80 of the host "www.w3.org" and send the line:
+
+ GET /pub/WWW/TheProject.html HTTP/1.0
+
+ followed by the remainder of the Full-Request. Note that the absolute
+ path cannot be empty; if none is present in the original URI, it must
+ be given as "/" (the server root).
+
+ The Request-URI is transmitted as an encoded string, where some
+ characters may be escaped using the "% HEX HEX" encoding defined by
+ RFC 1738 [4]. The origin server must decode the Request-URI in order
+ to properly interpret the request.
+
+5.2 Request Header Fields
+
+ The request header fields allow the client to pass additional
+ information about the request, and about the client itself, to the
+ server. These fields act as request modifiers, with semantics
+ equivalent to the parameters on a programming language method
+ (procedure) invocation.
+
+ Request-Header = Authorization ; Section 10.2
+ | From ; Section 10.8
+ | If-Modified-Since ; Section 10.9
+ | Referer ; Section 10.13
+ | User-Agent ; Section 10.15
+
+ Request-Header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields may be given the semantics of request
+ header fields if all parties in the communication recognize them to
+ be request header fields. Unrecognized header fields are treated as
+ Entity-Header fields.
+
+6. Response
+
+ After receiving and interpreting a request message, a server responds
+ in the form of an HTTP response message.
+
+ Response = Simple-Response | Full-Response
+
+ Simple-Response = [ Entity-Body ]
+
+
+
+
+Berners-Lee, et al Informational [Page 25]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ Full-Response = Status-Line ; Section 6.1
+ *( General-Header ; Section 4.3
+ | Response-Header ; Section 6.2
+ | Entity-Header ) ; Section 7.1
+ CRLF
+ [ Entity-Body ] ; Section 7.2
+
+ A Simple-Response should only be sent in response to an HTTP/0.9
+ Simple-Request or if the server only supports the more limited
+ HTTP/0.9 protocol. If a client sends an HTTP/1.0 Full-Request and
+ receives a response that does not begin with a Status-Line, it should
+ assume that the response is a Simple-Response and parse it
+ accordingly. Note that the Simple-Response consists only of the
+ entity body and is terminated by the server closing the connection.
+
+6.1 Status-Line
+
+ The first line of a Full-Response message is the Status-Line,
+ consisting of the protocol version followed by a numeric status code
+ and its associated textual phrase, with each element separated by SP
+ characters. No CR or LF is allowed except in the final CRLF sequence.
+
+ Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+
+ Since a status line always begins with the protocol version and
+ status code
+
+ "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP
+
+ (e.g., "HTTP/1.0 200 "), the presence of that expression is
+ sufficient to differentiate a Full-Response from a Simple-Response.
+ Although the Simple-Response format may allow such an expression to
+ occur at the beginning of an entity body, and thus cause a
+ misinterpretation of the message if it was given in response to a
+ Full-Request, most HTTP/0.9 servers are limited to responses of type
+ "text/html" and therefore would never generate such a response.
+
+6.1.1 Status Code and Reason Phrase
+
+ The Status-Code element is a 3-digit integer result code of the
+ attempt to understand and satisfy the request. The Reason-Phrase is
+ intended to give a short textual description of the Status-Code. The
+ Status-Code is intended for use by automata and the Reason-Phrase is
+ intended for the human user. The client is not required to examine or
+ display the Reason-Phrase.
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 26]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ The first digit of the Status-Code defines the class of response. The
+ last two digits do not have any categorization role. There are 5
+ values for the first digit:
+
+ o 1xx: Informational - Not used, but reserved for future use
+
+ o 2xx: Success - The action was successfully received,
+ understood, and accepted.
+
+ o 3xx: Redirection - Further action must be taken in order to
+ complete the request
+
+ o 4xx: Client Error - The request contains bad syntax or cannot
+ be fulfilled
+
+ o 5xx: Server Error - The server failed to fulfill an apparently
+ valid request
+
+ The individual values of the numeric status codes defined for
+ HTTP/1.0, and an example set of corresponding Reason-Phrase's, are
+ presented below. The reason phrases listed here are only recommended
+ -- they may be replaced by local equivalents without affecting the
+ protocol. These codes are fully defined in Section 9.
+
+ Status-Code = "200" ; OK
+ | "201" ; Created
+ | "202" ; Accepted
+ | "204" ; No Content
+ | "301" ; Moved Permanently
+ | "302" ; Moved Temporarily
+ | "304" ; Not Modified
+ | "400" ; Bad Request
+ | "401" ; Unauthorized
+ | "403" ; Forbidden
+ | "404" ; Not Found
+ | "500" ; Internal Server Error
+ | "501" ; Not Implemented
+ | "502" ; Bad Gateway
+ | "503" ; Service Unavailable
+ | extension-code
+
+ extension-code = 3DIGIT
+
+ Reason-Phrase = *<TEXT, excluding CR, LF>
+
+ HTTP status codes are extensible, but the above codes are the only
+ ones generally recognized in current practice. HTTP applications are
+ not required to understand the meaning of all registered status
+
+
+
+Berners-Lee, et al Informational [Page 27]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ codes, though such understanding is obviously desirable. However,
+ applications must understand the class of any status code, as
+ indicated by the first digit, and treat any unrecognized response as
+ being equivalent to the x00 status code of that class, with the
+ exception that an unrecognized response must not be cached. For
+ example, if an unrecognized status code of 431 is received by the
+ client, it can safely assume that there was something wrong with its
+ request and treat the response as if it had received a 400 status
+ code. In such cases, user agents should present to the user the
+ entity returned with the response, since that entity is likely to
+ include human-readable information which will explain the unusual
+ status.
+
+6.2 Response Header Fields
+
+ The response header fields allow the server to pass additional
+ information about the response which cannot be placed in the Status-
+ Line. These header fields give information about the server and about
+ further access to the resource identified by the Request-URI.
+
+ Response-Header = Location ; Section 10.11
+ | Server ; Section 10.14
+ | WWW-Authenticate ; Section 10.16
+
+ Response-Header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields may be given the semantics of response
+ header fields if all parties in the communication recognize them to
+ be response header fields. Unrecognized header fields are treated as
+ Entity-Header fields.
+
+7. Entity
+
+ Full-Request and Full-Response messages may transfer an entity within
+ some requests and responses. An entity consists of Entity-Header
+ fields and (usually) an Entity-Body. In this section, both sender and
+ recipient refer to either the client or the server, depending on who
+ sends and who receives the entity.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 28]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+7.1 Entity Header Fields
+
+ Entity-Header fields define optional metainformation about the
+ Entity-Body or, if no body is present, about the resource identified
+ by the request.
+
+ Entity-Header = Allow ; Section 10.1
+ | Content-Encoding ; Section 10.3
+ | Content-Length ; Section 10.4
+ | Content-Type ; Section 10.5
+ | Expires ; Section 10.7
+ | Last-Modified ; Section 10.10
+ | extension-header
+
+ extension-header = HTTP-header
+
+ The extension-header mechanism allows additional Entity-Header fields
+ to be defined without changing the protocol, but these fields cannot
+ be assumed to be recognizable by the recipient. Unrecognized header
+ fields should be ignored by the recipient and forwarded by proxies.
+
+7.2 Entity Body
+
+ The entity body (if any) sent with an HTTP request or response is in
+ a format and encoding defined by the Entity-Header fields.
+
+ Entity-Body = *OCTET
+
+ An entity body is included with a request message only when the
+ request method calls for one. The presence of an entity body in a
+ request is signaled by the inclusion of a Content-Length header field
+ in the request message headers. HTTP/1.0 requests containing an
+ entity body must include a valid Content-Length header field.
+
+ For response messages, whether or not an entity body is included with
+ a message is dependent on both the request method and the response
+ code. All responses to the HEAD request method must not include a
+ body, even though the presence of entity header fields may lead one
+ to believe they do. All 1xx (informational), 204 (no content), and
+ 304 (not modified) responses must not include a body. All other
+ responses must include an entity body or a Content-Length header
+ field defined with a value of zero (0).
+
+7.2.1 Type
+
+ When an Entity-Body is included with a message, the data type of that
+ body is determined via the header fields Content-Type and Content-
+ Encoding. These define a two-layer, ordered encoding model:
+
+
+
+Berners-Lee, et al Informational [Page 29]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ entity-body := Content-Encoding( Content-Type( data ) )
+
+ A Content-Type specifies the media type of the underlying data. A
+ Content-Encoding may be used to indicate any additional content
+ coding applied to the type, usually for the purpose of data
+ compression, that is a property of the resource requested. The
+ default for the content encoding is none (i.e., the identity
+ function).
+
+ Any HTTP/1.0 message containing an entity body should include a
+ Content-Type header field defining the media type of that body. If
+ and only if the media type is not given by a Content-Type header, as
+ is the case for Simple-Response messages, the recipient may attempt
+ to guess the media type via inspection of its content and/or the name
+ extension(s) of the URL used to identify the resource. If the media
+ type remains unknown, the recipient should treat it as type
+ "application/octet-stream".
+
+7.2.2 Length
+
+ When an Entity-Body is included with a message, the length of that
+ body may be determined in one of two ways. If a Content-Length header
+ field is present, its value in bytes represents the length of the
+ Entity-Body. Otherwise, the body length is determined by the closing
+ of the connection by the server.
+
+ Closing the connection cannot be used to indicate the end of a
+ request body, since it leaves no possibility for the server to send
+ back a response. Therefore, HTTP/1.0 requests containing an entity
+ body must include a valid Content-Length header field. If a request
+ contains an entity body and Content-Length is not specified, and the
+ server does not recognize or cannot calculate the length from other
+ fields, then the server should send a 400 (bad request) response.
+
+ Note: Some older servers supply an invalid Content-Length when
+ sending a document that contains server-side includes dynamically
+ inserted into the data stream. It must be emphasized that this
+ will not be tolerated by future versions of HTTP. Unless the
+ client knows that it is receiving a response from a compliant
+ server, it should not depend on the Content-Length value being
+ correct.
+
+8. Method Definitions
+
+ The set of common methods for HTTP/1.0 is defined below. Although
+ this set can be expanded, additional methods cannot be assumed to
+ share the same semantics for separately extended clients and servers.
+
+
+
+
+Berners-Lee, et al Informational [Page 30]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+8.1 GET
+
+ The GET method means retrieve whatever information (in the form of an
+ entity) is identified by the Request-URI. If the Request-URI refers
+ to a data-producing process, it is the produced data which shall be
+ returned as the entity in the response and not the source text of the
+ process, unless that text happens to be the output of the process.
+
+ The semantics of the GET method changes to a "conditional GET" if the
+ request message includes an If-Modified-Since header field. A
+ conditional GET method requests that the identified resource be
+ transferred only if it has been modified since the date given by the
+ If-Modified-Since header, as described in Section 10.9. The
+ conditional GET method is intended to reduce network usage by
+ allowing cached entities to be refreshed without requiring multiple
+ requests or transferring unnecessary data.
+
+8.2 HEAD
+
+ The HEAD method is identical to GET except that the server must not
+ return any Entity-Body in the response. The metainformation contained
+ in the HTTP headers in response to a HEAD request should be identical
+ to the information sent in response to a GET request. This method can
+ be used for obtaining metainformation about the resource identified
+ by the Request-URI without transferring the Entity-Body itself. This
+ method is often used for testing hypertext links for validity,
+ accessibility, and recent modification.
+
+ There is no "conditional HEAD" request analogous to the conditional
+ GET. If an If-Modified-Since header field is included with a HEAD
+ request, it should be ignored.
+
+8.3 POST
+
+ The POST method is used to request that the destination server accept
+ the entity enclosed in the request as a new subordinate of the
+ resource identified by the Request-URI in the Request-Line. POST is
+ designed to allow a uniform method to cover the following functions:
+
+ o Annotation of existing resources;
+
+ o Posting a message to a bulletin board, newsgroup, mailing list,
+ or similar group of articles;
+
+ o Providing a block of data, such as the result of submitting a
+ form [3], to a data-handling process;
+
+ o Extending a database through an append operation.
+
+
+
+Berners-Lee, et al Informational [Page 31]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ The actual function performed by the POST method is determined by the
+ server and is usually dependent on the Request-URI. The posted entity
+ is subordinate to that URI in the same way that a file is subordinate
+ to a directory containing it, a news article is subordinate to a
+ newsgroup to which it is posted, or a record is subordinate to a
+ database.
+
+ A successful POST does not require that the entity be created as a
+ resource on the origin server or made accessible for future
+ reference. That is, the action performed by the POST method might not
+ result in a resource that can be identified by a URI. In this case,
+ either 200 (ok) or 204 (no content) is the appropriate response
+ status, depending on whether or not the response includes an entity
+ that describes the result.
+
+ If a resource has been created on the origin server, the response
+ should be 201 (created) and contain an entity (preferably of type
+ "text/html") which describes the status of the request and refers to
+ the new resource.
+
+ A valid Content-Length is required on all HTTP/1.0 POST requests. An
+ HTTP/1.0 server should respond with a 400 (bad request) message if it
+ cannot determine the length of the request message's content.
+
+ Applications must not cache responses to a POST request because the
+ application has no way of knowing that the server would return an
+ equivalent response on some future request.
+
+9. Status Code Definitions
+
+ Each Status-Code is described below, including a description of which
+ method(s) it can follow and any metainformation required in the
+ response.
+
+9.1 Informational 1xx
+
+ This class of status code indicates a provisional response,
+ consisting only of the Status-Line and optional headers, and is
+ terminated by an empty line. HTTP/1.0 does not define any 1xx status
+ codes and they are not a valid response to a HTTP/1.0 request.
+ However, they may be useful for experimental applications which are
+ outside the scope of this specification.
+
+9.2 Successful 2xx
+
+ This class of status code indicates that the client's request was
+ successfully received, understood, and accepted.
+
+
+
+
+Berners-Lee, et al Informational [Page 32]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ 200 OK
+
+ The request has succeeded. The information returned with the
+ response is dependent on the method used in the request, as follows:
+
+ GET an entity corresponding to the requested resource is sent
+ in the response;
+
+ HEAD the response must only contain the header information and
+ no Entity-Body;
+
+ POST an entity describing or containing the result of the action.
+
+ 201 Created
+
+ The request has been fulfilled and resulted in a new resource being
+ created. The newly created resource can be referenced by the URI(s)
+ returned in the entity of the response. The origin server should
+ create the resource before using this Status-Code. If the action
+ cannot be carried out immediately, the server must include in the
+ response body a description of when the resource will be available;
+ otherwise, the server should respond with 202 (accepted).
+
+ Of the methods defined by this specification, only POST can create a
+ resource.
+
+ 202 Accepted
+
+ The request has been accepted for processing, but the processing
+ has not been completed. The request may or may not eventually be
+ acted upon, as it may be disallowed when processing actually takes
+ place. There is no facility for re-sending a status code from an
+ asynchronous operation such as this.
+
+ The 202 response is intentionally non-committal. Its purpose is to
+ allow a server to accept a request for some other process (perhaps
+ a batch-oriented process that is only run once per day) without
+ requiring that the user agent's connection to the server persist
+ until the process is completed. The entity returned with this
+ response should include an indication of the request's current
+ status and either a pointer to a status monitor or some estimate of
+ when the user can expect the request to be fulfilled.
+
+ 204 No Content
+
+ The server has fulfilled the request but there is no new
+ information to send back. If the client is a user agent, it should
+ not change its document view from that which caused the request to
+
+
+
+Berners-Lee, et al Informational [Page 33]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ be generated. This response is primarily intended to allow input
+ for scripts or other actions to take place without causing a change
+ to the user agent's active document view. The response may include
+ new metainformation in the form of entity headers, which should
+ apply to the document currently in the user agent's active view.
+
+9.3 Redirection 3xx
+
+ This class of status code indicates that further action needs to be
+ taken by the user agent in order to fulfill the request. The action
+ required may be carried out by the user agent without interaction
+ with the user if and only if the method used in the subsequent
+ request is GET or HEAD. A user agent should never automatically
+ redirect a request more than 5 times, since such redirections usually
+ indicate an infinite loop.
+
+ 300 Multiple Choices
+
+ This response code is not directly used by HTTP/1.0 applications,
+ but serves as the default for interpreting the 3xx class of
+ responses.
+
+ The requested resource is available at one or more locations.
+ Unless it was a HEAD request, the response should include an entity
+ containing a list of resource characteristics and locations from
+ which the user or user agent can choose the one most appropriate.
+ If the server has a preferred choice, it should include the URL in
+ a Location field; user agents may use this field value for
+ automatic redirection.
+
+ 301 Moved Permanently
+
+ The requested resource has been assigned a new permanent URL and
+ any future references to this resource should be done using that
+ URL. Clients with link editing capabilities should automatically
+ relink references to the Request-URI to the new reference returned
+ by the server, where possible.
+
+ The new URL must be given by the Location field in the response.
+ Unless it was a HEAD request, the Entity-Body of the response
+ should contain a short note with a hyperlink to the new URL.
+
+ If the 301 status code is received in response to a request using
+ the POST method, the user agent must not automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+
+
+
+
+Berners-Lee, et al Informational [Page 34]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ Note: When automatically redirecting a POST request after
+ receiving a 301 status code, some existing user agents will
+ erroneously change it into a GET request.
+
+ 302 Moved Temporarily
+
+ The requested resource resides temporarily under a different URL.
+ Since the redirection may be altered on occasion, the client should
+ continue to use the Request-URI for future requests.
+
+ The URL must be given by the Location field in the response. Unless
+ it was a HEAD request, the Entity-Body of the response should
+ contain a short note with a hyperlink to the new URI(s).
+
+ If the 302 status code is received in response to a request using
+ the POST method, the user agent must not automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+ Note: When automatically redirecting a POST request after
+ receiving a 302 status code, some existing user agents will
+ erroneously change it into a GET request.
+
+ 304 Not Modified
+
+ If the client has performed a conditional GET request and access is
+ allowed, but the document has not been modified since the date and
+ time specified in the If-Modified-Since field, the server must
+ respond with this status code and not send an Entity-Body to the
+ client. Header fields contained in the response should only include
+ information which is relevant to cache managers or which may have
+ changed independently of the entity's Last-Modified date. Examples
+ of relevant header fields include: Date, Server, and Expires. A
+ cache should update its cached entity to reflect any new field
+ values given in the 304 response.
+
+9.4 Client Error 4xx
+
+ The 4xx class of status code is intended for cases in which the
+ client seems to have erred. If the client has not completed the
+ request when a 4xx code is received, it should immediately cease
+ sending data to the server. Except when responding to a HEAD request,
+ the server should include an entity containing an explanation of the
+ error situation, and whether it is a temporary or permanent
+ condition. These status codes are applicable to any request method.
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 35]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ Note: If the client is sending data, server implementations on TCP
+ should be careful to ensure that the client acknowledges receipt
+ of the packet(s) containing the response prior to closing the
+ input connection. If the client continues sending data to the
+ server after the close, the server's controller will send a reset
+ packet to the client, which may erase the client's unacknowledged
+ input buffers before they can be read and interpreted by the HTTP
+ application.
+
+ 400 Bad Request
+
+ The request could not be understood by the server due to malformed
+ syntax. The client should not repeat the request without
+ modifications.
+
+ 401 Unauthorized
+
+ The request requires user authentication. The response must include
+ a WWW-Authenticate header field (Section 10.16) containing a
+ challenge applicable to the requested resource. The client may
+ repeat the request with a suitable Authorization header field
+ (Section 10.2). If the request already included Authorization
+ credentials, then the 401 response indicates that authorization has
+ been refused for those credentials. If the 401 response contains
+ the same challenge as the prior response, and the user agent has
+ already attempted authentication at least once, then the user
+ should be presented the entity that was given in the response,
+ since that entity may include relevant diagnostic information. HTTP
+ access authentication is explained in Section 11.
+
+ 403 Forbidden
+
+ The server understood the request, but is refusing to fulfill it.
+ Authorization will not help and the request should not be repeated.
+ If the request method was not HEAD and the server wishes to make
+ public why the request has not been fulfilled, it should describe
+ the reason for the refusal in the entity body. This status code is
+ commonly used when the server does not wish to reveal exactly why
+ the request has been refused, or when no other response is
+ applicable.
+
+ 404 Not Found
+
+ The server has not found anything matching the Request-URI. No
+ indication is given of whether the condition is temporary or
+ permanent. If the server does not wish to make this information
+ available to the client, the status code 403 (forbidden) can be
+ used instead.
+
+
+
+Berners-Lee, et al Informational [Page 36]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+9.5 Server Error 5xx
+
+ Response status codes beginning with the digit "5" indicate cases in
+ which the server is aware that it has erred or is incapable of
+ performing the request. If the client has not completed the request
+ when a 5xx code is received, it should immediately cease sending data
+ to the server. Except when responding to a HEAD request, the server
+ should include an entity containing an explanation of the error
+ situation, and whether it is a temporary or permanent condition.
+ These response codes are applicable to any request method and there
+ are no required header fields.
+
+ 500 Internal Server Error
+
+ The server encountered an unexpected condition which prevented it
+ from fulfilling the request.
+
+ 501 Not Implemented
+
+ The server does not support the functionality required to fulfill
+ the request. This is the appropriate response when the server does
+ not recognize the request method and is not capable of supporting
+ it for any resource.
+
+ 502 Bad Gateway
+
+ The server, while acting as a gateway or proxy, received an invalid
+ response from the upstream server it accessed in attempting to
+ fulfill the request.
+
+ 503 Service Unavailable
+
+ The server is currently unable to handle the request due to a
+ temporary overloading or maintenance of the server. The implication
+ is that this is a temporary condition which will be alleviated
+ after some delay.
+
+ Note: The existence of the 503 status code does not imply
+ that a server must use it when becoming overloaded. Some
+ servers may wish to simply refuse the connection.
+
+10. Header Field Definitions
+
+ This section defines the syntax and semantics of all commonly used
+ HTTP/1.0 header fields. For general and entity header fields, both
+ sender and recipient refer to either the client or the server,
+ depending on who sends and who receives the message.
+
+
+
+
+Berners-Lee, et al Informational [Page 37]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+10.1 Allow
+
+ The Allow entity-header field lists the set of methods supported by
+ the resource identified by the Request-URI. The purpose of this field
+ is strictly to inform the recipient of valid methods associated with
+ the resource. The Allow header field is not permitted in a request
+ using the POST method, and thus should be ignored if it is received
+ as part of a POST entity.
+
+ Allow = "Allow" ":" 1#method
+
+ Example of use:
+
+ Allow: GET, HEAD
+
+ This field cannot prevent a client from trying other methods.
+ However, the indications given by the Allow header field value should
+ be followed. The actual set of allowed methods is defined by the
+ origin server at the time of each request.
+
+ A proxy must not modify the Allow header field even if it does not
+ understand all the methods specified, since the user agent may have
+ other means of communicating with the origin server.
+
+ The Allow header field does not indicate what methods are implemented
+ by the server.
+
+10.2 Authorization
+
+ A user agent that wishes to authenticate itself with a server--
+ usually, but not necessarily, after receiving a 401 response--may do
+ so by including an Authorization request-header field with the
+ request. The Authorization field value consists of credentials
+ containing the authentication information of the user agent for the
+ realm of the resource being requested.
+
+ Authorization = "Authorization" ":" credentials
+
+ HTTP access authentication is described in Section 11. If a request
+ is authenticated and a realm specified, the same credentials should
+ be valid for all other requests within this realm.
+
+ Responses to requests containing an Authorization field are not
+ cachable.
+
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 38]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+10.3 Content-Encoding
+
+ The Content-Encoding entity-header field is used as a modifier to the
+ media-type. When present, its value indicates what additional content
+ coding has been applied to the resource, and thus what decoding
+ mechanism must be applied in order to obtain the media-type
+ referenced by the Content-Type header field. The Content-Encoding is
+ primarily used to allow a document to be compressed without losing
+ the identity of its underlying media type.
+
+ Content-Encoding = "Content-Encoding" ":" content-coding
+
+ Content codings are defined in Section 3.5. An example of its use is
+
+ Content-Encoding: x-gzip
+
+ The Content-Encoding is a characteristic of the resource identified
+ by the Request-URI. Typically, the resource is stored with this
+ encoding and is only decoded before rendering or analogous usage.
+
+10.4 Content-Length
+
+ The Content-Length entity-header field indicates the size of the
+ Entity-Body, in decimal number of octets, sent to the recipient or,
+ in the case of the HEAD method, the size of the Entity-Body that
+ would have been sent had the request been a GET.
+
+ Content-Length = "Content-Length" ":" 1*DIGIT
+
+ An example is
+
+ Content-Length: 3495
+
+ Applications should use this field to indicate the size of the
+ Entity-Body to be transferred, regardless of the media type of the
+ entity. A valid Content-Length field value is required on all
+ HTTP/1.0 request messages containing an entity body.
+
+ Any Content-Length greater than or equal to zero is a valid value.
+ Section 7.2.2 describes how to determine the length of a response
+ entity body if a Content-Length is not given.
+
+ Note: The meaning of this field is significantly different from
+ the corresponding definition in MIME, where it is an optional
+ field used within the "message/external-body" content-type. In
+ HTTP, it should be used whenever the entity's length can be
+ determined prior to being transferred.
+
+
+
+
+Berners-Lee, et al Informational [Page 39]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+10.5 Content-Type
+
+ The Content-Type entity-header field indicates the media type of the
+ Entity-Body sent to the recipient or, in the case of the HEAD method,
+ the media type that would have been sent had the request been a GET.
+
+ Content-Type = "Content-Type" ":" media-type
+
+ Media types are defined in Section 3.6. An example of the field is
+
+ Content-Type: text/html
+
+ Further discussion of methods for identifying the media type of an
+ entity is provided in Section 7.2.1.
+
+10.6 Date
+
+ The Date general-header field represents the date and time at which
+ the message was originated, having the same semantics as orig-date in
+ RFC 822. The field value is an HTTP-date, as described in Section
+ 3.3.
+
+ Date = "Date" ":" HTTP-date
+
+ An example is
+
+ Date: Tue, 15 Nov 1994 08:12:31 GMT
+
+ If a message is received via direct connection with the user agent
+ (in the case of requests) or the origin server (in the case of
+ responses), then the date can be assumed to be the current date at
+ the receiving end. However, since the date--as it is believed by the
+ origin--is important for evaluating cached responses, origin servers
+ should always include a Date header. Clients should only send a Date
+ header field in messages that include an entity body, as in the case
+ of the POST request, and even then it is optional. A received message
+ which does not have a Date header field should be assigned one by the
+ recipient if the message will be cached by that recipient or
+ gatewayed via a protocol which requires a Date.
+
+ In theory, the date should represent the moment just before the
+ entity is generated. In practice, the date can be generated at any
+ time during the message origination without affecting its semantic
+ value.
+
+ Note: An earlier version of this document incorrectly specified
+ that this field should contain the creation date of the enclosed
+ Entity-Body. This has been changed to reflect actual (and proper)
+
+
+
+Berners-Lee, et al Informational [Page 40]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ usage.
+
+10.7 Expires
+
+ The Expires entity-header field gives the date/time after which the
+ entity should be considered stale. This allows information providers
+ to suggest the volatility of the resource, or a date after which the
+ information may no longer be valid. Applications must not cache this
+ entity beyond the date given. The presence of an Expires field does
+ not imply that the original resource will change or cease to exist
+ at, before, or after that time. However, information providers that
+ know or even suspect that a resource will change by a certain date
+ should include an Expires header with that date. The format is an
+ absolute date and time as defined by HTTP-date in Section 3.3.
+
+ Expires = "Expires" ":" HTTP-date
+
+ An example of its use is
+
+ Expires: Thu, 01 Dec 1994 16:00:00 GMT
+
+ If the date given is equal to or earlier than the value of the Date
+ header, the recipient must not cache the enclosed entity. If a
+ resource is dynamic by nature, as is the case with many data-
+ producing processes, entities from that resource should be given an
+ appropriate Expires value which reflects that dynamism.
+
+ The Expires field cannot be used to force a user agent to refresh its
+ display or reload a resource; its semantics apply only to caching
+ mechanisms, and such mechanisms need only check a resource's
+ expiration status when a new request for that resource is initiated.
+
+ User agents often have history mechanisms, such as "Back" buttons and
+ history lists, which can be used to redisplay an entity retrieved
+ earlier in a session. By default, the Expires field does not apply to
+ history mechanisms. If the entity is still in storage, a history
+ mechanism should display it even if the entity has expired, unless
+ the user has specifically configured the agent to refresh expired
+ history documents.
+
+ Note: Applications are encouraged to be tolerant of bad or
+ misinformed implementations of the Expires header. A value of zero
+ (0) or an invalid date format should be considered equivalent to
+ an "expires immediately." Although these values are not legitimate
+ for HTTP/1.0, a robust implementation is always desirable.
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 41]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+10.8 From
+
+ The From request-header field, if given, should contain an Internet
+ e-mail address for the human user who controls the requesting user
+ agent. The address should be machine-usable, as defined by mailbox in
+ RFC 822 [7] (as updated by RFC 1123 [6]):
+
+ From = "From" ":" mailbox
+
+ An example is:
+
+ From: webmaster@w3.org
+
+ This header field may be used for logging purposes and as a means for
+ identifying the source of invalid or unwanted requests. It should not
+ be used as an insecure form of access protection. The interpretation
+ of this field is that the request is being performed on behalf of the
+ person given, who accepts responsibility for the method performed. In
+ particular, robot agents should include this header so that the
+ person responsible for running the robot can be contacted if problems
+ occur on the receiving end.
+
+ The Internet e-mail address in this field may be separate from the
+ Internet host which issued the request. For example, when a request
+ is passed through a proxy, the original issuer's address should be
+ used.
+
+ Note: The client should not send the From header field without the
+ user's approval, as it may conflict with the user's privacy
+ interests or their site's security policy. It is strongly
+ recommended that the user be able to disable, enable, and modify
+ the value of this field at any time prior to a request.
+
+10.9 If-Modified-Since
+
+ The If-Modified-Since request-header field is used with the GET
+ method to make it conditional: if the requested resource has not been
+ modified since the time specified in this field, a copy of the
+ resource will not be returned from the server; instead, a 304 (not
+ modified) response will be returned without any Entity-Body.
+
+ If-Modified-Since = "If-Modified-Since" ":" HTTP-date
+
+ An example of the field is:
+
+ If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+
+
+
+
+
+Berners-Lee, et al Informational [Page 42]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ A conditional GET method requests that the identified resource be
+ transferred only if it has been modified since the date given by the
+ If-Modified-Since header. The algorithm for determining this includes
+ the following cases:
+
+ a) If the request would normally result in anything other than
+ a 200 (ok) status, or if the passed If-Modified-Since date
+ is invalid, the response is exactly the same as for a
+ normal GET. A date which is later than the server's current
+ time is invalid.
+
+ b) If the resource has been modified since the
+ If-Modified-Since date, the response is exactly the same as
+ for a normal GET.
+
+ c) If the resource has not been modified since a valid
+ If-Modified-Since date, the server shall return a 304 (not
+ modified) response.
+
+ The purpose of this feature is to allow efficient updates of cached
+ information with a minimum amount of transaction overhead.
+
+10.10 Last-Modified
+
+ The Last-Modified entity-header field indicates the date and time at
+ which the sender believes the resource was last modified. The exact
+ semantics of this field are defined in terms of how the recipient
+ should interpret it: if the recipient has a copy of this resource
+ which is older than the date given by the Last-Modified field, that
+ copy should be considered stale.
+
+ Last-Modified = "Last-Modified" ":" HTTP-date
+
+ An example of its use is
+
+ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
+
+ The exact meaning of this header field depends on the implementation
+ of the sender and the nature of the original resource. For files, it
+ may be just the file system last-modified time. For entities with
+ dynamically included parts, it may be the most recent of the set of
+ last-modify times for its component parts. For database gateways, it
+ may be the last-update timestamp of the record. For virtual objects,
+ it may be the last time the internal state changed.
+
+ An origin server must not send a Last-Modified date which is later
+ than the server's time of message origination. In such cases, where
+ the resource's last modification would indicate some time in the
+
+
+
+Berners-Lee, et al Informational [Page 43]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ future, the server must replace that date with the message
+ origination date.
+
+10.11 Location
+
+ The Location response-header field defines the exact location of the
+ resource that was identified by the Request-URI. For 3xx responses,
+ the location must indicate the server's preferred URL for automatic
+ redirection to the resource. Only one absolute URL is allowed.
+
+ Location = "Location" ":" absoluteURI
+
+ An example is
+
+ Location: http://www.w3.org/hypertext/WWW/NewLocation.html
+
+10.12 Pragma
+
+ The Pragma general-header field is used to include implementation-
+ specific directives that may apply to any recipient along the
+ request/response chain. All pragma directives specify optional
+ behavior from the viewpoint of the protocol; however, some systems
+ may require that behavior be consistent with the directives.
+
+ Pragma = "Pragma" ":" 1#pragma-directive
+
+ pragma-directive = "no-cache" | extension-pragma
+ extension-pragma = token [ "=" word ]
+
+ When the "no-cache" directive is present in a request message, an
+ application should forward the request toward the origin server even
+ if it has a cached copy of what is being requested. This allows a
+ client to insist upon receiving an authoritative response to its
+ request. It also allows a client to refresh a cached copy which is
+ known to be corrupted or stale.
+
+ Pragma directives must be passed through by a proxy or gateway
+ application, regardless of their significance to that application,
+ since the directives may be applicable to all recipients along the
+ request/response chain. It is not possible to specify a pragma for a
+ specific recipient; however, any pragma directive not relevant to a
+ recipient should be ignored by that recipient.
+
+10.13 Referer
+
+ The Referer request-header field allows the client to specify, for
+ the server's benefit, the address (URI) of the resource from which
+ the Request-URI was obtained. This allows a server to generate lists
+
+
+
+Berners-Lee, et al Informational [Page 44]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ of back-links to resources for interest, logging, optimized caching,
+ etc. It also allows obsolete or mistyped links to be traced for
+ maintenance. The Referer field must not be sent if the Request-URI
+ was obtained from a source that does not have its own URI, such as
+ input from the user keyboard.
+
+ Referer = "Referer" ":" ( absoluteURI | relativeURI )
+
+ Example:
+
+ Referer: http://www.w3.org/hypertext/DataSources/Overview.html
+
+ If a partial URI is given, it should be interpreted relative to the
+ Request-URI. The URI must not include a fragment.
+
+ Note: Because the source of a link may be private information or
+ may reveal an otherwise private information source, it is strongly
+ recommended that the user be able to select whether or not the
+ Referer field is sent. For example, a browser client could have a
+ toggle switch for browsing openly/anonymously, which would
+ respectively enable/disable the sending of Referer and From
+ information.
+
+10.14 Server
+
+ The Server response-header field contains information about the
+ software used by the origin server to handle the request. The field
+ can contain multiple product tokens (Section 3.7) and comments
+ identifying the server and any significant subproducts. By
+ convention, the product tokens are listed in order of their
+ significance for identifying the application.
+
+ Server = "Server" ":" 1*( product | comment )
+
+ Example:
+
+ Server: CERN/3.0 libwww/2.17
+
+ If the response is being forwarded through a proxy, the proxy
+ application must not add its data to the product list.
+
+ Note: Revealing the specific software version of the server may
+ allow the server machine to become more vulnerable to attacks
+ against software that is known to contain security holes. Server
+ implementors are encouraged to make this field a configurable
+ option.
+
+
+
+
+
+Berners-Lee, et al Informational [Page 45]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ Note: Some existing servers fail to restrict themselves to the
+ product token syntax within the Server field.
+
+10.15 User-Agent
+
+ The User-Agent request-header field contains information about the
+ user agent originating the request. This is for statistical purposes,
+ the tracing of protocol violations, and automated recognition of user
+ agents for the sake of tailoring responses to avoid particular user
+ agent limitations. Although it is not required, user agents should
+ include this field with requests. The field can contain multiple
+ product tokens (Section 3.7) and comments identifying the agent and
+ any subproducts which form a significant part of the user agent. By
+ convention, the product tokens are listed in order of their
+ significance for identifying the application.
+
+ User-Agent = "User-Agent" ":" 1*( product | comment )
+
+ Example:
+
+ User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+
+ Note: Some current proxy applications append their product
+ information to the list in the User-Agent field. This is not
+ recommended, since it makes machine interpretation of these
+ fields ambiguous.
+
+ Note: Some existing clients fail to restrict themselves to
+ the product token syntax within the User-Agent field.
+
+10.16 WWW-Authenticate
+
+ The WWW-Authenticate response-header field must be included in 401
+ (unauthorized) response messages. The field value consists of at
+ least one challenge that indicates the authentication scheme(s) and
+ parameters applicable to the Request-URI.
+
+ WWW-Authenticate = "WWW-Authenticate" ":" 1#challenge
+
+ The HTTP access authentication process is described in Section 11.
+ User agents must take special care in parsing the WWW-Authenticate
+ field value if it contains more than one challenge, or if more than
+ one WWW-Authenticate header field is provided, since the contents of
+ a challenge may itself contain a comma-separated list of
+ authentication parameters.
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 46]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+11. Access Authentication
+
+ HTTP provides a simple challenge-response authentication mechanism
+ which may be used by a server to challenge a client request and by a
+ client to provide authentication information. It uses an extensible,
+ case-insensitive token to identify the authentication scheme,
+ followed by a comma-separated list of attribute-value pairs which
+ carry the parameters necessary for achieving authentication via that
+ scheme.
+
+ auth-scheme = token
+
+ auth-param = token "=" quoted-string
+
+ The 401 (unauthorized) response message is used by an origin server
+ to challenge the authorization of a user agent. This response must
+ include a WWW-Authenticate header field containing at least one
+ challenge applicable to the requested resource.
+
+ challenge = auth-scheme 1*SP realm *( "," auth-param )
+
+ realm = "realm" "=" realm-value
+ realm-value = quoted-string
+
+ The realm attribute (case-insensitive) is required for all
+ authentication schemes which issue a challenge. The realm value
+ (case-sensitive), in combination with the canonical root URL of the
+ server being accessed, defines the protection space. These realms
+ allow the protected resources on a server to be partitioned into a
+ set of protection spaces, each with its own authentication scheme
+ and/or authorization database. The realm value is a string, generally
+ assigned by the origin server, which may have additional semantics
+ specific to the authentication scheme.
+
+ A user agent that wishes to authenticate itself with a server--
+ usually, but not necessarily, after receiving a 401 response--may do
+ so by including an Authorization header field with the request. The
+ Authorization field value consists of credentials containing the
+ authentication information of the user agent for the realm of the
+ resource being requested.
+
+ credentials = basic-credentials
+ | ( auth-scheme #auth-param )
+
+ The domain over which credentials can be automatically applied by a
+ user agent is determined by the protection space. If a prior request
+ has been authorized, the same credentials may be reused for all other
+ requests within that protection space for a period of time determined
+
+
+
+Berners-Lee, et al Informational [Page 47]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ by the authentication scheme, parameters, and/or user preference.
+ Unless otherwise defined by the authentication scheme, a single
+ protection space cannot extend outside the scope of its server.
+
+ If the server does not wish to accept the credentials sent with a
+ request, it should return a 403 (forbidden) response.
+
+ The HTTP protocol does not restrict applications to this simple
+ challenge-response mechanism for access authentication. Additional
+ mechanisms may be used, such as encryption at the transport level or
+ via message encapsulation, and with additional header fields
+ specifying authentication information. However, these additional
+ mechanisms are not defined by this specification.
+
+ Proxies must be completely transparent regarding user agent
+ authentication. That is, they must forward the WWW-Authenticate and
+ Authorization headers untouched, and must not cache the response to a
+ request containing Authorization. HTTP/1.0 does not provide a means
+ for a client to be authenticated with a proxy.
+
+11.1 Basic Authentication Scheme
+
+ The "basic" authentication scheme is based on the model that the user
+ agent must authenticate itself with a user-ID and a password for each
+ realm. The realm value should be considered an opaque string which
+ can only be compared for equality with other realms on that server.
+ The server will authorize the request only if it can validate the
+ user-ID and password for the protection space of the Request-URI.
+ There are no optional authentication parameters.
+
+ Upon receipt of an unauthorized request for a URI within the
+ protection space, the server should respond with a challenge like the
+ following:
+
+ WWW-Authenticate: Basic realm="WallyWorld"
+
+ where "WallyWorld" is the string assigned by the server to identify
+ the protection space of the Request-URI.
+
+ To receive authorization, the client sends the user-ID and password,
+ separated by a single colon (":") character, within a base64 [5]
+ encoded string in the credentials.
+
+ basic-credentials = "Basic" SP basic-cookie
+
+ basic-cookie = <base64 [5] encoding of userid-password,
+ except not limited to 76 char/line>
+
+
+
+
+Berners-Lee, et al Informational [Page 48]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ userid-password = [ token ] ":" *TEXT
+
+ If the user agent wishes to send the user-ID "Aladdin" and password
+ "open sesame", it would use the following header field:
+
+ Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+
+ The basic authentication scheme is a non-secure method of filtering
+ unauthorized access to resources on an HTTP server. It is based on
+ the assumption that the connection between the client and the server
+ can be regarded as a trusted carrier. As this is not generally true
+ on an open network, the basic authentication scheme should be used
+ accordingly. In spite of this, clients should implement the scheme in
+ order to communicate with servers that use it.
+
+12. Security Considerations
+
+ This section is meant to inform application developers, information
+ providers, and users of the security limitations in HTTP/1.0 as
+ described by this document. The discussion does not include
+ definitive solutions to the problems revealed, though it does make
+ some suggestions for reducing security risks.
+
+12.1 Authentication of Clients
+
+ As mentioned in Section 11.1, the Basic authentication scheme is not
+ a secure method of user authentication, nor does it prevent the
+ Entity-Body from being transmitted in clear text across the physical
+ network used as the carrier. HTTP/1.0 does not prevent additional
+ authentication schemes and encryption mechanisms from being employed
+ to increase security.
+
+12.2 Safe Methods
+
+ The writers of client software should be aware that the software
+ represents the user in their interactions over the Internet, and
+ should be careful to allow the user to be aware of any actions they
+ may take which may have an unexpected significance to themselves or
+ others.
+
+ In particular, the convention has been established that the GET and
+ HEAD methods should never have the significance of taking an action
+ other than retrieval. These methods should be considered "safe." This
+ allows user agents to represent other methods, such as POST, in a
+ special way, so that the user is made aware of the fact that a
+ possibly unsafe action is being requested.
+
+
+
+
+
+Berners-Lee, et al Informational [Page 49]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ Naturally, it is not possible to ensure that the server does not
+ generate side-effects as a result of performing a GET request; in
+ fact, some dynamic resources consider that a feature. The important
+ distinction here is that the user did not request the side-effects,
+ so therefore cannot be held accountable for them.
+
+12.3 Abuse of Server Log Information
+
+ A server is in the position to save personal data about a user's
+ requests which may identify their reading patterns or subjects of
+ interest. This information is clearly confidential in nature and its
+ handling may be constrained by law in certain countries. People using
+ the HTTP protocol to provide data are responsible for ensuring that
+ such material is not distributed without the permission of any
+ individuals that are identifiable by the published results.
+
+12.4 Transfer of Sensitive Information
+
+ Like any generic data transfer protocol, HTTP cannot regulate the
+ content of the data that is transferred, nor is there any a priori
+ method of determining the sensitivity of any particular piece of
+ information within the context of any given request. Therefore,
+ applications should supply as much control over this information as
+ possible to the provider of that information. Three header fields are
+ worth special mention in this context: Server, Referer and From.
+
+ Revealing the specific software version of the server may allow the
+ server machine to become more vulnerable to attacks against software
+ that is known to contain security holes. Implementors should make the
+ Server header field a configurable option.
+
+ The Referer field allows reading patterns to be studied and reverse
+ links drawn. Although it can be very useful, its power can be abused
+ if user details are not separated from the information contained in
+ the Referer. Even when the personal information has been removed, the
+ Referer field may indicate a private document's URI whose publication
+ would be inappropriate.
+
+ The information sent in the From field might conflict with the user's
+ privacy interests or their site's security policy, and hence it
+ should not be transmitted without the user being able to disable,
+ enable, and modify the contents of the field. The user must be able
+ to set the contents of this field within a user preference or
+ application defaults configuration.
+
+ We suggest, though do not require, that a convenient toggle interface
+ be provided for the user to enable or disable the sending of From and
+ Referer information.
+
+
+
+Berners-Lee, et al Informational [Page 50]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+12.5 Attacks Based On File and Path Names
+
+ Implementations of HTTP origin servers should be careful to restrict
+ the documents returned by HTTP requests to be only those that were
+ intended by the server administrators. If an HTTP server translates
+ HTTP URIs directly into file system calls, the server must take
+ special care not to serve files that were not intended to be
+ delivered to HTTP clients. For example, Unix, Microsoft Windows, and
+ other operating systems use ".." as a path component to indicate a
+ directory level above the current one. On such a system, an HTTP
+ server must disallow any such construct in the Request-URI if it
+ would otherwise allow access to a resource outside those intended to
+ be accessible via the HTTP server. Similarly, files intended for
+ reference only internally to the server (such as access control
+ files, configuration files, and script code) must be protected from
+ inappropriate retrieval, since they might contain sensitive
+ information. Experience has shown that minor bugs in such HTTP server
+ implementations have turned into security risks.
+
+13. Acknowledgments
+
+ This specification makes heavy use of the augmented BNF and generic
+ constructs defined by David H. Crocker for RFC 822 [7]. Similarly, it
+ reuses many of the definitions provided by Nathaniel Borenstein and
+ Ned Freed for MIME [5]. We hope that their inclusion in this
+ specification will help reduce past confusion over the relationship
+ between HTTP/1.0 and Internet mail message formats.
+
+ The HTTP protocol has evolved considerably over the past four years.
+ It has benefited from a large and active developer community--the
+ many people who have participated on the www-talk mailing list--and
+ it is that community which has been most responsible for the success
+ of HTTP and of the World-Wide Web in general. Marc Andreessen, Robert
+ Cailliau, Daniel W. Connolly, Bob Denny, Jean-Francois Groff, Phillip
+ M. Hallam-Baker, Hakon W. Lie, Ari Luotonen, Rob McCool, Lou
+ Montulli, Dave Raggett, Tony Sanders, and Marc VanHeyningen deserve
+ special recognition for their efforts in defining aspects of the
+ protocol for early versions of this specification.
+
+ Paul Hoffman contributed sections regarding the informational status
+ of this document and Appendices C and D.
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 51]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ This document has benefited greatly from the comments of all those
+ participating in the HTTP-WG. In addition to those already mentioned,
+ the following individuals have contributed to this specification:
+
+ Gary Adams Harald Tveit Alvestrand
+ Keith Ball Brian Behlendorf
+ Paul Burchard Maurizio Codogno
+ Mike Cowlishaw Roman Czyborra
+ Michael A. Dolan John Franks
+ Jim Gettys Marc Hedlund
+ Koen Holtman Alex Hopmann
+ Bob Jernigan Shel Kaphan
+ Martijn Koster Dave Kristol
+ Daniel LaLiberte Paul Leach
+ Albert Lunde John C. Mallery
+ Larry Masinter Mitra
+ Jeffrey Mogul Gavin Nicol
+ Bill Perry Jeffrey Perry
+ Owen Rees Luigi Rizzo
+ David Robinson Marc Salomon
+ Rich Salz Jim Seidman
+ Chuck Shotton Eric W. Sink
+ Simon E. Spero Robert S. Thau
+ Francois Yergeau Mary Ellen Zurko
+ Jean-Philippe Martin-Flatin
+
+14. References
+
+ [1] Anklesaria, F., McCahill, M., Lindner, P., Johnson, D.,
+ Torrey, D., and B. Alberti, "The Internet Gopher Protocol: A
+ Distributed Document Search and Retrieval Protocol", RFC 1436,
+ University of Minnesota, March 1993.
+
+ [2] Berners-Lee, T., "Universal Resource Identifiers in WWW: A
+ Unifying Syntax for the Expression of Names and Addresses of
+ Objects on the Network as used in the World-Wide Web",
+ RFC 1630, CERN, June 1994.
+
+ [3] Berners-Lee, T., and D. Connolly, "Hypertext Markup Language -
+ 2.0", RFC 1866, MIT/W3C, November 1995.
+
+ [4] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform
+ Resource Locators (URL)", RFC 1738, CERN, Xerox PARC,
+ University of Minnesota, December 1994.
+
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 52]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ [5] Borenstein, N., and N. Freed, "MIME (Multipurpose Internet Mail
+ Extensions) Part One: Mechanisms for Specifying and Describing
+ the Format of Internet Message Bodies", RFC 1521, Bellcore,
+ Innosoft, September 1993.
+
+ [6] Braden, R., "Requirements for Internet hosts - Application and
+ Support", STD 3, RFC 1123, IETF, October 1989.
+
+ [7] Crocker, D., "Standard for the Format of ARPA Internet Text
+ Messages", STD 11, RFC 822, UDEL, August 1982.
+
+ [8] F. Davis, B. Kahle, H. Morris, J. Salem, T. Shen, R. Wang,
+ J. Sui, and M. Grinbaum. "WAIS Interface Protocol Prototype
+ Functional Specification." (v1.5), Thinking Machines
+ Corporation, April 1990.
+
+ [9] Fielding, R., "Relative Uniform Resource Locators", RFC 1808,
+ UC Irvine, June 1995.
+
+ [10] Horton, M., and R. Adams, "Standard for interchange of USENET
+ Messages", RFC 1036 (Obsoletes RFC 850), AT&T Bell
+ Laboratories, Center for Seismic Studies, December 1987.
+
+ [11] Kantor, B., and P. Lapsley, "Network News Transfer Protocol:
+ A Proposed Standard for the Stream-Based Transmission of News",
+ RFC 977, UC San Diego, UC Berkeley, February 1986.
+
+ [12] Postel, J., "Simple Mail Transfer Protocol." STD 10, RFC 821,
+ USC/ISI, August 1982.
+
+ [13] Postel, J., "Media Type Registration Procedure." RFC 1590,
+ USC/ISI, March 1994.
+
+ [14] Postel, J., and J. Reynolds, "File Transfer Protocol (FTP)",
+ STD 9, RFC 959, USC/ISI, October 1985.
+
+ [15] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC
+ 1700, USC/ISI, October 1994.
+
+ [16] Sollins, K., and L. Masinter, "Functional Requirements for
+ Uniform Resource Names", RFC 1737, MIT/LCS, Xerox Corporation,
+ December 1994.
+
+ [17] US-ASCII. Coded Character Set - 7-Bit American Standard Code
+ for Information Interchange. Standard ANSI X3.4-1986, ANSI,
+ 1986.
+
+
+
+
+
+Berners-Lee, et al Informational [Page 53]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ [18] ISO-8859. International Standard -- Information Processing --
+ 8-bit Single-Byte Coded Graphic Character Sets --
+ Part 1: Latin alphabet No. 1, ISO 8859-1:1987.
+ Part 2: Latin alphabet No. 2, ISO 8859-2, 1987.
+ Part 3: Latin alphabet No. 3, ISO 8859-3, 1988.
+ Part 4: Latin alphabet No. 4, ISO 8859-4, 1988.
+ Part 5: Latin/Cyrillic alphabet, ISO 8859-5, 1988.
+ Part 6: Latin/Arabic alphabet, ISO 8859-6, 1987.
+ Part 7: Latin/Greek alphabet, ISO 8859-7, 1987.
+ Part 8: Latin/Hebrew alphabet, ISO 8859-8, 1988.
+ Part 9: Latin alphabet No. 5, ISO 8859-9, 1990.
+
+15. Authors' Addresses
+
+ Tim Berners-Lee
+ Director, W3 Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, U.S.A.
+
+ Fax: +1 (617) 258 8682
+ EMail: timbl@w3.org
+
+
+ Roy T. Fielding
+ Department of Information and Computer Science
+ University of California
+ Irvine, CA 92717-3425, U.S.A.
+
+ Fax: +1 (714) 824-4056
+ EMail: fielding@ics.uci.edu
+
+
+ Henrik Frystyk Nielsen
+ W3 Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, U.S.A.
+
+ Fax: +1 (617) 258 8682
+ EMail: frystyk@w3.org
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 54]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+Appendices
+
+ These appendices are provided for informational reasons only -- they
+ do not form a part of the HTTP/1.0 specification.
+
+A. Internet Media Type message/http
+
+ In addition to defining the HTTP/1.0 protocol, this document serves
+ as the specification for the Internet media type "message/http". The
+ following is to be registered with IANA [13].
+
+ Media Type name: message
+
+ Media subtype name: http
+
+ Required parameters: none
+
+ Optional parameters: version, msgtype
+
+ version: The HTTP-Version number of the enclosed message
+ (e.g., "1.0"). If not present, the version can be
+ determined from the first line of the body.
+
+ msgtype: The message type -- "request" or "response". If
+ not present, the type can be determined from the
+ first line of the body.
+
+ Encoding considerations: only "7bit", "8bit", or "binary" are
+ permitted
+
+ Security considerations: none
+
+B. Tolerant Applications
+
+ Although this document specifies the requirements for the generation
+ of HTTP/1.0 messages, not all applications will be correct in their
+ implementation. We therefore recommend that operational applications
+ be tolerant of deviations whenever those deviations can be
+ interpreted unambiguously.
+
+ Clients should be tolerant in parsing the Status-Line and servers
+ tolerant when parsing the Request-Line. In particular, they should
+ accept any amount of SP or HT characters between fields, even though
+ only a single SP is required.
+
+ The line terminator for HTTP-header fields is the sequence CRLF.
+ However, we recommend that applications, when parsing such headers,
+ recognize a single LF as a line terminator and ignore the leading CR.
+
+
+
+Berners-Lee, et al Informational [Page 55]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+C. Relationship to MIME
+
+ HTTP/1.0 uses many of the constructs defined for Internet Mail (RFC
+ 822 [7]) and the Multipurpose Internet Mail Extensions (MIME [5]) to
+ allow entities to be transmitted in an open variety of
+ representations and with extensible mechanisms. However, RFC 1521
+ discusses mail, and HTTP has a few features that are different than
+ those described in RFC 1521. These differences were carefully chosen
+ to optimize performance over binary connections, to allow greater
+ freedom in the use of new media types, to make date comparisons
+ easier, and to acknowledge the practice of some early HTTP servers
+ and clients.
+
+ At the time of this writing, it is expected that RFC 1521 will be
+ revised. The revisions may include some of the practices found in
+ HTTP/1.0 but not in RFC 1521.
+
+ This appendix describes specific areas where HTTP differs from RFC
+ 1521. Proxies and gateways to strict MIME environments should be
+ aware of these differences and provide the appropriate conversions
+ where necessary. Proxies and gateways from MIME environments to HTTP
+ also need to be aware of the differences because some conversions may
+ be required.
+
+C.1 Conversion to Canonical Form
+
+ RFC 1521 requires that an Internet mail entity be converted to
+ canonical form prior to being transferred, as described in Appendix G
+ of RFC 1521 [5]. Section 3.6.1 of this document describes the forms
+ allowed for subtypes of the "text" media type when transmitted over
+ HTTP.
+
+ RFC 1521 requires that content with a Content-Type of "text"
+ represent line breaks as CRLF and forbids the use of CR or LF outside
+ of line break sequences. HTTP allows CRLF, bare CR, and bare LF to
+ indicate a line break within text content when a message is
+ transmitted over HTTP.
+
+ Where it is possible, a proxy or gateway from HTTP to a strict RFC
+ 1521 environment should translate all line breaks within the text
+ media types described in Section 3.6.1 of this document to the RFC
+ 1521 canonical form of CRLF. Note, however, that this may be
+ complicated by the presence of a Content-Encoding and by the fact
+ that HTTP allows the use of some character sets which do not use
+ octets 13 and 10 to represent CR and LF, as is the case for some
+ multi-byte character sets.
+
+
+
+
+
+Berners-Lee, et al Informational [Page 56]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+C.2 Conversion of Date Formats
+
+ HTTP/1.0 uses a restricted set of date formats (Section 3.3) to
+ simplify the process of date comparison. Proxies and gateways from
+ other protocols should ensure that any Date header field present in a
+ message conforms to one of the HTTP/1.0 formats and rewrite the date
+ if necessary.
+
+C.3 Introduction of Content-Encoding
+
+ RFC 1521 does not include any concept equivalent to HTTP/1.0's
+ Content-Encoding header field. Since this acts as a modifier on the
+ media type, proxies and gateways from HTTP to MIME-compliant
+ protocols must either change the value of the Content-Type header
+ field or decode the Entity-Body before forwarding the message. (Some
+ experimental applications of Content-Type for Internet mail have used
+ a media-type parameter of ";conversions=<content-coding>" to perform
+ an equivalent function as Content-Encoding. However, this parameter
+ is not part of RFC 1521.)
+
+C.4 No Content-Transfer-Encoding
+
+ HTTP does not use the Content-Transfer-Encoding (CTE) field of RFC
+ 1521. Proxies and gateways from MIME-compliant protocols to HTTP must
+ remove any non-identity CTE ("quoted-printable" or "base64") encoding
+ prior to delivering the response message to an HTTP client.
+
+ Proxies and gateways from HTTP to MIME-compliant protocols are
+ responsible for ensuring that the message is in the correct format
+ and encoding for safe transport on that protocol, where "safe
+ transport" is defined by the limitations of the protocol being used.
+ Such a proxy or gateway should label the data with an appropriate
+ Content-Transfer-Encoding if doing so will improve the likelihood of
+ safe transport over the destination protocol.
+
+C.5 HTTP Header Fields in Multipart Body-Parts
+
+ In RFC 1521, most header fields in multipart body-parts are generally
+ ignored unless the field name begins with "Content-". In HTTP/1.0,
+ multipart body-parts may contain any HTTP header fields which are
+ significant to the meaning of that part.
+
+D. Additional Features
+
+ This appendix documents protocol elements used by some existing HTTP
+ implementations, but not consistently and correctly across most
+ HTTP/1.0 applications. Implementors should be aware of these
+ features, but cannot rely upon their presence in, or interoperability
+
+
+
+Berners-Lee, et al Informational [Page 57]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ with, other HTTP/1.0 applications.
+
+D.1 Additional Request Methods
+
+D.1.1 PUT
+
+ The PUT method requests that the enclosed entity be stored under the
+ supplied Request-URI. If the Request-URI refers to an already
+ existing resource, the enclosed entity should be considered as a
+ modified version of the one residing on the origin server. If the
+ Request-URI does not point to an existing resource, and that URI is
+ capable of being defined as a new resource by the requesting user
+ agent, the origin server can create the resource with that URI.
+
+ The fundamental difference between the POST and PUT requests is
+ reflected in the different meaning of the Request-URI. The URI in a
+ POST request identifies the resource that will handle the enclosed
+ entity as data to be processed. That resource may be a data-accepting
+ process, a gateway to some other protocol, or a separate entity that
+ accepts annotations. In contrast, the URI in a PUT request identifies
+ the entity enclosed with the request -- the user agent knows what URI
+ is intended and the server should not apply the request to some other
+ resource.
+
+D.1.2 DELETE
+
+ The DELETE method requests that the origin server delete the resource
+ identified by the Request-URI.
+
+D.1.3 LINK
+
+ The LINK method establishes one or more Link relationships between
+ the existing resource identified by the Request-URI and other
+ existing resources.
+
+D.1.4 UNLINK
+
+ The UNLINK method removes one or more Link relationships from the
+ existing resource identified by the Request-URI.
+
+D.2 Additional Header Field Definitions
+
+D.2.1 Accept
+
+ The Accept request-header field can be used to indicate a list of
+ media ranges which are acceptable as a response to the request. The
+ asterisk "*" character is used to group media types into ranges, with
+ "*/*" indicating all media types and "type/*" indicating all subtypes
+
+
+
+Berners-Lee, et al Informational [Page 58]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+ of that type. The set of ranges given by the client should represent
+ what types are acceptable given the context of the request.
+
+D.2.2 Accept-Charset
+
+ The Accept-Charset request-header field can be used to indicate a
+ list of preferred character sets other than the default US-ASCII and
+ ISO-8859-1. This field allows clients capable of understanding more
+ comprehensive or special-purpose character sets to signal that
+ capability to a server which is capable of representing documents in
+ those character sets.
+
+D.2.3 Accept-Encoding
+
+ The Accept-Encoding request-header field is similar to Accept, but
+ restricts the content-coding values which are acceptable in the
+ response.
+
+D.2.4 Accept-Language
+
+ The Accept-Language request-header field is similar to Accept, but
+ restricts the set of natural languages that are preferred as a
+ response to the request.
+
+D.2.5 Content-Language
+
+ The Content-Language entity-header field describes the natural
+ language(s) of the intended audience for the enclosed entity. Note
+ that this may not be equivalent to all the languages used within the
+ entity.
+
+D.2.6 Link
+
+ The Link entity-header field provides a means for describing a
+ relationship between the entity and some other resource. An entity
+ may include multiple Link values. Links at the metainformation level
+ typically indicate relationships like hierarchical structure and
+ navigation paths.
+
+D.2.7 MIME-Version
+
+ HTTP messages may include a single MIME-Version general-header field
+ to indicate what version of the MIME protocol was used to construct
+ the message. Use of the MIME-Version header field, as defined by RFC
+ 1521 [5], should indicate that the message is MIME-conformant.
+ Unfortunately, some older HTTP/1.0 servers send it indiscriminately,
+ and thus this field should be ignored.
+
+
+
+
+Berners-Lee, et al Informational [Page 59]
+\f
+RFC 1945 HTTP/1.0 May 1996
+
+
+D.2.8 Retry-After
+
+ The Retry-After response-header field can be used with a 503 (service
+ unavailable) response to indicate how long the service is expected to
+ be unavailable to the requesting client. The value of this field can
+ be either an HTTP-date or an integer number of seconds (in decimal)
+ after the time of the response.
+
+D.2.9 Title
+
+ The Title entity-header field indicates the title of the entity.
+
+D.2.10 URI
+
+ The URI entity-header field may contain some or all of the Uniform
+ Resource Identifiers (Section 3.2) by which the Request-URI resource
+ can be identified. There is no guarantee that the resource can be
+ accessed using the URI(s) specified.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al Informational [Page 60]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group R. Fielding
+Request for Comments: 2068 UC Irvine
+Category: Standards Track J. Gettys
+ J. Mogul
+ DEC
+ H. Frystyk
+ T. Berners-Lee
+ MIT/LCS
+ January 1997
+
+
+ Hypertext Transfer Protocol -- HTTP/1.1
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ The Hypertext Transfer Protocol (HTTP) is an application-level
+ protocol for distributed, collaborative, hypermedia information
+ systems. It is a generic, stateless, object-oriented protocol which
+ can be used for many tasks, such as name servers and distributed
+ object management systems, through extension of its request methods.
+ A feature of HTTP is the typing and negotiation of data
+ representation, allowing systems to be built independently of the
+ data being transferred.
+
+ HTTP has been in use by the World-Wide Web global information
+ initiative since 1990. This specification defines the protocol
+ referred to as "HTTP/1.1".
+
+Table of Contents
+
+ 1 Introduction.............................................7
+ 1.1 Purpose ..............................................7
+ 1.2 Requirements .........................................7
+ 1.3 Terminology ..........................................8
+ 1.4 Overall Operation ...................................11
+ 2 Notational Conventions and Generic Grammar..............13
+ 2.1 Augmented BNF .......................................13
+ 2.2 Basic Rules .........................................15
+ 3 Protocol Parameters.....................................17
+ 3.1 HTTP Version ........................................17
+
+
+
+Fielding, et. al. Standards Track [Page 1]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ 3.2 Uniform Resource Identifiers ........................18
+ 3.2.1 General Syntax ...................................18
+ 3.2.2 http URL .........................................19
+ 3.2.3 URI Comparison ...................................20
+ 3.3 Date/Time Formats ...................................21
+ 3.3.1 Full Date ........................................21
+ 3.3.2 Delta Seconds ....................................22
+ 3.4 Character Sets ......................................22
+ 3.5 Content Codings .....................................23
+ 3.6 Transfer Codings ....................................24
+ 3.7 Media Types .........................................25
+ 3.7.1 Canonicalization and Text Defaults ...............26
+ 3.7.2 Multipart Types ..................................27
+ 3.8 Product Tokens ......................................28
+ 3.9 Quality Values ......................................28
+ 3.10 Language Tags ......................................28
+ 3.11 Entity Tags ........................................29
+ 3.12 Range Units ........................................30
+ 4 HTTP Message............................................30
+ 4.1 Message Types .......................................30
+ 4.2 Message Headers .....................................31
+ 4.3 Message Body ........................................32
+ 4.4 Message Length ......................................32
+ 4.5 General Header Fields ...............................34
+ 5 Request.................................................34
+ 5.1 Request-Line ........................................34
+ 5.1.1 Method ...........................................35
+ 5.1.2 Request-URI ......................................35
+ 5.2 The Resource Identified by a Request ................37
+ 5.3 Request Header Fields ...............................37
+ 6 Response................................................38
+ 6.1 Status-Line .........................................38
+ 6.1.1 Status Code and Reason Phrase ....................39
+ 6.2 Response Header Fields ..............................41
+ 7 Entity..................................................41
+ 7.1 Entity Header Fields ................................41
+ 7.2 Entity Body .........................................42
+ 7.2.1 Type .............................................42
+ 7.2.2 Length ...........................................43
+ 8 Connections.............................................43
+ 8.1 Persistent Connections ..............................43
+ 8.1.1 Purpose ..........................................43
+ 8.1.2 Overall Operation ................................44
+ 8.1.3 Proxy Servers ....................................45
+ 8.1.4 Practical Considerations .........................45
+ 8.2 Message Transmission Requirements ...................46
+ 9 Method Definitions......................................48
+ 9.1 Safe and Idempotent Methods .........................48
+
+
+
+Fielding, et. al. Standards Track [Page 2]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ 9.1.1 Safe Methods .....................................48
+ 9.1.2 Idempotent Methods ...............................49
+ 9.2 OPTIONS .............................................49
+ 9.3 GET .................................................50
+ 9.4 HEAD ................................................50
+ 9.5 POST ................................................51
+ 9.6 PUT .................................................52
+ 9.7 DELETE ..............................................53
+ 9.8 TRACE ...............................................53
+ 10 Status Code Definitions................................53
+ 10.1 Informational 1xx ..................................54
+ 10.1.1 100 Continue ....................................54
+ 10.1.2 101 Switching Protocols .........................54
+ 10.2 Successful 2xx .....................................54
+ 10.2.1 200 OK ..........................................54
+ 10.2.2 201 Created .....................................55
+ 10.2.3 202 Accepted ....................................55
+ 10.2.4 203 Non-Authoritative Information ...............55
+ 10.2.5 204 No Content ..................................55
+ 10.2.6 205 Reset Content ...............................56
+ 10.2.7 206 Partial Content .............................56
+ 10.3 Redirection 3xx ....................................56
+ 10.3.1 300 Multiple Choices ............................57
+ 10.3.2 301 Moved Permanently ...........................57
+ 10.3.3 302 Moved Temporarily ...........................58
+ 10.3.4 303 See Other ...................................58
+ 10.3.5 304 Not Modified ................................58
+ 10.3.6 305 Use Proxy ...................................59
+ 10.4 Client Error 4xx ...................................59
+ 10.4.1 400 Bad Request .................................60
+ 10.4.2 401 Unauthorized ................................60
+ 10.4.3 402 Payment Required ............................60
+ 10.4.4 403 Forbidden ...................................60
+ 10.4.5 404 Not Found ...................................60
+ 10.4.6 405 Method Not Allowed ..........................61
+ 10.4.7 406 Not Acceptable ..............................61
+ 10.4.8 407 Proxy Authentication Required ...............61
+ 10.4.9 408 Request Timeout .............................62
+ 10.4.10 409 Conflict ...................................62
+ 10.4.11 410 Gone .......................................62
+ 10.4.12 411 Length Required ............................63
+ 10.4.13 412 Precondition Failed ........................63
+ 10.4.14 413 Request Entity Too Large ...................63
+ 10.4.15 414 Request-URI Too Long .......................63
+ 10.4.16 415 Unsupported Media Type .....................63
+ 10.5 Server Error 5xx ...................................64
+ 10.5.1 500 Internal Server Error .......................64
+ 10.5.2 501 Not Implemented .............................64
+
+
+
+Fielding, et. al. Standards Track [Page 3]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ 10.5.3 502 Bad Gateway .................................64
+ 10.5.4 503 Service Unavailable .........................64
+ 10.5.5 504 Gateway Timeout .............................64
+ 10.5.6 505 HTTP Version Not Supported ..................65
+ 11 Access Authentication..................................65
+ 11.1 Basic Authentication Scheme ........................66
+ 11.2 Digest Authentication Scheme .......................67
+ 12 Content Negotiation....................................67
+ 12.1 Server-driven Negotiation ..........................68
+ 12.2 Agent-driven Negotiation ...........................69
+ 12.3 Transparent Negotiation ............................70
+ 13 Caching in HTTP........................................70
+ 13.1.1 Cache Correctness ...............................72
+ 13.1.2 Warnings ........................................73
+ 13.1.3 Cache-control Mechanisms ........................74
+ 13.1.4 Explicit User Agent Warnings ....................74
+ 13.1.5 Exceptions to the Rules and Warnings ............75
+ 13.1.6 Client-controlled Behavior ......................75
+ 13.2 Expiration Model ...................................75
+ 13.2.1 Server-Specified Expiration .....................75
+ 13.2.2 Heuristic Expiration ............................76
+ 13.2.3 Age Calculations ................................77
+ 13.2.4 Expiration Calculations .........................79
+ 13.2.5 Disambiguating Expiration Values ................80
+ 13.2.6 Disambiguating Multiple Responses ...............80
+ 13.3 Validation Model ...................................81
+ 13.3.1 Last-modified Dates .............................82
+ 13.3.2 Entity Tag Cache Validators .....................82
+ 13.3.3 Weak and Strong Validators ......................82
+ 13.3.4 Rules for When to Use Entity Tags and Last-
+ modified Dates..........................................85
+ 13.3.5 Non-validating Conditionals .....................86
+ 13.4 Response Cachability ...............................86
+ 13.5 Constructing Responses From Caches .................87
+ 13.5.1 End-to-end and Hop-by-hop Headers ...............88
+ 13.5.2 Non-modifiable Headers ..........................88
+ 13.5.3 Combining Headers ...............................89
+ 13.5.4 Combining Byte Ranges ...........................90
+ 13.6 Caching Negotiated Responses .......................90
+ 13.7 Shared and Non-Shared Caches .......................91
+ 13.8 Errors or Incomplete Response Cache Behavior .......91
+ 13.9 Side Effects of GET and HEAD .......................92
+ 13.10 Invalidation After Updates or Deletions ...........92
+ 13.11 Write-Through Mandatory ...........................93
+ 13.12 Cache Replacement .................................93
+ 13.13 History Lists .....................................93
+ 14 Header Field Definitions...............................94
+ 14.1 Accept .............................................95
+
+
+
+Fielding, et. al. Standards Track [Page 4]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ 14.2 Accept-Charset .....................................97
+ 14.3 Accept-Encoding ....................................97
+ 14.4 Accept-Language ....................................98
+ 14.5 Accept-Ranges ......................................99
+ 14.6 Age ................................................99
+ 14.7 Allow .............................................100
+ 14.8 Authorization .....................................100
+ 14.9 Cache-Control .....................................101
+ 14.9.1 What is Cachable ...............................103
+ 14.9.2 What May be Stored by Caches ...................103
+ 14.9.3 Modifications of the Basic Expiration Mechanism 104
+ 14.9.4 Cache Revalidation and Reload Controls .........105
+ 14.9.5 No-Transform Directive .........................107
+ 14.9.6 Cache Control Extensions .......................108
+ 14.10 Connection .......................................109
+ 14.11 Content-Base .....................................109
+ 14.12 Content-Encoding .................................110
+ 14.13 Content-Language .................................110
+ 14.14 Content-Length ...................................111
+ 14.15 Content-Location .................................112
+ 14.16 Content-MD5 ......................................113
+ 14.17 Content-Range ....................................114
+ 14.18 Content-Type .....................................116
+ 14.19 Date .............................................116
+ 14.20 ETag .............................................117
+ 14.21 Expires ..........................................117
+ 14.22 From .............................................118
+ 14.23 Host .............................................119
+ 14.24 If-Modified-Since ................................119
+ 14.25 If-Match .........................................121
+ 14.26 If-None-Match ....................................122
+ 14.27 If-Range .........................................123
+ 14.28 If-Unmodified-Since ..............................124
+ 14.29 Last-Modified ....................................124
+ 14.30 Location .........................................125
+ 14.31 Max-Forwards .....................................125
+ 14.32 Pragma ...........................................126
+ 14.33 Proxy-Authenticate ...............................127
+ 14.34 Proxy-Authorization ..............................127
+ 14.35 Public ...........................................127
+ 14.36 Range ............................................128
+ 14.36.1 Byte Ranges ...................................128
+ 14.36.2 Range Retrieval Requests ......................130
+ 14.37 Referer ..........................................131
+ 14.38 Retry-After ......................................131
+ 14.39 Server ...........................................132
+ 14.40 Transfer-Encoding ................................132
+ 14.41 Upgrade ..........................................132
+
+
+
+Fielding, et. al. Standards Track [Page 5]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ 14.42 User-Agent .......................................134
+ 14.43 Vary .............................................134
+ 14.44 Via ..............................................135
+ 14.45 Warning ..........................................137
+ 14.46 WWW-Authenticate .................................139
+ 15 Security Considerations...............................139
+ 15.1 Authentication of Clients .........................139
+ 15.2 Offering a Choice of Authentication Schemes .......140
+ 15.3 Abuse of Server Log Information ...................141
+ 15.4 Transfer of Sensitive Information .................141
+ 15.5 Attacks Based On File and Path Names ..............142
+ 15.6 Personal Information ..............................143
+ 15.7 Privacy Issues Connected to Accept Headers ........143
+ 15.8 DNS Spoofing ......................................144
+ 15.9 Location Headers and Spoofing .....................144
+ 16 Acknowledgments.......................................144
+ 17 References............................................146
+ 18 Authors' Addresses....................................149
+ 19 Appendices............................................150
+ 19.1 Internet Media Type message/http ..................150
+ 19.2 Internet Media Type multipart/byteranges ..........150
+ 19.3 Tolerant Applications .............................151
+ 19.4 Differences Between HTTP Entities and
+ MIME Entities...........................................152
+ 19.4.1 Conversion to Canonical Form ...................152
+ 19.4.2 Conversion of Date Formats .....................153
+ 19.4.3 Introduction of Content-Encoding ...............153
+ 19.4.4 No Content-Transfer-Encoding ...................153
+ 19.4.5 HTTP Header Fields in Multipart Body-Parts .....153
+ 19.4.6 Introduction of Transfer-Encoding ..............154
+ 19.4.7 MIME-Version ...................................154
+ 19.5 Changes from HTTP/1.0 .............................154
+ 19.5.1 Changes to Simplify Multi-homed Web Servers and
+ Conserve IP Addresses .................................155
+ 19.6 Additional Features ...............................156
+ 19.6.1 Additional Request Methods .....................156
+ 19.6.2 Additional Header Field Definitions ............156
+ 19.7 Compatibility with Previous Versions ..............160
+ 19.7.1 Compatibility with HTTP/1.0 Persistent
+ Connections............................................161
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 6]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+1 Introduction
+
+1.1 Purpose
+
+ The Hypertext Transfer Protocol (HTTP) is an application-level
+ protocol for distributed, collaborative, hypermedia information
+ systems. HTTP has been in use by the World-Wide Web global
+ information initiative since 1990. The first version of HTTP,
+ referred to as HTTP/0.9, was a simple protocol for raw data transfer
+ across the Internet. HTTP/1.0, as defined by RFC 1945 [6], improved
+ the protocol by allowing messages to be in the format of MIME-like
+ messages, containing metainformation about the data transferred and
+ modifiers on the request/response semantics. However, HTTP/1.0 does
+ not sufficiently take into consideration the effects of hierarchical
+ proxies, caching, the need for persistent connections, and virtual
+ hosts. In addition, the proliferation of incompletely-implemented
+ applications calling themselves "HTTP/1.0" has necessitated a
+ protocol version change in order for two communicating applications
+ to determine each other's true capabilities.
+
+ This specification defines the protocol referred to as "HTTP/1.1".
+ This protocol includes more stringent requirements than HTTP/1.0 in
+ order to ensure reliable implementation of its features.
+
+ Practical information systems require more functionality than simple
+ retrieval, including search, front-end update, and annotation. HTTP
+ allows an open-ended set of methods that indicate the purpose of a
+ request. It builds on the discipline of reference provided by the
+ Uniform Resource Identifier (URI) [3][20], as a location (URL) [4] or
+ name (URN) , for indicating the resource to which a method is to be
+ applied. Messages are passed in a format similar to that used by
+ Internet mail as defined by the Multipurpose Internet Mail Extensions
+ (MIME).
+
+ HTTP is also used as a generic protocol for communication between
+ user agents and proxies/gateways to other Internet systems, including
+ those supported by the SMTP [16], NNTP [13], FTP [18], Gopher [2],
+ and WAIS [10] protocols. In this way, HTTP allows basic hypermedia
+ access to resources available from diverse applications.
+
+1.2 Requirements
+
+ This specification uses the same words as RFC 1123 [8] for defining
+ the significance of each particular requirement. These words are:
+
+ MUST
+ This word or the adjective "required" means that the item is an
+ absolute requirement of the specification.
+
+
+
+Fielding, et. al. Standards Track [Page 7]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ SHOULD
+ This word or the adjective "recommended" means that there may
+ exist valid reasons in particular circumstances to ignore this
+ item, but the full implications should be understood and the case
+ carefully weighed before choosing a different course.
+
+ MAY
+ This word or the adjective "optional" means that this item is
+ truly optional. One vendor may choose to include the item because
+ a particular marketplace requires it or because it enhances the
+ product, for example; another vendor may omit the same item.
+
+ An implementation is not compliant if it fails to satisfy one or more
+ of the MUST requirements for the protocols it implements. An
+ implementation that satisfies all the MUST and all the SHOULD
+ requirements for its protocols is said to be "unconditionally
+ compliant"; one that satisfies all the MUST requirements but not all
+ the SHOULD requirements for its protocols is said to be
+ "conditionally compliant."
+
+1.3 Terminology
+
+ This specification uses a number of terms to refer to the roles
+ played by participants in, and objects of, the HTTP communication.
+
+ connection
+ A transport layer virtual circuit established between two programs
+ for the purpose of communication.
+
+ message
+ The basic unit of HTTP communication, consisting of a structured
+ sequence of octets matching the syntax defined in section 4 and
+ transmitted via the connection.
+
+ request
+ An HTTP request message, as defined in section 5.
+
+ response
+ An HTTP response message, as defined in section 6.
+
+ resource
+ A network data object or service that can be identified by a URI,
+ as defined in section 3.2. Resources may be available in multiple
+ representations (e.g. multiple languages, data formats, size,
+ resolutions) or vary in other ways.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 8]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ entity
+ The information transferred as the payload of a request or
+ response. An entity consists of metainformation in the form of
+ entity-header fields and content in the form of an entity-body, as
+ described in section 7.
+
+ representation
+ An entity included with a response that is subject to content
+ negotiation, as described in section 12. There may exist multiple
+ representations associated with a particular response status.
+
+ content negotiation
+ The mechanism for selecting the appropriate representation when
+ servicing a request, as described in section 12. The
+ representation of entities in any response can be negotiated
+ (including error responses).
+
+ variant
+ A resource may have one, or more than one, representation(s)
+ associated with it at any given instant. Each of these
+ representations is termed a `variant.' Use of the term `variant'
+ does not necessarily imply that the resource is subject to content
+ negotiation.
+
+ client
+ A program that establishes connections for the purpose of sending
+ requests.
+
+ user agent
+ The client which initiates a request. These are often browsers,
+ editors, spiders (web-traversing robots), or other end user tools.
+
+ server
+ An application program that accepts connections in order to
+ service requests by sending back responses. Any given program may
+ be capable of being both a client and a server; our use of these
+ terms refers only to the role being performed by the program for a
+ particular connection, rather than to the program's capabilities
+ in general. Likewise, any server may act as an origin server,
+ proxy, gateway, or tunnel, switching behavior based on the nature
+ of each request.
+
+ origin server
+ The server on which a given resource resides or is to be created.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 9]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ proxy
+ An intermediary program which acts as both a server and a client
+ for the purpose of making requests on behalf of other clients.
+ Requests are serviced internally or by passing them on, with
+ possible translation, to other servers. A proxy must implement
+ both the client and server requirements of this specification.
+
+ gateway
+ A server which acts as an intermediary for some other server.
+ Unlike a proxy, a gateway receives requests as if it were the
+ origin server for the requested resource; the requesting client
+ may not be aware that it is communicating with a gateway.
+
+ tunnel
+ An intermediary program which is acting as a blind relay between
+ two connections. Once active, a tunnel is not considered a party
+ to the HTTP communication, though the tunnel may have been
+ initiated by an HTTP request. The tunnel ceases to exist when both
+ ends of the relayed connections are closed.
+
+ cache
+ A program's local store of response messages and the subsystem
+ that controls its message storage, retrieval, and deletion. A
+ cache stores cachable responses in order to reduce the response
+ time and network bandwidth consumption on future, equivalent
+ requests. Any client or server may include a cache, though a cache
+ cannot be used by a server that is acting as a tunnel.
+
+ cachable
+ A response is cachable if a cache is allowed to store a copy of
+ the response message for use in answering subsequent requests. The
+ rules for determining the cachability of HTTP responses are
+ defined in section 13. Even if a resource is cachable, there may
+ be additional constraints on whether a cache can use the cached
+ copy for a particular request.
+
+ first-hand
+ A response is first-hand if it comes directly and without
+ unnecessary delay from the origin server, perhaps via one or more
+ proxies. A response is also first-hand if its validity has just
+ been checked directly with the origin server.
+
+ explicit expiration time
+ The time at which the origin server intends that an entity should
+ no longer be returned by a cache without further validation.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 10]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ heuristic expiration time
+ An expiration time assigned by a cache when no explicit expiration
+ time is available.
+
+ age
+ The age of a response is the time since it was sent by, or
+ successfully validated with, the origin server.
+
+ freshness lifetime
+ The length of time between the generation of a response and its
+ expiration time.
+
+ fresh
+ A response is fresh if its age has not yet exceeded its freshness
+ lifetime.
+
+ stale
+ A response is stale if its age has passed its freshness lifetime.
+
+ semantically transparent
+ A cache behaves in a "semantically transparent" manner, with
+ respect to a particular response, when its use affects neither the
+ requesting client nor the origin server, except to improve
+ performance. When a cache is semantically transparent, the client
+ receives exactly the same response (except for hop-by-hop headers)
+ that it would have received had its request been handled directly
+ by the origin server.
+
+ validator
+ A protocol element (e.g., an entity tag or a Last-Modified time)
+ that is used to find out whether a cache entry is an equivalent
+ copy of an entity.
+
+1.4 Overall Operation
+
+ The HTTP protocol is a request/response protocol. A client sends a
+ request to the server in the form of a request method, URI, and
+ protocol version, followed by a MIME-like message containing request
+ modifiers, client information, and possible body content over a
+ connection with a server. The server responds with a status line,
+ including the message's protocol version and a success or error code,
+ followed by a MIME-like message containing server information, entity
+ metainformation, and possible entity-body content. The relationship
+ between HTTP and MIME is described in appendix 19.4.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 11]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Most HTTP communication is initiated by a user agent and consists of
+ a request to be applied to a resource on some origin server. In the
+ simplest case, this may be accomplished via a single connection (v)
+ between the user agent (UA) and the origin server (O).
+
+ request chain ------------------------>
+ UA -------------------v------------------- O
+ <----------------------- response chain
+
+ A more complicated situation occurs when one or more intermediaries
+ are present in the request/response chain. There are three common
+ forms of intermediary: proxy, gateway, and tunnel. A proxy is a
+ forwarding agent, receiving requests for a URI in its absolute form,
+ rewriting all or part of the message, and forwarding the reformatted
+ request toward the server identified by the URI. A gateway is a
+ receiving agent, acting as a layer above some other server(s) and, if
+ necessary, translating the requests to the underlying server's
+ protocol. A tunnel acts as a relay point between two connections
+ without changing the messages; tunnels are used when the
+ communication needs to pass through an intermediary (such as a
+ firewall) even when the intermediary cannot understand the contents
+ of the messages.
+
+ request chain -------------------------------------->
+ UA -----v----- A -----v----- B -----v----- C -----v----- O
+ <------------------------------------- response chain
+
+ The figure above shows three intermediaries (A, B, and C) between the
+ user agent and origin server. A request or response message that
+ travels the whole chain will pass through four separate connections.
+ This distinction is important because some HTTP communication options
+ may apply only to the connection with the nearest, non-tunnel
+ neighbor, only to the end-points of the chain, or to all connections
+ along the chain. Although the diagram is linear, each participant
+ may be engaged in multiple, simultaneous communications. For example,
+ B may be receiving requests from many clients other than A, and/or
+ forwarding requests to servers other than C, at the same time that it
+ is handling A's request.
+
+ Any party to the communication which is not acting as a tunnel may
+ employ an internal cache for handling requests. The effect of a cache
+ is that the request/response chain is shortened if one of the
+ participants along the chain has a cached response applicable to that
+ request. The following illustrates the resulting chain if B has a
+ cached copy of an earlier response from O (via C) for a request which
+ has not been cached by UA or A.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 12]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ request chain ---------->
+ UA -----v----- A -----v----- B - - - - - - C - - - - - - O
+ <--------- response chain
+
+ Not all responses are usefully cachable, and some requests may
+ contain modifiers which place special requirements on cache behavior.
+ HTTP requirements for cache behavior and cachable responses are
+ defined in section 13.
+
+ In fact, there are a wide variety of architectures and configurations
+ of caches and proxies currently being experimented with or deployed
+ across the World Wide Web; these systems include national hierarchies
+ of proxy caches to save transoceanic bandwidth, systems that
+ broadcast or multicast cache entries, organizations that distribute
+ subsets of cached data via CD-ROM, and so on. HTTP systems are used
+ in corporate intranets over high-bandwidth links, and for access via
+ PDAs with low-power radio links and intermittent connectivity. The
+ goal of HTTP/1.1 is to support the wide diversity of configurations
+ already deployed while introducing protocol constructs that meet the
+ needs of those who build web applications that require high
+ reliability and, failing that, at least reliable indications of
+ failure.
+
+ HTTP communication usually takes place over TCP/IP connections. The
+ default port is TCP 80, but other ports can be used. This does not
+ preclude HTTP from being implemented on top of any other protocol on
+ the Internet, or on other networks. HTTP only presumes a reliable
+ transport; any protocol that provides such guarantees can be used;
+ the mapping of the HTTP/1.1 request and response structures onto the
+ transport data units of the protocol in question is outside the scope
+ of this specification.
+
+ In HTTP/1.0, most implementations used a new connection for each
+ request/response exchange. In HTTP/1.1, a connection may be used for
+ one or more request/response exchanges, although connections may be
+ closed for a variety of reasons (see section 8.1).
+
+2 Notational Conventions and Generic Grammar
+
+2.1 Augmented BNF
+
+ All of the mechanisms specified in this document are described in
+ both prose and an augmented Backus-Naur Form (BNF) similar to that
+ used by RFC 822 [9]. Implementers will need to be familiar with the
+ notation in order to understand this specification. The augmented BNF
+ includes the following constructs:
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 13]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+name = definition
+ The name of a rule is simply the name itself (without any enclosing
+ "<" and ">") and is separated from its definition by the equal "="
+ character. Whitespace is only significant in that indentation of
+ continuation lines is used to indicate a rule definition that spans
+ more than one line. Certain basic rules are in uppercase, such as
+ SP, LWS, HT, CRLF, DIGIT, ALPHA, etc. Angle brackets are used
+ within definitions whenever their presence will facilitate
+ discerning the use of rule names.
+
+"literal"
+ Quotation marks surround literal text. Unless stated otherwise, the
+ text is case-insensitive.
+
+rule1 | rule2
+ Elements separated by a bar ("|") are alternatives, e.g., "yes |
+ no" will accept yes or no.
+
+(rule1 rule2)
+ Elements enclosed in parentheses are treated as a single element.
+ Thus, "(elem (foo | bar) elem)" allows the token sequences "elem
+ foo elem" and "elem bar elem".
+
+*rule
+ The character "*" preceding an element indicates repetition. The
+ full form is "<n>*<m>element" indicating at least <n> and at most
+ <m> occurrences of element. Default values are 0 and infinity so
+ that "*(element)" allows any number, including zero; "1*element"
+ requires at least one; and "1*2element" allows one or two.
+
+[rule]
+ Square brackets enclose optional elements; "[foo bar]" is
+ equivalent to "*1(foo bar)".
+
+N rule
+ Specific repetition: "<n>(element)" is equivalent to
+ "<n>*<n>(element)"; that is, exactly <n> occurrences of (element).
+ Thus 2DIGIT is a 2-digit number, and 3ALPHA is a string of three
+ alphabetic characters.
+
+#rule
+ A construct "#" is defined, similar to "*", for defining lists of
+ elements. The full form is "<n>#<m>element " indicating at least
+ <n> and at most <m> elements, each separated by one or more commas
+ (",") and optional linear whitespace (LWS). This makes the usual
+ form of lists very easy; a rule such as "( *LWS element *( *LWS ","
+ *LWS element )) " can be shown as "1#element". Wherever this
+ construct is used, null elements are allowed, but do not contribute
+
+
+
+Fielding, et. al. Standards Track [Page 14]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ to the count of elements present. That is, "(element), , (element)
+ " is permitted, but counts as only two elements. Therefore, where
+ at least one element is required, at least one non-null element
+ must be present. Default values are 0 and infinity so that
+ "#element" allows any number, including zero; "1#element" requires
+ at least one; and "1#2element" allows one or two.
+
+; comment
+ A semi-colon, set off some distance to the right of rule text,
+ starts a comment that continues to the end of line. This is a
+ simple way of including useful notes in parallel with the
+ specifications.
+
+implied *LWS
+ The grammar described by this specification is word-based. Except
+ where noted otherwise, linear whitespace (LWS) can be included
+ between any two adjacent words (token or quoted-string), and
+ between adjacent tokens and delimiters (tspecials), without
+ changing the interpretation of a field. At least one delimiter
+ (tspecials) must exist between any two tokens, since they would
+ otherwise be interpreted as a single token.
+
+2.2 Basic Rules
+
+ The following rules are used throughout this specification to
+ describe basic parsing constructs. The US-ASCII coded character set
+ is defined by ANSI X3.4-1986 [21].
+
+ OCTET = <any 8-bit sequence of data>
+ CHAR = <any US-ASCII character (octets 0 - 127)>
+ UPALPHA = <any US-ASCII uppercase letter "A".."Z">
+ LOALPHA = <any US-ASCII lowercase letter "a".."z">
+ ALPHA = UPALPHA | LOALPHA
+ DIGIT = <any US-ASCII digit "0".."9">
+ CTL = <any US-ASCII control character
+ (octets 0 - 31) and DEL (127)>
+ CR = <US-ASCII CR, carriage return (13)>
+ LF = <US-ASCII LF, linefeed (10)>
+ SP = <US-ASCII SP, space (32)>
+ HT = <US-ASCII HT, horizontal-tab (9)>
+ <"> = <US-ASCII double-quote mark (34)>
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 15]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all
+ protocol elements except the entity-body (see appendix 19.3 for
+ tolerant applications). The end-of-line marker within an entity-body
+ is defined by its associated media type, as described in section 3.7.
+
+ CRLF = CR LF
+
+ HTTP/1.1 headers can be folded onto multiple lines if the
+ continuation line begins with a space or horizontal tab. All linear
+ white space, including folding, has the same semantics as SP.
+
+ LWS = [CRLF] 1*( SP | HT )
+
+ The TEXT rule is only used for descriptive field contents and values
+ that are not intended to be interpreted by the message parser. Words
+ of *TEXT may contain characters from character sets other than ISO
+ 8859-1 [22] only when encoded according to the rules of RFC 1522
+ [14].
+
+ TEXT = <any OCTET except CTLs,
+ but including LWS>
+
+ Hexadecimal numeric characters are used in several protocol elements.
+
+ HEX = "A" | "B" | "C" | "D" | "E" | "F"
+ | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
+
+ Many HTTP/1.1 header field values consist of words separated by LWS
+ or special characters. These special characters MUST be in a quoted
+ string to be used within a parameter value.
+
+ token = 1*<any CHAR except CTLs or tspecials>
+
+ tspecials = "(" | ")" | "<" | ">" | "@"
+ | "," | ";" | ":" | "\" | <">
+ | "/" | "[" | "]" | "?" | "="
+ | "{" | "}" | SP | HT
+
+ Comments can be included in some HTTP header fields by surrounding
+ the comment text with parentheses. Comments are only allowed in
+ fields containing "comment" as part of their field value definition.
+ In all other fields, parentheses are considered part of the field
+ value.
+
+ comment = "(" *( ctext | comment ) ")"
+ ctext = <any TEXT excluding "(" and ")">
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 16]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ A string of text is parsed as a single word if it is quoted using
+ double-quote marks.
+
+ quoted-string = ( <"> *(qdtext) <"> )
+
+ qdtext = <any TEXT except <">>
+
+ The backslash character ("\") may be used as a single-character quoting
+ mechanism only within quoted-string and comment constructs.
+
+ quoted-pair = "\" CHAR
+
+3 Protocol Parameters
+
+3.1 HTTP Version
+
+ HTTP uses a "<major>.<minor>" numbering scheme to indicate versions
+ of the protocol. The protocol versioning policy is intended to allow
+ the sender to indicate the format of a message and its capacity for
+ understanding further HTTP communication, rather than the features
+ obtained via that communication. No change is made to the version
+ number for the addition of message components which do not affect
+ communication behavior or which only add to extensible field values.
+ The <minor> number is incremented when the changes made to the
+ protocol add features which do not change the general message parsing
+ algorithm, but which may add to the message semantics and imply
+ additional capabilities of the sender. The <major> number is
+ incremented when the format of a message within the protocol is
+ changed.
+
+ The version of an HTTP message is indicated by an HTTP-Version field
+ in the first line of the message.
+
+ HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+
+ Note that the major and minor numbers MUST be treated as separate
+ integers and that each may be incremented higher than a single digit.
+ Thus, HTTP/2.4 is a lower version than HTTP/2.13, which in turn is
+ lower than HTTP/12.3. Leading zeros MUST be ignored by recipients and
+ MUST NOT be sent.
+
+ Applications sending Request or Response messages, as defined by this
+ specification, MUST include an HTTP-Version of "HTTP/1.1". Use of
+ this version number indicates that the sending application is at
+ least conditionally compliant with this specification.
+
+ The HTTP version of an application is the highest HTTP version for
+ which the application is at least conditionally compliant.
+
+
+
+Fielding, et. al. Standards Track [Page 17]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Proxy and gateway applications must be careful when forwarding
+ messages in protocol versions different from that of the application.
+ Since the protocol version indicates the protocol capability of the
+ sender, a proxy/gateway MUST never send a message with a version
+ indicator which is greater than its actual version; if a higher
+ version request is received, the proxy/gateway MUST either downgrade
+ the request version, respond with an error, or switch to tunnel
+ behavior. Requests with a version lower than that of the
+ proxy/gateway's version MAY be upgraded before being forwarded; the
+ proxy/gateway's response to that request MUST be in the same major
+ version as the request.
+
+ Note: Converting between versions of HTTP may involve modification
+ of header fields required or forbidden by the versions involved.
+
+3.2 Uniform Resource Identifiers
+
+ URIs have been known by many names: WWW addresses, Universal Document
+ Identifiers, Universal Resource Identifiers , and finally the
+ combination of Uniform Resource Locators (URL) and Names (URN). As
+ far as HTTP is concerned, Uniform Resource Identifiers are simply
+ formatted strings which identify--via name, location, or any other
+ characteristic--a resource.
+
+3.2.1 General Syntax
+
+ URIs in HTTP can be represented in absolute form or relative to some
+ known base URI, depending upon the context of their use. The two
+ forms are differentiated by the fact that absolute URIs always begin
+ with a scheme name followed by a colon.
+
+ URI = ( absoluteURI | relativeURI ) [ "#" fragment ]
+
+ absoluteURI = scheme ":" *( uchar | reserved )
+
+ relativeURI = net_path | abs_path | rel_path
+
+ net_path = "//" net_loc [ abs_path ]
+ abs_path = "/" rel_path
+ rel_path = [ path ] [ ";" params ] [ "?" query ]
+
+ path = fsegment *( "/" segment )
+ fsegment = 1*pchar
+ segment = *pchar
+
+ params = param *( ";" param )
+ param = *( pchar | "/" )
+
+
+
+
+Fielding, et. al. Standards Track [Page 18]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ scheme = 1*( ALPHA | DIGIT | "+" | "-" | "." )
+ net_loc = *( pchar | ";" | "?" )
+
+ query = *( uchar | reserved )
+ fragment = *( uchar | reserved )
+
+ pchar = uchar | ":" | "@" | "&" | "=" | "+"
+ uchar = unreserved | escape
+ unreserved = ALPHA | DIGIT | safe | extra | national
+
+ escape = "%" HEX HEX
+ reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"
+ extra = "!" | "*" | "'" | "(" | ")" | ","
+ safe = "$" | "-" | "_" | "."
+ unsafe = CTL | SP | <"> | "#" | "%" | "<" | ">"
+ national = <any OCTET excluding ALPHA, DIGIT,
+ reserved, extra, safe, and unsafe>
+
+ For definitive information on URL syntax and semantics, see RFC 1738
+ [4] and RFC 1808 [11]. The BNF above includes national characters not
+ allowed in valid URLs as specified by RFC 1738, since HTTP servers
+ are not restricted in the set of unreserved characters allowed to
+ represent the rel_path part of addresses, and HTTP proxies may
+ receive requests for URIs not defined by RFC 1738.
+
+ The HTTP protocol does not place any a priori limit on the length of
+ a URI. Servers MUST be able to handle the URI of any resource they
+ serve, and SHOULD be able to handle URIs of unbounded length if they
+ provide GET-based forms that could generate such URIs. A server
+ SHOULD return 414 (Request-URI Too Long) status if a URI is longer
+ than the server can handle (see section 10.4.15).
+
+ Note: Servers should be cautious about depending on URI lengths
+ above 255 bytes, because some older client or proxy implementations
+ may not properly support these lengths.
+
+3.2.2 http URL
+
+ The "http" scheme is used to locate network resources via the HTTP
+ protocol. This section defines the scheme-specific syntax and
+ semantics for http URLs.
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 19]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ http_URL = "http:" "//" host [ ":" port ] [ abs_path ]
+
+ host = <A legal Internet host domain name
+ or IP address (in dotted-decimal form),
+ as defined by Section 2.1 of RFC 1123>
+
+ port = *DIGIT
+
+ If the port is empty or not given, port 80 is assumed. The semantics
+ are that the identified resource is located at the server listening
+ for TCP connections on that port of that host, and the Request-URI
+ for the resource is abs_path. The use of IP addresses in URL's SHOULD
+ be avoided whenever possible (see RFC 1900 [24]). If the abs_path is
+ not present in the URL, it MUST be given as "/" when used as a
+ Request-URI for a resource (section 5.1.2).
+
+3.2.3 URI Comparison
+
+ When comparing two URIs to decide if they match or not, a client
+ SHOULD use a case-sensitive octet-by-octet comparison of the entire
+ URIs, with these exceptions:
+
+ o A port that is empty or not given is equivalent to the default
+ port for that URI;
+
+ o Comparisons of host names MUST be case-insensitive;
+
+ o Comparisons of scheme names MUST be case-insensitive;
+
+ o An empty abs_path is equivalent to an abs_path of "/".
+
+ Characters other than those in the "reserved" and "unsafe" sets (see
+ section 3.2) are equivalent to their ""%" HEX HEX" encodings.
+
+ For example, the following three URIs are equivalent:
+
+ http://abc.com:80/~smith/home.html
+ http://ABC.com/%7Esmith/home.html
+ http://ABC.com:/%7esmith/home.html
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 20]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+3.3 Date/Time Formats
+
+3.3.1 Full Date
+
+ HTTP applications have historically allowed three different formats
+ for the representation of date/time stamps:
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+ The first format is preferred as an Internet standard and represents
+ a fixed-length subset of that defined by RFC 1123 (an update to RFC
+ 822). The second format is in common use, but is based on the
+ obsolete RFC 850 [12] date format and lacks a four-digit year.
+ HTTP/1.1 clients and servers that parse the date value MUST accept
+ all three formats (for compatibility with HTTP/1.0), though they MUST
+ only generate the RFC 1123 format for representing HTTP-date values
+ in header fields.
+
+ Note: Recipients of date values are encouraged to be robust in
+ accepting date values that may have been sent by non-HTTP
+ applications, as is sometimes the case when retrieving or posting
+ messages via proxies/gateways to SMTP or NNTP.
+
+ All HTTP date/time stamps MUST be represented in Greenwich Mean Time
+ (GMT), without exception. This is indicated in the first two formats
+ by the inclusion of "GMT" as the three-letter abbreviation for time
+ zone, and MUST be assumed when reading the asctime format.
+
+ HTTP-date = rfc1123-date | rfc850-date | asctime-date
+
+ rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+ rfc850-date = weekday "," SP date2 SP time SP "GMT"
+ asctime-date = wkday SP date3 SP time SP 4DIGIT
+
+ date1 = 2DIGIT SP month SP 4DIGIT
+ ; day month year (e.g., 02 Jun 1982)
+ date2 = 2DIGIT "-" month "-" 2DIGIT
+ ; day-month-year (e.g., 02-Jun-82)
+ date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
+ ; month day (e.g., Jun 2)
+
+ time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ ; 00:00:00 - 23:59:59
+
+ wkday = "Mon" | "Tue" | "Wed"
+ | "Thu" | "Fri" | "Sat" | "Sun"
+
+
+
+Fielding, et. al. Standards Track [Page 21]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ weekday = "Monday" | "Tuesday" | "Wednesday"
+ | "Thursday" | "Friday" | "Saturday" | "Sunday"
+
+ month = "Jan" | "Feb" | "Mar" | "Apr"
+ | "May" | "Jun" | "Jul" | "Aug"
+ | "Sep" | "Oct" | "Nov" | "Dec"
+
+ Note: HTTP requirements for the date/time stamp format apply only
+ to their usage within the protocol stream. Clients and servers are
+ not required to use these formats for user presentation, request
+ logging, etc.
+
+3.3.2 Delta Seconds
+
+ Some HTTP header fields allow a time value to be specified as an
+ integer number of seconds, represented in decimal, after the time
+ that the message was received.
+
+ delta-seconds = 1*DIGIT
+
+3.4 Character Sets
+
+ HTTP uses the same definition of the term "character set" as that
+ described for MIME:
+
+ The term "character set" is used in this document to refer to a
+ method used with one or more tables to convert a sequence of octets
+ into a sequence of characters. Note that unconditional conversion
+ in the other direction is not required, in that not all characters
+ may be available in a given character set and a character set may
+ provide more than one sequence of octets to represent a particular
+ character. This definition is intended to allow various kinds of
+ character encodings, from simple single-table mappings such as US-
+ ASCII to complex table switching methods such as those that use ISO
+ 2022's techniques. However, the definition associated with a MIME
+ character set name MUST fully specify the mapping to be performed
+ from octets to characters. In particular, use of external profiling
+ information to determine the exact mapping is not permitted.
+
+ Note: This use of the term "character set" is more commonly
+ referred to as a "character encoding." However, since HTTP and MIME
+ share the same registry, it is important that the terminology also
+ be shared.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 22]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ HTTP character sets are identified by case-insensitive tokens. The
+ complete set of tokens is defined by the IANA Character Set registry
+ [19].
+
+ charset = token
+
+ Although HTTP allows an arbitrary token to be used as a charset
+ value, any token that has a predefined value within the IANA
+ Character Set registry MUST represent the character set defined by
+ that registry. Applications SHOULD limit their use of character sets
+ to those defined by the IANA registry.
+
+3.5 Content Codings
+
+ Content coding values indicate an encoding transformation that has
+ been or can be applied to an entity. Content codings are primarily
+ used to allow a document to be compressed or otherwise usefully
+ transformed without losing the identity of its underlying media type
+ and without loss of information. Frequently, the entity is stored in
+ coded form, transmitted directly, and only decoded by the recipient.
+
+ content-coding = token
+
+ All content-coding values are case-insensitive. HTTP/1.1 uses
+ content-coding values in the Accept-Encoding (section 14.3) and
+ Content-Encoding (section 14.12) header fields. Although the value
+ describes the content-coding, what is more important is that it
+ indicates what decoding mechanism will be required to remove the
+ encoding.
+
+ The Internet Assigned Numbers Authority (IANA) acts as a registry for
+ content-coding value tokens. Initially, the registry contains the
+ following tokens:
+
+ gzip An encoding format produced by the file compression program "gzip"
+ (GNU zip) as described in RFC 1952 [25]. This format is a Lempel-
+ Ziv coding (LZ77) with a 32 bit CRC.
+
+ compress
+ The encoding format produced by the common UNIX file compression
+ program "compress". This format is an adaptive Lempel-Ziv-Welch
+ coding (LZW).
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 23]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Note: Use of program names for the identification of encoding
+ formats is not desirable and should be discouraged for future
+ encodings. Their use here is representative of historical practice,
+ not good design. For compatibility with previous implementations of
+ HTTP, applications should consider "x-gzip" and "x-compress" to be
+ equivalent to "gzip" and "compress" respectively.
+
+ deflate The "zlib" format defined in RFC 1950[31] in combination with
+ the "deflate" compression mechanism described in RFC 1951[29].
+
+ New content-coding value tokens should be registered; to allow
+ interoperability between clients and servers, specifications of the
+ content coding algorithms needed to implement a new value should be
+ publicly available and adequate for independent implementation, and
+ conform to the purpose of content coding defined in this section.
+
+3.6 Transfer Codings
+
+ Transfer coding values are used to indicate an encoding
+ transformation that has been, can be, or may need to be applied to an
+ entity-body in order to ensure "safe transport" through the network.
+ This differs from a content coding in that the transfer coding is a
+ property of the message, not of the original entity.
+
+ transfer-coding = "chunked" | transfer-extension
+
+ transfer-extension = token
+
+ All transfer-coding values are case-insensitive. HTTP/1.1 uses
+ transfer coding values in the Transfer-Encoding header field (section
+ 14.40).
+
+ Transfer codings are analogous to the Content-Transfer-Encoding
+ values of MIME , which were designed to enable safe transport of
+ binary data over a 7-bit transport service. However, safe transport
+ has a different focus for an 8bit-clean transfer protocol. In HTTP,
+ the only unsafe characteristic of message-bodies is the difficulty in
+ determining the exact body length (section 7.2.2), or the desire to
+ encrypt data over a shared transport.
+
+ The chunked encoding modifies the body of a message in order to
+ transfer it as a series of chunks, each with its own size indicator,
+ followed by an optional footer containing entity-header fields. This
+ allows dynamically-produced content to be transferred along with the
+ information necessary for the recipient to verify that it has
+ received the full message.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 24]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Chunked-Body = *chunk
+ "0" CRLF
+ footer
+ CRLF
+
+ chunk = chunk-size [ chunk-ext ] CRLF
+ chunk-data CRLF
+
+ hex-no-zero = <HEX excluding "0">
+
+ chunk-size = hex-no-zero *HEX
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] )
+ chunk-ext-name = token
+ chunk-ext-val = token | quoted-string
+ chunk-data = chunk-size(OCTET)
+
+ footer = *entity-header
+
+ The chunked encoding is ended by a zero-sized chunk followed by the
+ footer, which is terminated by an empty line. The purpose of the
+ footer is to provide an efficient way to supply information about an
+ entity that is generated dynamically; applications MUST NOT send
+ header fields in the footer which are not explicitly defined as being
+ appropriate for the footer, such as Content-MD5 or future extensions
+ to HTTP for digital signatures or other facilities.
+
+ An example process for decoding a Chunked-Body is presented in
+ appendix 19.4.6.
+
+ All HTTP/1.1 applications MUST be able to receive and decode the
+ "chunked" transfer coding, and MUST ignore transfer coding extensions
+ they do not understand. A server which receives an entity-body with a
+ transfer-coding it does not understand SHOULD return 501
+ (Unimplemented), and close the connection. A server MUST NOT send
+ transfer-codings to an HTTP/1.0 client.
+
+3.7 Media Types
+
+ HTTP uses Internet Media Types in the Content-Type (section 14.18)
+ and Accept (section 14.1) header fields in order to provide open and
+ extensible data typing and type negotiation.
+
+ media-type = type "/" subtype *( ";" parameter )
+ type = token
+ subtype = token
+
+ Parameters may follow the type/subtype in the form of attribute/value
+ pairs.
+
+
+
+Fielding, et. al. Standards Track [Page 25]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ parameter = attribute "=" value
+ attribute = token
+ value = token | quoted-string
+
+ The type, subtype, and parameter attribute names are case-
+ insensitive. Parameter values may or may not be case-sensitive,
+ depending on the semantics of the parameter name. Linear white space
+ (LWS) MUST NOT be used between the type and subtype, nor between an
+ attribute and its value. User agents that recognize the media-type
+ MUST process (or arrange to be processed by any external applications
+ used to process that type/subtype by the user agent) the parameters
+ for that MIME type as described by that type/subtype definition to
+ the and inform the user of any problems discovered.
+
+ Note: some older HTTP applications do not recognize media type
+ parameters. When sending data to older HTTP applications,
+ implementations should only use media type parameters when they are
+ required by that type/subtype definition.
+
+ Media-type values are registered with the Internet Assigned Number
+ Authority (IANA). The media type registration process is outlined in
+ RFC 2048 [17]. Use of non-registered media types is discouraged.
+
+3.7.1 Canonicalization and Text Defaults
+
+ Internet media types are registered with a canonical form. In
+ general, an entity-body transferred via HTTP messages MUST be
+ represented in the appropriate canonical form prior to its
+ transmission; the exception is "text" types, as defined in the next
+ paragraph.
+
+ When in canonical form, media subtypes of the "text" type use CRLF as
+ the text line break. HTTP relaxes this requirement and allows the
+ transport of text media with plain CR or LF alone representing a line
+ break when it is done consistently for an entire entity-body. HTTP
+ applications MUST accept CRLF, bare CR, and bare LF as being
+ representative of a line break in text media received via HTTP. In
+ addition, if the text is represented in a character set that does not
+ use octets 13 and 10 for CR and LF respectively, as is the case for
+ some multi-byte character sets, HTTP allows the use of whatever octet
+ sequences are defined by that character set to represent the
+ equivalent of CR and LF for line breaks. This flexibility regarding
+ line breaks applies only to text media in the entity-body; a bare CR
+ or LF MUST NOT be substituted for CRLF within any of the HTTP control
+ structures (such as header fields and multipart boundaries).
+
+ If an entity-body is encoded with a Content-Encoding, the underlying
+ data MUST be in a form defined above prior to being encoded.
+
+
+
+Fielding, et. al. Standards Track [Page 26]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The "charset" parameter is used with some media types to define the
+ character set (section 3.4) of the data. When no explicit charset
+ parameter is provided by the sender, media subtypes of the "text"
+ type are defined to have a default charset value of "ISO-8859-1" when
+ received via HTTP. Data in character sets other than "ISO-8859-1" or
+ its subsets MUST be labeled with an appropriate charset value.
+
+ Some HTTP/1.0 software has interpreted a Content-Type header without
+ charset parameter incorrectly to mean "recipient should guess."
+ Senders wishing to defeat this behavior MAY include a charset
+ parameter even when the charset is ISO-8859-1 and SHOULD do so when
+ it is known that it will not confuse the recipient.
+
+ Unfortunately, some older HTTP/1.0 clients did not deal properly with
+ an explicit charset parameter. HTTP/1.1 recipients MUST respect the
+ charset label provided by the sender; and those user agents that have
+ a provision to "guess" a charset MUST use the charset from the
+ content-type field if they support that charset, rather than the
+ recipient's preference, when initially displaying a document.
+
+3.7.2 Multipart Types
+
+ MIME provides for a number of "multipart" types -- encapsulations of
+ one or more entities within a single message-body. All multipart
+ types share a common syntax, as defined in MIME [7], and MUST
+ include a boundary parameter as part of the media type value. The
+ message body is itself a protocol element and MUST therefore use only
+ CRLF to represent line breaks between body-parts. Unlike in MIME, the
+ epilogue of any multipart message MUST be empty; HTTP applications
+ MUST NOT transmit the epilogue (even if the original multipart
+ contains an epilogue).
+
+ In HTTP, multipart body-parts MAY contain header fields which are
+ significant to the meaning of that part. A Content-Location header
+ field (section 14.15) SHOULD be included in the body-part of each
+ enclosed entity that can be identified by a URL.
+
+ In general, an HTTP user agent SHOULD follow the same or similar
+ behavior as a MIME user agent would upon receipt of a multipart type.
+ If an application receives an unrecognized multipart subtype, the
+ application MUST treat it as being equivalent to "multipart/mixed".
+
+ Note: The "multipart/form-data" type has been specifically defined
+ for carrying form data suitable for processing via the POST request
+ method, as described in RFC 1867 [15].
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 27]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+3.8 Product Tokens
+
+ Product tokens are used to allow communicating applications to
+ identify themselves by software name and version. Most fields using
+ product tokens also allow sub-products which form a significant part
+ of the application to be listed, separated by whitespace. By
+ convention, the products are listed in order of their significance
+ for identifying the application.
+
+ product = token ["/" product-version]
+ product-version = token
+
+ Examples:
+
+ User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+ Server: Apache/0.8.4
+
+ Product tokens should be short and to the point -- use of them for
+ advertising or other non-essential information is explicitly
+ forbidden. Although any token character may appear in a product-
+ version, this token SHOULD only be used for a version identifier
+ (i.e., successive versions of the same product SHOULD only differ in
+ the product-version portion of the product value).
+
+3.9 Quality Values
+
+ HTTP content negotiation (section 12) uses short "floating point"
+ numbers to indicate the relative importance ("weight") of various
+ negotiable parameters. A weight is normalized to a real number in the
+ range 0 through 1, where 0 is the minimum and 1 the maximum value.
+ HTTP/1.1 applications MUST NOT generate more than three digits after
+ the decimal point. User configuration of these values SHOULD also be
+ limited in this fashion.
+
+ qvalue = ( "0" [ "." 0*3DIGIT ] )
+ | ( "1" [ "." 0*3("0") ] )
+
+ "Quality values" is a misnomer, since these values merely represent
+ relative degradation in desired quality.
+
+3.10 Language Tags
+
+ A language tag identifies a natural language spoken, written, or
+ otherwise conveyed by human beings for communication of information
+ to other human beings. Computer languages are explicitly excluded.
+ HTTP uses language tags within the Accept-Language and Content-
+ Language fields.
+
+
+
+
+Fielding, et. al. Standards Track [Page 28]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The syntax and registry of HTTP language tags is the same as that
+ defined by RFC 1766 [1]. In summary, a language tag is composed of 1
+ or more parts: A primary language tag and a possibly empty series of
+ subtags:
+
+ language-tag = primary-tag *( "-" subtag )
+
+ primary-tag = 1*8ALPHA
+ subtag = 1*8ALPHA
+
+ Whitespace is not allowed within the tag and all tags are case-
+ insensitive. The name space of language tags is administered by the
+ IANA. Example tags include:
+
+ en, en-US, en-cockney, i-cherokee, x-pig-latin
+
+ where any two-letter primary-tag is an ISO 639 language abbreviation
+ and any two-letter initial subtag is an ISO 3166 country code. (The
+ last three tags above are not registered tags; all but the last are
+ examples of tags which could be registered in future.)
+
+3.11 Entity Tags
+
+ Entity tags are used for comparing two or more entities from the same
+ requested resource. HTTP/1.1 uses entity tags in the ETag (section
+ 14.20), If-Match (section 14.25), If-None-Match (section 14.26), and
+ If-Range (section 14.27) header fields. The definition of how they
+ are used and compared as cache validators is in section 13.3.3. An
+ entity tag consists of an opaque quoted string, possibly prefixed by
+ a weakness indicator.
+
+ entity-tag = [ weak ] opaque-tag
+
+ weak = "W/"
+ opaque-tag = quoted-string
+
+ A "strong entity tag" may be shared by two entities of a resource
+ only if they are equivalent by octet equality.
+
+ A "weak entity tag," indicated by the "W/" prefix, may be shared by
+ two entities of a resource only if the entities are equivalent and
+ could be substituted for each other with no significant change in
+ semantics. A weak entity tag can only be used for weak comparison.
+
+ An entity tag MUST be unique across all versions of all entities
+ associated with a particular resource. A given entity tag value may
+ be used for entities obtained by requests on different URIs without
+ implying anything about the equivalence of those entities.
+
+
+
+Fielding, et. al. Standards Track [Page 29]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+3.12 Range Units
+
+ HTTP/1.1 allows a client to request that only part (a range of) the
+ response entity be included within the response. HTTP/1.1 uses range
+ units in the Range (section 14.36) and Content-Range (section 14.17)
+ header fields. An entity may be broken down into subranges according
+ to various structural units.
+
+ range-unit = bytes-unit | other-range-unit
+
+ bytes-unit = "bytes"
+ other-range-unit = token
+
+The only range unit defined by HTTP/1.1 is "bytes". HTTP/1.1
+ implementations may ignore ranges specified using other units.
+ HTTP/1.1 has been designed to allow implementations of applications
+ that do not depend on knowledge of ranges.
+
+4 HTTP Message
+
+4.1 Message Types
+
+ HTTP messages consist of requests from client to server and responses
+ from server to client.
+
+ HTTP-message = Request | Response ; HTTP/1.1 messages
+
+ Request (section 5) and Response (section 6) messages use the generic
+ message format of RFC 822 [9] for transferring entities (the payload
+ of the message). Both types of message consist of a start-line, one
+ or more header fields (also known as "headers"), an empty line (i.e.,
+ a line with nothing preceding the CRLF) indicating the end of the
+ header fields, and an optional message-body.
+
+ generic-message = start-line
+ *message-header
+ CRLF
+ [ message-body ]
+
+ start-line = Request-Line | Status-Line
+
+ In the interest of robustness, servers SHOULD ignore any empty
+ line(s) received where a Request-Line is expected. In other words, if
+ the server is reading the protocol stream at the beginning of a
+ message and receives a CRLF first, it should ignore the CRLF.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 30]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Note: certain buggy HTTP/1.0 client implementations generate an
+ extra CRLF's after a POST request. To restate what is explicitly
+ forbidden by the BNF, an HTTP/1.1 client must not preface or follow
+ a request with an extra CRLF.
+
+4.2 Message Headers
+
+ HTTP header fields, which include general-header (section 4.5),
+ request-header (section 5.3), response-header (section 6.2), and
+ entity-header (section 7.1) fields, follow the same generic format as
+ that given in Section 3.1 of RFC 822 [9]. Each header field consists
+ of a name followed by a colon (":") and the field value. Field names
+ are case-insensitive. The field value may be preceded by any amount
+ of LWS, though a single SP is preferred. Header fields can be
+ extended over multiple lines by preceding each extra line with at
+ least one SP or HT. Applications SHOULD follow "common form" when
+ generating HTTP constructs, since there might exist some
+ implementations that fail to accept anything beyond the common forms.
+
+ message-header = field-name ":" [ field-value ] CRLF
+
+ field-name = token
+ field-value = *( field-content | LWS )
+
+ field-content = <the OCTETs making up the field-value
+ and consisting of either *TEXT or combinations
+ of token, tspecials, and quoted-string>
+
+ The order in which header fields with differing field names are
+ received is not significant. However, it is "good practice" to send
+ general-header fields first, followed by request-header or response-
+ header fields, and ending with the entity-header fields.
+
+ Multiple message-header fields with the same field-name may be
+ present in a message if and only if the entire field-value for that
+ header field is defined as a comma-separated list [i.e., #(values)].
+ It MUST be possible to combine the multiple header fields into one
+ "field-name: field-value" pair, without changing the semantics of the
+ message, by appending each subsequent field-value to the first, each
+ separated by a comma. The order in which header fields with the same
+ field-name are received is therefore significant to the
+ interpretation of the combined field value, and thus a proxy MUST NOT
+ change the order of these field values when a message is forwarded.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 31]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+4.3 Message Body
+
+ The message-body (if any) of an HTTP message is used to carry the
+ entity-body associated with the request or response. The message-body
+ differs from the entity-body only when a transfer coding has been
+ applied, as indicated by the Transfer-Encoding header field (section
+ 14.40).
+
+ message-body = entity-body
+ | <entity-body encoded as per Transfer-Encoding>
+
+ Transfer-Encoding MUST be used to indicate any transfer codings
+ applied by an application to ensure safe and proper transfer of the
+ message. Transfer-Encoding is a property of the message, not of the
+ entity, and thus can be added or removed by any application along the
+ request/response chain.
+
+ The rules for when a message-body is allowed in a message differ for
+ requests and responses.
+
+ The presence of a message-body in a request is signaled by the
+ inclusion of a Content-Length or Transfer-Encoding header field in
+ the request's message-headers. A message-body MAY be included in a
+ request only when the request method (section 5.1.1) allows an
+ entity-body.
+
+ For response messages, whether or not a message-body is included with
+ a message is dependent on both the request method and the response
+ status code (section 6.1.1). All responses to the HEAD request method
+ MUST NOT include a message-body, even though the presence of entity-
+ header fields might lead one to believe they do. All 1xx
+ (informational), 204 (no content), and 304 (not modified) responses
+ MUST NOT include a message-body. All other responses do include a
+ message-body, although it may be of zero length.
+
+4.4 Message Length
+
+ When a message-body is included with a message, the length of that
+ body is determined by one of the following (in order of precedence):
+
+ 1. Any response message which MUST NOT include a message-body
+ (such as the 1xx, 204, and 304 responses and any response to a HEAD
+ request) is always terminated by the first empty line after the
+ header fields, regardless of the entity-header fields present in the
+ message.
+
+ 2. If a Transfer-Encoding header field (section 14.40) is present and
+ indicates that the "chunked" transfer coding has been applied, then
+
+
+
+Fielding, et. al. Standards Track [Page 32]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ the length is defined by the chunked encoding (section 3.6).
+
+ 3. If a Content-Length header field (section 14.14) is present, its
+ value in bytes represents the length of the message-body.
+
+ 4. If the message uses the media type "multipart/byteranges", which is
+ self-delimiting, then that defines the length. This media type MUST
+ NOT be used unless the sender knows that the recipient can parse it;
+ the presence in a request of a Range header with multiple byte-range
+ specifiers implies that the client can parse multipart/byteranges
+ responses.
+
+ 5. By the server closing the connection. (Closing the connection
+ cannot be used to indicate the end of a request body, since that
+ would leave no possibility for the server to send back a response.)
+
+ For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
+ containing a message-body MUST include a valid Content-Length header
+ field unless the server is known to be HTTP/1.1 compliant. If a
+ request contains a message-body and a Content-Length is not given,
+ the server SHOULD respond with 400 (bad request) if it cannot
+ determine the length of the message, or with 411 (length required) if
+ it wishes to insist on receiving a valid Content-Length.
+
+ All HTTP/1.1 applications that receive entities MUST accept the
+ "chunked" transfer coding (section 3.6), thus allowing this mechanism
+ to be used for messages when the message length cannot be determined
+ in advance.
+
+ Messages MUST NOT include both a Content-Length header field and the
+ "chunked" transfer coding. If both are received, the Content-Length
+ MUST be ignored.
+
+ When a Content-Length is given in a message where a message-body is
+ allowed, its field value MUST exactly match the number of OCTETs in
+ the message-body. HTTP/1.1 user agents MUST notify the user when an
+ invalid length is received and detected.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 33]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+4.5 General Header Fields
+
+ There are a few header fields which have general applicability for
+ both request and response messages, but which do not apply to the
+ entity being transferred. These header fields apply only to the
+ message being transmitted.
+
+ general-header = Cache-Control ; Section 14.9
+ | Connection ; Section 14.10
+ | Date ; Section 14.19
+ | Pragma ; Section 14.32
+ | Transfer-Encoding ; Section 14.40
+ | Upgrade ; Section 14.41
+ | Via ; Section 14.44
+
+ General-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields may be given the semantics of general
+ header fields if all parties in the communication recognize them to
+ be general-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+5 Request
+
+ A request message from a client to a server includes, within the
+ first line of that message, the method to be applied to the resource,
+ the identifier of the resource, and the protocol version in use.
+
+ Request = Request-Line ; Section 5.1
+ *( general-header ; Section 4.5
+ | request-header ; Section 5.3
+ | entity-header ) ; Section 7.1
+ CRLF
+ [ message-body ] ; Section 7.2
+
+5.1 Request-Line
+
+ The Request-Line begins with a method token, followed by the
+ Request-URI and the protocol version, and ending with CRLF. The
+ elements are separated by SP characters. No CR or LF are allowed
+ except in the final CRLF sequence.
+
+ Request-Line = Method SP Request-URI SP HTTP-Version CRLF
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 34]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+5.1.1 Method
+
+ The Method token indicates the method to be performed on the resource
+ identified by the Request-URI. The method is case-sensitive.
+
+ Method = "OPTIONS" ; Section 9.2
+ | "GET" ; Section 9.3
+ | "HEAD" ; Section 9.4
+ | "POST" ; Section 9.5
+ | "PUT" ; Section 9.6
+ | "DELETE" ; Section 9.7
+ | "TRACE" ; Section 9.8
+ | extension-method
+
+ extension-method = token
+
+ The list of methods allowed by a resource can be specified in an
+ Allow header field (section 14.7). The return code of the response
+ always notifies the client whether a method is currently allowed on a
+ resource, since the set of allowed methods can change dynamically.
+ Servers SHOULD return the status code 405 (Method Not Allowed) if the
+ method is known by the server but not allowed for the requested
+ resource, and 501 (Not Implemented) if the method is unrecognized or
+ not implemented by the server. The list of methods known by a server
+ can be listed in a Public response-header field (section 14.35).
+
+ The methods GET and HEAD MUST be supported by all general-purpose
+ servers. All other methods are optional; however, if the above
+ methods are implemented, they MUST be implemented with the same
+ semantics as those specified in section 9.
+
+5.1.2 Request-URI
+
+ The Request-URI is a Uniform Resource Identifier (section 3.2) and
+ identifies the resource upon which to apply the request.
+
+ Request-URI = "*" | absoluteURI | abs_path
+
+ The three options for Request-URI are dependent on the nature of the
+ request. The asterisk "*" means that the request does not apply to a
+ particular resource, but to the server itself, and is only allowed
+ when the method used does not necessarily apply to a resource. One
+ example would be
+
+ OPTIONS * HTTP/1.1
+
+ The absoluteURI form is required when the request is being made to a
+ proxy. The proxy is requested to forward the request or service it
+
+
+
+Fielding, et. al. Standards Track [Page 35]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ from a valid cache, and return the response. Note that the proxy MAY
+ forward the request on to another proxy or directly to the server
+ specified by the absoluteURI. In order to avoid request loops, a
+ proxy MUST be able to recognize all of its server names, including
+ any aliases, local variations, and the numeric IP address. An example
+ Request-Line would be:
+
+ GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1
+
+ To allow for transition to absoluteURIs in all requests in future
+ versions of HTTP, all HTTP/1.1 servers MUST accept the absoluteURI
+ form in requests, even though HTTP/1.1 clients will only generate
+ them in requests to proxies.
+
+ The most common form of Request-URI is that used to identify a
+ resource on an origin server or gateway. In this case the absolute
+ path of the URI MUST be transmitted (see section 3.2.1, abs_path) as
+ the Request-URI, and the network location of the URI (net_loc) MUST
+ be transmitted in a Host header field. For example, a client wishing
+ to retrieve the resource above directly from the origin server would
+ create a TCP connection to port 80 of the host "www.w3.org" and send
+ the lines:
+
+ GET /pub/WWW/TheProject.html HTTP/1.1
+ Host: www.w3.org
+
+ followed by the remainder of the Request. Note that the absolute path
+ cannot be empty; if none is present in the original URI, it MUST be
+ given as "/" (the server root).
+
+ If a proxy receives a request without any path in the Request-URI and
+ the method specified is capable of supporting the asterisk form of
+ request, then the last proxy on the request chain MUST forward the
+ request with "*" as the final Request-URI. For example, the request
+
+ OPTIONS http://www.ics.uci.edu:8001 HTTP/1.1
+
+ would be forwarded by the proxy as
+
+ OPTIONS * HTTP/1.1
+ Host: www.ics.uci.edu:8001
+
+ after connecting to port 8001 of host "www.ics.uci.edu".
+
+ The Request-URI is transmitted in the format specified in section
+ 3.2.1. The origin server MUST decode the Request-URI in order to
+ properly interpret the request. Servers SHOULD respond to invalid
+ Request-URIs with an appropriate status code.
+
+
+
+Fielding, et. al. Standards Track [Page 36]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ In requests that they forward, proxies MUST NOT rewrite the
+ "abs_path" part of a Request-URI in any way except as noted above to
+ replace a null abs_path with "*", no matter what the proxy does in
+ its internal implementation.
+
+ Note: The "no rewrite" rule prevents the proxy from changing the
+ meaning of the request when the origin server is improperly using a
+ non-reserved URL character for a reserved purpose. Implementers
+ should be aware that some pre-HTTP/1.1 proxies have been known to
+ rewrite the Request-URI.
+
+5.2 The Resource Identified by a Request
+
+ HTTP/1.1 origin servers SHOULD be aware that the exact resource
+ identified by an Internet request is determined by examining both the
+ Request-URI and the Host header field.
+
+ An origin server that does not allow resources to differ by the
+ requested host MAY ignore the Host header field value. (But see
+ section 19.5.1 for other requirements on Host support in HTTP/1.1.)
+
+ An origin server that does differentiate resources based on the host
+ requested (sometimes referred to as virtual hosts or vanity
+ hostnames) MUST use the following rules for determining the requested
+ resource on an HTTP/1.1 request:
+
+ 1. If Request-URI is an absoluteURI, the host is part of the
+ Request-URI. Any Host header field value in the request MUST be
+ ignored.
+
+ 2. If the Request-URI is not an absoluteURI, and the request
+ includes a Host header field, the host is determined by the Host
+ header field value.
+
+ 3. If the host as determined by rule 1 or 2 is not a valid host on
+ the server, the response MUST be a 400 (Bad Request) error
+ message.
+
+ Recipients of an HTTP/1.0 request that lacks a Host header field MAY
+ attempt to use heuristics (e.g., examination of the URI path for
+ something unique to a particular host) in order to determine what
+ exact resource is being requested.
+
+5.3 Request Header Fields
+
+ The request-header fields allow the client to pass additional
+ information about the request, and about the client itself, to the
+ server. These fields act as request modifiers, with semantics
+
+
+
+Fielding, et. al. Standards Track [Page 37]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ equivalent to the parameters on a programming language method
+ invocation.
+
+ request-header = Accept ; Section 14.1
+ | Accept-Charset ; Section 14.2
+ | Accept-Encoding ; Section 14.3
+ | Accept-Language ; Section 14.4
+ | Authorization ; Section 14.8
+ | From ; Section 14.22
+ | Host ; Section 14.23
+ | If-Modified-Since ; Section 14.24
+ | If-Match ; Section 14.25
+ | If-None-Match ; Section 14.26
+ | If-Range ; Section 14.27
+ | If-Unmodified-Since ; Section 14.28
+ | Max-Forwards ; Section 14.31
+ | Proxy-Authorization ; Section 14.34
+ | Range ; Section 14.36
+ | Referer ; Section 14.37
+ | User-Agent ; Section 14.42
+
+ Request-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields MAY be given the semantics of request-
+ header fields if all parties in the communication recognize them to
+ be request-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+6 Response
+
+ After receiving and interpreting a request message, a server responds
+ with an HTTP response message.
+
+ Response = Status-Line ; Section 6.1
+ *( general-header ; Section 4.5
+ | response-header ; Section 6.2
+ | entity-header ) ; Section 7.1
+ CRLF
+ [ message-body ] ; Section 7.2
+
+6.1 Status-Line
+
+ The first line of a Response message is the Status-Line, consisting
+ of the protocol version followed by a numeric status code and its
+ associated textual phrase, with each element separated by SP
+ characters. No CR or LF is allowed except in the final CRLF
+ sequence.
+
+
+
+
+Fielding, et. al. Standards Track [Page 38]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+
+6.1.1 Status Code and Reason Phrase
+
+ The Status-Code element is a 3-digit integer result code of the
+ attempt to understand and satisfy the request. These codes are fully
+ defined in section 10. The Reason-Phrase is intended to give a short
+ textual description of the Status-Code. The Status-Code is intended
+ for use by automata and the Reason-Phrase is intended for the human
+ user. The client is not required to examine or display the Reason-
+ Phrase.
+
+ The first digit of the Status-Code defines the class of response. The
+ last two digits do not have any categorization role. There are 5
+ values for the first digit:
+
+ o 1xx: Informational - Request received, continuing process
+
+ o 2xx: Success - The action was successfully received, understood,
+ and accepted
+
+ o 3xx: Redirection - Further action must be taken in order to
+ complete the request
+
+ o 4xx: Client Error - The request contains bad syntax or cannot be
+ fulfilled
+
+ o 5xx: Server Error - The server failed to fulfill an apparently
+ valid request
+
+ The individual values of the numeric status codes defined for
+ HTTP/1.1, and an example set of corresponding Reason-Phrase's, are
+ presented below. The reason phrases listed here are only recommended
+ -- they may be replaced by local equivalents without affecting the
+ protocol.
+
+ Status-Code = "100" ; Continue
+ | "101" ; Switching Protocols
+ | "200" ; OK
+ | "201" ; Created
+ | "202" ; Accepted
+ | "203" ; Non-Authoritative Information
+ | "204" ; No Content
+ | "205" ; Reset Content
+ | "206" ; Partial Content
+ | "300" ; Multiple Choices
+ | "301" ; Moved Permanently
+ | "302" ; Moved Temporarily
+
+
+
+Fielding, et. al. Standards Track [Page 39]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ | "303" ; See Other
+ | "304" ; Not Modified
+ | "305" ; Use Proxy
+ | "400" ; Bad Request
+ | "401" ; Unauthorized
+ | "402" ; Payment Required
+ | "403" ; Forbidden
+ | "404" ; Not Found
+ | "405" ; Method Not Allowed
+ | "406" ; Not Acceptable
+ | "407" ; Proxy Authentication Required
+ | "408" ; Request Time-out
+ | "409" ; Conflict
+ | "410" ; Gone
+ | "411" ; Length Required
+ | "412" ; Precondition Failed
+ | "413" ; Request Entity Too Large
+ | "414" ; Request-URI Too Large
+ | "415" ; Unsupported Media Type
+ | "500" ; Internal Server Error
+ | "501" ; Not Implemented
+ | "502" ; Bad Gateway
+ | "503" ; Service Unavailable
+ | "504" ; Gateway Time-out
+ | "505" ; HTTP Version not supported
+ | extension-code
+
+ extension-code = 3DIGIT
+
+ Reason-Phrase = *<TEXT, excluding CR, LF>
+
+ HTTP status codes are extensible. HTTP applications are not required
+ to understand the meaning of all registered status codes, though such
+ understanding is obviously desirable. However, applications MUST
+ understand the class of any status code, as indicated by the first
+ digit, and treat any unrecognized response as being equivalent to the
+ x00 status code of that class, with the exception that an
+ unrecognized response MUST NOT be cached. For example, if an
+ unrecognized status code of 431 is received by the client, it can
+ safely assume that there was something wrong with its request and
+ treat the response as if it had received a 400 status code. In such
+ cases, user agents SHOULD present to the user the entity returned
+ with the response, since that entity is likely to include human-
+ readable information which will explain the unusual status.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 40]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+6.2 Response Header Fields
+
+ The response-header fields allow the server to pass additional
+ information about the response which cannot be placed in the Status-
+ Line. These header fields give information about the server and about
+ further access to the resource identified by the Request-URI.
+
+ response-header = Age ; Section 14.6
+ | Location ; Section 14.30
+ | Proxy-Authenticate ; Section 14.33
+ | Public ; Section 14.35
+ | Retry-After ; Section 14.38
+ | Server ; Section 14.39
+ | Vary ; Section 14.43
+ | Warning ; Section 14.45
+ | WWW-Authenticate ; Section 14.46
+
+ Response-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields MAY be given the semantics of response-
+ header fields if all parties in the communication recognize them to
+ be response-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+7 Entity
+
+ Request and Response messages MAY transfer an entity if not otherwise
+ restricted by the request method or response status code. An entity
+ consists of entity-header fields and an entity-body, although some
+ responses will only include the entity-headers.
+
+ In this section, both sender and recipient refer to either the client
+ or the server, depending on who sends and who receives the entity.
+
+7.1 Entity Header Fields
+
+ Entity-header fields define optional metainformation about the
+ entity-body or, if no body is present, about the resource identified
+ by the request.
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 41]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ entity-header = Allow ; Section 14.7
+ | Content-Base ; Section 14.11
+ | Content-Encoding ; Section 14.12
+ | Content-Language ; Section 14.13
+ | Content-Length ; Section 14.14
+ | Content-Location ; Section 14.15
+ | Content-MD5 ; Section 14.16
+ | Content-Range ; Section 14.17
+ | Content-Type ; Section 14.18
+ | ETag ; Section 14.20
+ | Expires ; Section 14.21
+ | Last-Modified ; Section 14.29
+ | extension-header
+
+ extension-header = message-header
+
+ The extension-header mechanism allows additional entity-header fields
+ to be defined without changing the protocol, but these fields cannot
+ be assumed to be recognizable by the recipient. Unrecognized header
+ fields SHOULD be ignored by the recipient and forwarded by proxies.
+
+7.2 Entity Body
+
+ The entity-body (if any) sent with an HTTP request or response is in
+ a format and encoding defined by the entity-header fields.
+
+ entity-body = *OCTET
+
+ An entity-body is only present in a message when a message-body is
+ present, as described in section 4.3. The entity-body is obtained
+ from the message-body by decoding any Transfer-Encoding that may have
+ been applied to ensure safe and proper transfer of the message.
+
+7.2.1 Type
+
+ When an entity-body is included with a message, the data type of that
+ body is determined via the header fields Content-Type and Content-
+ Encoding. These define a two-layer, ordered encoding model:
+
+ entity-body := Content-Encoding( Content-Type( data ) )
+
+ Content-Type specifies the media type of the underlying data.
+ Content-Encoding may be used to indicate any additional content
+ codings applied to the data, usually for the purpose of data
+ compression, that are a property of the requested resource. There is
+ no default encoding.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 42]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Any HTTP/1.1 message containing an entity-body SHOULD include a
+ Content-Type header field defining the media type of that body. If
+ and only if the media type is not given by a Content-Type field, the
+ recipient MAY attempt to guess the media type via inspection of its
+ content and/or the name extension(s) of the URL used to identify the
+ resource. If the media type remains unknown, the recipient SHOULD
+ treat it as type "application/octet-stream".
+
+7.2.2 Length
+
+ The length of an entity-body is the length of the message-body after
+ any transfer codings have been removed. Section 4.4 defines how the
+ length of a message-body is determined.
+
+8 Connections
+
+8.1 Persistent Connections
+
+8.1.1 Purpose
+
+ Prior to persistent connections, a separate TCP connection was
+ established to fetch each URL, increasing the load on HTTP servers
+ and causing congestion on the Internet. The use of inline images and
+ other associated data often requires a client to make multiple
+ requests of the same server in a short amount of time. Analyses of
+ these performance problems are available [30][27]; analysis and
+ results from a prototype implementation are in [26].
+
+ Persistent HTTP connections have a number of advantages:
+
+ o By opening and closing fewer TCP connections, CPU time is saved,
+ and memory used for TCP protocol control blocks is also saved.
+ o HTTP requests and responses can be pipelined on a connection.
+ Pipelining allows a client to make multiple requests without
+ waiting for each response, allowing a single TCP connection to be
+ used much more efficiently, with much lower elapsed time.
+ o Network congestion is reduced by reducing the number of packets
+ caused by TCP opens, and by allowing TCP sufficient time to
+ determine the congestion state of the network.
+ o HTTP can evolve more gracefully; since errors can be reported
+ without the penalty of closing the TCP connection. Clients using
+ future versions of HTTP might optimistically try a new feature, but
+ if communicating with an older server, retry with old semantics
+ after an error is reported.
+
+ HTTP implementations SHOULD implement persistent connections.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 43]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+8.1.2 Overall Operation
+
+ A significant difference between HTTP/1.1 and earlier versions of
+ HTTP is that persistent connections are the default behavior of any
+ HTTP connection. That is, unless otherwise indicated, the client may
+ assume that the server will maintain a persistent connection.
+
+ Persistent connections provide a mechanism by which a client and a
+ server can signal the close of a TCP connection. This signaling takes
+ place using the Connection header field. Once a close has been
+ signaled, the client MUST not send any more requests on that
+ connection.
+
+8.1.2.1 Negotiation
+
+ An HTTP/1.1 server MAY assume that a HTTP/1.1 client intends to
+ maintain a persistent connection unless a Connection header including
+ the connection-token "close" was sent in the request. If the server
+ chooses to close the connection immediately after sending the
+ response, it SHOULD send a Connection header including the
+ connection-token close.
+
+ An HTTP/1.1 client MAY expect a connection to remain open, but would
+ decide to keep it open based on whether the response from a server
+ contains a Connection header with the connection-token close. In case
+ the client does not want to maintain a connection for more than that
+ request, it SHOULD send a Connection header including the
+ connection-token close.
+
+ If either the client or the server sends the close token in the
+ Connection header, that request becomes the last one for the
+ connection.
+
+ Clients and servers SHOULD NOT assume that a persistent connection is
+ maintained for HTTP versions less than 1.1 unless it is explicitly
+ signaled. See section 19.7.1 for more information on backwards
+ compatibility with HTTP/1.0 clients.
+
+ In order to remain persistent, all messages on the connection must
+ have a self-defined message length (i.e., one not defined by closure
+ of the connection), as described in section 4.4.
+
+8.1.2.2 Pipelining
+
+ A client that supports persistent connections MAY "pipeline" its
+ requests (i.e., send multiple requests without waiting for each
+ response). A server MUST send its responses to those requests in the
+ same order that the requests were received.
+
+
+
+Fielding, et. al. Standards Track [Page 44]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Clients which assume persistent connections and pipeline immediately
+ after connection establishment SHOULD be prepared to retry their
+ connection if the first pipelined attempt fails. If a client does
+ such a retry, it MUST NOT pipeline before it knows the connection is
+ persistent. Clients MUST also be prepared to resend their requests if
+ the server closes the connection before sending all of the
+ corresponding responses.
+
+8.1.3 Proxy Servers
+
+ It is especially important that proxies correctly implement the
+ properties of the Connection header field as specified in 14.2.1.
+
+ The proxy server MUST signal persistent connections separately with
+ its clients and the origin servers (or other proxy servers) that it
+ connects to. Each persistent connection applies to only one transport
+ link.
+
+ A proxy server MUST NOT establish a persistent connection with an
+ HTTP/1.0 client.
+
+8.1.4 Practical Considerations
+
+ Servers will usually have some time-out value beyond which they will
+ no longer maintain an inactive connection. Proxy servers might make
+ this a higher value since it is likely that the client will be making
+ more connections through the same server. The use of persistent
+ connections places no requirements on the length of this time-out for
+ either the client or the server.
+
+ When a client or server wishes to time-out it SHOULD issue a graceful
+ close on the transport connection. Clients and servers SHOULD both
+ constantly watch for the other side of the transport close, and
+ respond to it as appropriate. If a client or server does not detect
+ the other side's close promptly it could cause unnecessary resource
+ drain on the network.
+
+ A client, server, or proxy MAY close the transport connection at any
+ time. For example, a client MAY have started to send a new request at
+ the same time that the server has decided to close the "idle"
+ connection. From the server's point of view, the connection is being
+ closed while it was idle, but from the client's point of view, a
+ request is in progress.
+
+ This means that clients, servers, and proxies MUST be able to recover
+ from asynchronous close events. Client software SHOULD reopen the
+ transport connection and retransmit the aborted request without user
+ interaction so long as the request method is idempotent (see section
+
+
+
+Fielding, et. al. Standards Track [Page 45]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ 9.1.2); other methods MUST NOT be automatically retried, although
+ user agents MAY offer a human operator the choice of retrying the
+ request.
+
+ However, this automatic retry SHOULD NOT be repeated if the second
+ request fails.
+
+ Servers SHOULD always respond to at least one request per connection,
+ if at all possible. Servers SHOULD NOT close a connection in the
+ middle of transmitting a response, unless a network or client failure
+ is suspected.
+
+ Clients that use persistent connections SHOULD limit the number of
+ simultaneous connections that they maintain to a given server. A
+ single-user client SHOULD maintain AT MOST 2 connections with any
+ server or proxy. A proxy SHOULD use up to 2*N connections to another
+ server or proxy, where N is the number of simultaneously active
+ users. These guidelines are intended to improve HTTP response times
+ and avoid congestion of the Internet or other networks.
+
+8.2 Message Transmission Requirements
+
+General requirements:
+
+o HTTP/1.1 servers SHOULD maintain persistent connections and use
+ TCP's flow control mechanisms to resolve temporary overloads,
+ rather than terminating connections with the expectation that
+ clients will retry. The latter technique can exacerbate network
+ congestion.
+
+o An HTTP/1.1 (or later) client sending a message-body SHOULD monitor
+ the network connection for an error status while it is transmitting
+ the request. If the client sees an error status, it SHOULD
+ immediately cease transmitting the body. If the body is being sent
+ using a "chunked" encoding (section 3.6), a zero length chunk and
+ empty footer MAY be used to prematurely mark the end of the
+ message. If the body was preceded by a Content-Length header, the
+ client MUST close the connection.
+
+o An HTTP/1.1 (or later) client MUST be prepared to accept a 100
+ (Continue) status followed by a regular response.
+
+o An HTTP/1.1 (or later) server that receives a request from a
+ HTTP/1.0 (or earlier) client MUST NOT transmit the 100 (continue)
+ response; it SHOULD either wait for the request to be completed
+ normally (thus avoiding an interrupted request) or close the
+ connection prematurely.
+
+
+
+
+Fielding, et. al. Standards Track [Page 46]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Upon receiving a method subject to these requirements from an
+ HTTP/1.1 (or later) client, an HTTP/1.1 (or later) server MUST either
+ respond with 100 (Continue) status and continue to read from the
+ input stream, or respond with an error status. If it responds with an
+ error status, it MAY close the transport (TCP) connection or it MAY
+ continue to read and discard the rest of the request. It MUST NOT
+ perform the requested method if it returns an error status.
+
+ Clients SHOULD remember the version number of at least the most
+ recently used server; if an HTTP/1.1 client has seen an HTTP/1.1 or
+ later response from the server, and it sees the connection close
+ before receiving any status from the server, the client SHOULD retry
+ the request without user interaction so long as the request method is
+ idempotent (see section 9.1.2); other methods MUST NOT be
+ automatically retried, although user agents MAY offer a human
+ operator the choice of retrying the request.. If the client does
+ retry the request, the client
+
+ o MUST first send the request header fields, and then
+
+ o MUST wait for the server to respond with either a 100 (Continue)
+ response, in which case the client should continue, or with an
+ error status.
+
+ If an HTTP/1.1 client has not seen an HTTP/1.1 or later response from
+ the server, it should assume that the server implements HTTP/1.0 or
+ older and will not use the 100 (Continue) response. If in this case
+ the client sees the connection close before receiving any status from
+ the server, the client SHOULD retry the request. If the client does
+ retry the request to this HTTP/1.0 server, it should use the
+ following "binary exponential backoff" algorithm to be assured of
+ obtaining a reliable response:
+
+ 1. Initiate a new connection to the server
+
+ 2. Transmit the request-headers
+
+ 3. Initialize a variable R to the estimated round-trip time to the
+ server (e.g., based on the time it took to establish the
+ connection), or to a constant value of 5 seconds if the round-trip
+ time is not available.
+
+ 4. Compute T = R * (2**N), where N is the number of previous retries
+ of this request.
+
+ 5. Wait either for an error response from the server, or for T seconds
+ (whichever comes first)
+
+
+
+
+Fielding, et. al. Standards Track [Page 47]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ 6. If no error response is received, after T seconds transmit the body
+ of the request.
+
+ 7. If client sees that the connection is closed prematurely, repeat
+ from step 1 until the request is accepted, an error response is
+ received, or the user becomes impatient and terminates the retry
+ process.
+
+ No matter what the server version, if an error status is received,
+ the client
+
+ o MUST NOT continue and
+
+ o MUST close the connection if it has not completed sending the
+ message.
+
+ An HTTP/1.1 (or later) client that sees the connection close after
+ receiving a 100 (Continue) but before receiving any other status
+ SHOULD retry the request, and need not wait for 100 (Continue)
+ response (but MAY do so if this simplifies the implementation).
+
+9 Method Definitions
+
+ The set of common methods for HTTP/1.1 is defined below. Although
+ this set can be expanded, additional methods cannot be assumed to
+ share the same semantics for separately extended clients and servers.
+
+ The Host request-header field (section 14.23) MUST accompany all
+ HTTP/1.1 requests.
+
+9.1 Safe and Idempotent Methods
+
+9.1.1 Safe Methods
+
+ Implementers should be aware that the software represents the user in
+ their interactions over the Internet, and should be careful to allow
+ the user to be aware of any actions they may take which may have an
+ unexpected significance to themselves or others.
+
+ In particular, the convention has been established that the GET and
+ HEAD methods should never have the significance of taking an action
+ other than retrieval. These methods should be considered "safe." This
+ allows user agents to represent other methods, such as POST, PUT and
+ DELETE, in a special way, so that the user is made aware of the fact
+ that a possibly unsafe action is being requested.
+
+ Naturally, it is not possible to ensure that the server does not
+ generate side-effects as a result of performing a GET request; in
+
+
+
+Fielding, et. al. Standards Track [Page 48]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ fact, some dynamic resources consider that a feature. The important
+ distinction here is that the user did not request the side-effects,
+ so therefore cannot be held accountable for them.
+
+9.1.2 Idempotent Methods
+
+ Methods may also have the property of "idempotence" in that (aside
+ from error or expiration issues) the side-effects of N > 0 identical
+ requests is the same as for a single request. The methods GET, HEAD,
+ PUT and DELETE share this property.
+
+9.2 OPTIONS
+
+ The OPTIONS method represents a request for information about the
+ communication options available on the request/response chain
+ identified by the Request-URI. This method allows the client to
+ determine the options and/or requirements associated with a resource,
+ or the capabilities of a server, without implying a resource action
+ or initiating a resource retrieval.
+
+ Unless the server's response is an error, the response MUST NOT
+ include entity information other than what can be considered as
+ communication options (e.g., Allow is appropriate, but Content-Type
+ is not). Responses to this method are not cachable.
+
+ If the Request-URI is an asterisk ("*"), the OPTIONS request is
+ intended to apply to the server as a whole. A 200 response SHOULD
+ include any header fields which indicate optional features
+ implemented by the server (e.g., Public), including any extensions
+ not defined by this specification, in addition to any applicable
+ general or response-header fields. As described in section 5.1.2, an
+ "OPTIONS *" request can be applied through a proxy by specifying the
+ destination server in the Request-URI without any path information.
+
+ If the Request-URI is not an asterisk, the OPTIONS request applies
+ only to the options that are available when communicating with that
+ resource. A 200 response SHOULD include any header fields which
+ indicate optional features implemented by the server and applicable
+ to that resource (e.g., Allow), including any extensions not defined
+ by this specification, in addition to any applicable general or
+ response-header fields. If the OPTIONS request passes through a
+ proxy, the proxy MUST edit the response to exclude those options
+ which apply to a proxy's capabilities and which are known to be
+ unavailable through that proxy.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 49]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+9.3 GET
+
+ The GET method means retrieve whatever information (in the form of an
+ entity) is identified by the Request-URI. If the Request-URI refers
+ to a data-producing process, it is the produced data which shall be
+ returned as the entity in the response and not the source text of the
+ process, unless that text happens to be the output of the process.
+
+ The semantics of the GET method change to a "conditional GET" if the
+ request message includes an If-Modified-Since, If-Unmodified-Since,
+ If-Match, If-None-Match, or If-Range header field. A conditional GET
+ method requests that the entity be transferred only under the
+ circumstances described by the conditional header field(s). The
+ conditional GET method is intended to reduce unnecessary network
+ usage by allowing cached entities to be refreshed without requiring
+ multiple requests or transferring data already held by the client.
+
+ The semantics of the GET method change to a "partial GET" if the
+ request message includes a Range header field. A partial GET requests
+ that only part of the entity be transferred, as described in section
+ 14.36. The partial GET method is intended to reduce unnecessary
+ network usage by allowing partially-retrieved entities to be
+ completed without transferring data already held by the client.
+
+ The response to a GET request is cachable if and only if it meets the
+ requirements for HTTP caching described in section 13.
+
+9.4 HEAD
+
+ The HEAD method is identical to GET except that the server MUST NOT
+ return a message-body in the response. The metainformation contained
+ in the HTTP headers in response to a HEAD request SHOULD be identical
+ to the information sent in response to a GET request. This method can
+ be used for obtaining metainformation about the entity implied by the
+ request without transferring the entity-body itself. This method is
+ often used for testing hypertext links for validity, accessibility,
+ and recent modification.
+
+ The response to a HEAD request may be cachable in the sense that the
+ information contained in the response may be used to update a
+ previously cached entity from that resource. If the new field values
+ indicate that the cached entity differs from the current entity (as
+ would be indicated by a change in Content-Length, Content-MD5, ETag
+ or Last-Modified), then the cache MUST treat the cache entry as
+ stale.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 50]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+9.5 POST
+
+ The POST method is used to request that the destination server accept
+ the entity enclosed in the request as a new subordinate of the
+ resource identified by the Request-URI in the Request-Line. POST is
+ designed to allow a uniform method to cover the following functions:
+
+ o Annotation of existing resources;
+
+ o Posting a message to a bulletin board, newsgroup, mailing list,
+ or similar group of articles;
+
+ o Providing a block of data, such as the result of submitting a
+ form, to a data-handling process;
+
+ o Extending a database through an append operation.
+
+ The actual function performed by the POST method is determined by the
+ server and is usually dependent on the Request-URI. The posted entity
+ is subordinate to that URI in the same way that a file is subordinate
+ to a directory containing it, a news article is subordinate to a
+ newsgroup to which it is posted, or a record is subordinate to a
+ database.
+
+ The action performed by the POST method might not result in a
+ resource that can be identified by a URI. In this case, either 200
+ (OK) or 204 (No Content) is the appropriate response status,
+ depending on whether or not the response includes an entity that
+ describes the result.
+
+ If a resource has been created on the origin server, the response
+ SHOULD be 201 (Created) and contain an entity which describes the
+ status of the request and refers to the new resource, and a Location
+ header (see section 14.30).
+
+ Responses to this method are not cachable, unless the response
+ includes appropriate Cache-Control or Expires header fields. However,
+ the 303 (See Other) response can be used to direct the user agent to
+ retrieve a cachable resource.
+
+ POST requests must obey the message transmission requirements set out
+ in section 8.2.
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 51]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+9.6 PUT
+
+ The PUT method requests that the enclosed entity be stored under the
+ supplied Request-URI. If the Request-URI refers to an already
+ existing resource, the enclosed entity SHOULD be considered as a
+ modified version of the one residing on the origin server. If the
+ Request-URI does not point to an existing resource, and that URI is
+ capable of being defined as a new resource by the requesting user
+ agent, the origin server can create the resource with that URI. If a
+ new resource is created, the origin server MUST inform the user agent
+ via the 201 (Created) response. If an existing resource is modified,
+ either the 200 (OK) or 204 (No Content) response codes SHOULD be sent
+ to indicate successful completion of the request. If the resource
+ could not be created or modified with the Request-URI, an appropriate
+ error response SHOULD be given that reflects the nature of the
+ problem. The recipient of the entity MUST NOT ignore any Content-*
+ (e.g. Content-Range) headers that it does not understand or implement
+ and MUST return a 501 (Not Implemented) response in such cases.
+
+ If the request passes through a cache and the Request-URI identifies
+ one or more currently cached entities, those entries should be
+ treated as stale. Responses to this method are not cachable.
+
+ The fundamental difference between the POST and PUT requests is
+ reflected in the different meaning of the Request-URI. The URI in a
+ POST request identifies the resource that will handle the enclosed
+ entity. That resource may be a data-accepting process, a gateway to
+ some other protocol, or a separate entity that accepts annotations.
+ In contrast, the URI in a PUT request identifies the entity enclosed
+ with the request -- the user agent knows what URI is intended and the
+ server MUST NOT attempt to apply the request to some other resource.
+ If the server desires that the request be applied to a different URI,
+ it MUST send a 301 (Moved Permanently) response; the user agent MAY
+ then make its own decision regarding whether or not to redirect the
+ request.
+
+ A single resource MAY be identified by many different URIs. For
+ example, an article may have a URI for identifying "the current
+ version" which is separate from the URI identifying each particular
+ version. In this case, a PUT request on a general URI may result in
+ several other URIs being defined by the origin server.
+
+ HTTP/1.1 does not define how a PUT method affects the state of an
+ origin server.
+
+ PUT requests must obey the message transmission requirements set out
+ in section 8.2.
+
+
+
+
+Fielding, et. al. Standards Track [Page 52]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+9.7 DELETE
+
+ The DELETE method requests that the origin server delete the resource
+ identified by the Request-URI. This method MAY be overridden by human
+ intervention (or other means) on the origin server. The client cannot
+ be guaranteed that the operation has been carried out, even if the
+ status code returned from the origin server indicates that the action
+ has been completed successfully. However, the server SHOULD not
+ indicate success unless, at the time the response is given, it
+ intends to delete the resource or move it to an inaccessible
+ location.
+
+ A successful response SHOULD be 200 (OK) if the response includes an
+ entity describing the status, 202 (Accepted) if the action has not
+ yet been enacted, or 204 (No Content) if the response is OK but does
+ not include an entity.
+
+ If the request passes through a cache and the Request-URI identifies
+ one or more currently cached entities, those entries should be
+ treated as stale. Responses to this method are not cachable.
+
+9.8 TRACE
+
+ The TRACE method is used to invoke a remote, application-layer loop-
+ back of the request message. The final recipient of the request
+ SHOULD reflect the message received back to the client as the
+ entity-body of a 200 (OK) response. The final recipient is either the
+ origin server or the first proxy or gateway to receive a Max-Forwards
+ value of zero (0) in the request (see section 14.31). A TRACE request
+ MUST NOT include an entity.
+
+ TRACE allows the client to see what is being received at the other
+ end of the request chain and use that data for testing or diagnostic
+ information. The value of the Via header field (section 14.44) is of
+ particular interest, since it acts as a trace of the request chain.
+ Use of the Max-Forwards header field allows the client to limit the
+ length of the request chain, which is useful for testing a chain of
+ proxies forwarding messages in an infinite loop.
+
+ If successful, the response SHOULD contain the entire request message
+ in the entity-body, with a Content-Type of "message/http". Responses
+ to this method MUST NOT be cached.
+
+10 Status Code Definitions
+
+ Each Status-Code is described below, including a description of which
+ method(s) it can follow and any metainformation required in the
+ response.
+
+
+
+Fielding, et. al. Standards Track [Page 53]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+10.1 Informational 1xx
+
+ This class of status code indicates a provisional response,
+ consisting only of the Status-Line and optional headers, and is
+ terminated by an empty line. Since HTTP/1.0 did not define any 1xx
+ status codes, servers MUST NOT send a 1xx response to an HTTP/1.0
+ client except under experimental conditions.
+
+10.1.1 100 Continue
+
+ The client may continue with its request. This interim response is
+ used to inform the client that the initial part of the request has
+ been received and has not yet been rejected by the server. The client
+ SHOULD continue by sending the remainder of the request or, if the
+ request has already been completed, ignore this response. The server
+ MUST send a final response after the request has been completed.
+
+10.1.2 101 Switching Protocols
+
+ The server understands and is willing to comply with the client's
+ request, via the Upgrade message header field (section 14.41), for a
+ change in the application protocol being used on this connection. The
+ server will switch protocols to those defined by the response's
+ Upgrade header field immediately after the empty line which
+ terminates the 101 response.
+
+ The protocol should only be switched when it is advantageous to do
+ so. For example, switching to a newer version of HTTP is
+ advantageous over older versions, and switching to a real-time,
+ synchronous protocol may be advantageous when delivering resources
+ that use such features.
+
+10.2 Successful 2xx
+
+ This class of status code indicates that the client's request was
+ successfully received, understood, and accepted.
+
+10.2.1 200 OK
+
+ The request has succeeded. The information returned with the response
+ is dependent on the method used in the request, for example:
+
+ GET an entity corresponding to the requested resource is sent in the
+ response;
+
+ HEAD the entity-header fields corresponding to the requested resource
+ are sent in the response without any message-body;
+
+
+
+
+Fielding, et. al. Standards Track [Page 54]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ POST an entity describing or containing the result of the action;
+
+ TRACE an entity containing the request message as received by the end
+ server.
+
+10.2.2 201 Created
+
+ The request has been fulfilled and resulted in a new resource being
+ created. The newly created resource can be referenced by the URI(s)
+ returned in the entity of the response, with the most specific URL
+ for the resource given by a Location header field. The origin server
+ MUST create the resource before returning the 201 status code. If the
+ action cannot be carried out immediately, the server should respond
+ with 202 (Accepted) response instead.
+
+10.2.3 202 Accepted
+
+ The request has been accepted for processing, but the processing has
+ not been completed. The request MAY or MAY NOT eventually be acted
+ upon, as it MAY be disallowed when processing actually takes place.
+ There is no facility for re-sending a status code from an
+ asynchronous operation such as this.
+
+ The 202 response is intentionally non-committal. Its purpose is to
+ allow a server to accept a request for some other process (perhaps a
+ batch-oriented process that is only run once per day) without
+ requiring that the user agent's connection to the server persist
+ until the process is completed. The entity returned with this
+ response SHOULD include an indication of the request's current status
+ and either a pointer to a status monitor or some estimate of when the
+ user can expect the request to be fulfilled.
+
+10.2.4 203 Non-Authoritative Information
+
+ The returned metainformation in the entity-header is not the
+ definitive set as available from the origin server, but is gathered
+ from a local or a third-party copy. The set presented MAY be a subset
+ or superset of the original version. For example, including local
+ annotation information about the resource MAY result in a superset of
+ the metainformation known by the origin server. Use of this response
+ code is not required and is only appropriate when the response would
+ otherwise be 200 (OK).
+
+10.2.5 204 No Content
+
+ The server has fulfilled the request but there is no new information
+ to send back. If the client is a user agent, it SHOULD NOT change its
+ document view from that which caused the request to be sent. This
+
+
+
+Fielding, et. al. Standards Track [Page 55]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ response is primarily intended to allow input for actions to take
+ place without causing a change to the user agent's active document
+ view. The response MAY include new metainformation in the form of
+ entity-headers, which SHOULD apply to the document currently in the
+ user agent's active view.
+
+ The 204 response MUST NOT include a message-body, and thus is always
+ terminated by the first empty line after the header fields.
+
+10.2.6 205 Reset Content
+
+ The server has fulfilled the request and the user agent SHOULD reset
+ the document view which caused the request to be sent. This response
+ is primarily intended to allow input for actions to take place via
+ user input, followed by a clearing of the form in which the input is
+ given so that the user can easily initiate another input action. The
+ response MUST NOT include an entity.
+
+10.2.7 206 Partial Content
+
+ The server has fulfilled the partial GET request for the resource.
+ The request must have included a Range header field (section 14.36)
+ indicating the desired range. The response MUST include either a
+ Content-Range header field (section 14.17) indicating the range
+ included with this response, or a multipart/byteranges Content-Type
+ including Content-Range fields for each part. If multipart/byteranges
+ is not used, the Content-Length header field in the response MUST
+ match the actual number of OCTETs transmitted in the message-body.
+
+ A cache that does not support the Range and Content-Range headers
+ MUST NOT cache 206 (Partial) responses.
+
+10.3 Redirection 3xx
+
+ This class of status code indicates that further action needs to be
+ taken by the user agent in order to fulfill the request. The action
+ required MAY be carried out by the user agent without interaction
+ with the user if and only if the method used in the second request is
+ GET or HEAD. A user agent SHOULD NOT automatically redirect a request
+ more than 5 times, since such redirections usually indicate an
+ infinite loop.
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 56]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+10.3.1 300 Multiple Choices
+
+ The requested resource corresponds to any one of a set of
+ representations, each with its own specific location, and agent-
+ driven negotiation information (section 12) is being provided so that
+ the user (or user agent) can select a preferred representation and
+ redirect its request to that location.
+
+ Unless it was a HEAD request, the response SHOULD include an entity
+ containing a list of resource characteristics and location(s) from
+ which the user or user agent can choose the one most appropriate. The
+ entity format is specified by the media type given in the Content-
+ Type header field. Depending upon the format and the capabilities of
+ the user agent, selection of the most appropriate choice may be
+ performed automatically. However, this specification does not define
+ any standard for such automatic selection.
+
+ If the server has a preferred choice of representation, it SHOULD
+ include the specific URL for that representation in the Location
+ field; user agents MAY use the Location field value for automatic
+ redirection. This response is cachable unless indicated otherwise.
+
+10.3.2 301 Moved Permanently
+
+ The requested resource has been assigned a new permanent URI and any
+ future references to this resource SHOULD be done using one of the
+ returned URIs. Clients with link editing capabilities SHOULD
+ automatically re-link references to the Request-URI to one or more of
+ the new references returned by the server, where possible. This
+ response is cachable unless indicated otherwise.
+
+ If the new URI is a location, its URL SHOULD be given by the Location
+ field in the response. Unless the request method was HEAD, the entity
+ of the response SHOULD contain a short hypertext note with a
+ hyperlink to the new URI(s).
+
+ If the 301 status code is received in response to a request other
+ than GET or HEAD, the user agent MUST NOT automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+ Note: When automatically redirecting a POST request after receiving
+ a 301 status code, some existing HTTP/1.0 user agents will
+ erroneously change it into a GET request.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 57]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+10.3.3 302 Moved Temporarily
+
+ The requested resource resides temporarily under a different URI.
+ Since the redirection may be altered on occasion, the client SHOULD
+ continue to use the Request-URI for future requests. This response is
+ only cachable if indicated by a Cache-Control or Expires header
+ field.
+
+ If the new URI is a location, its URL SHOULD be given by the Location
+ field in the response. Unless the request method was HEAD, the entity
+ of the response SHOULD contain a short hypertext note with a
+ hyperlink to the new URI(s).
+
+ If the 302 status code is received in response to a request other
+ than GET or HEAD, the user agent MUST NOT automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+ Note: When automatically redirecting a POST request after receiving
+ a 302 status code, some existing HTTP/1.0 user agents will
+ erroneously change it into a GET request.
+
+10.3.4 303 See Other
+
+ The response to the request can be found under a different URI and
+ SHOULD be retrieved using a GET method on that resource. This method
+ exists primarily to allow the output of a POST-activated script to
+ redirect the user agent to a selected resource. The new URI is not a
+ substitute reference for the originally requested resource. The 303
+ response is not cachable, but the response to the second (redirected)
+ request MAY be cachable.
+
+ If the new URI is a location, its URL SHOULD be given by the Location
+ field in the response. Unless the request method was HEAD, the entity
+ of the response SHOULD contain a short hypertext note with a
+ hyperlink to the new URI(s).
+
+10.3.5 304 Not Modified
+
+ If the client has performed a conditional GET request and access is
+ allowed, but the document has not been modified, the server SHOULD
+ respond with this status code. The response MUST NOT contain a
+ message-body.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 58]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The response MUST include the following header fields:
+
+ o Date
+
+ o ETag and/or Content-Location, if the header would have been sent in
+ a 200 response to the same request
+
+ o Expires, Cache-Control, and/or Vary, if the field-value might
+ differ from that sent in any previous response for the same variant
+
+ If the conditional GET used a strong cache validator (see section
+ 13.3.3), the response SHOULD NOT include other entity-headers.
+ Otherwise (i.e., the conditional GET used a weak validator), the
+ response MUST NOT include other entity-headers; this prevents
+ inconsistencies between cached entity-bodies and updated headers.
+
+ If a 304 response indicates an entity not currently cached, then the
+ cache MUST disregard the response and repeat the request without the
+ conditional.
+
+ If a cache uses a received 304 response to update a cache entry, the
+ cache MUST update the entry to reflect any new field values given in
+ the response.
+
+ The 304 response MUST NOT include a message-body, and thus is always
+ terminated by the first empty line after the header fields.
+
+10.3.6 305 Use Proxy
+
+ The requested resource MUST be accessed through the proxy given by
+ the Location field. The Location field gives the URL of the proxy.
+ The recipient is expected to repeat the request via the proxy.
+
+10.4 Client Error 4xx
+
+ The 4xx class of status code is intended for cases in which the
+ client seems to have erred. Except when responding to a HEAD request,
+ the server SHOULD include an entity containing an explanation of the
+ error situation, and whether it is a temporary or permanent
+ condition. These status codes are applicable to any request method.
+ User agents SHOULD display any included entity to the user.
+
+ Note: If the client is sending data, a server implementation using
+ TCP should be careful to ensure that the client acknowledges
+ receipt of the packet(s) containing the response, before the server
+ closes the input connection. If the client continues sending data
+ to the server after the close, the server's TCP stack will send a
+ reset packet to the client, which may erase the client's
+
+
+
+Fielding, et. al. Standards Track [Page 59]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ unacknowledged input buffers before they can be read and
+ interpreted by the HTTP application.
+
+10.4.1 400 Bad Request
+
+ The request could not be understood by the server due to malformed
+ syntax. The client SHOULD NOT repeat the request without
+ modifications.
+
+10.4.2 401 Unauthorized
+
+ The request requires user authentication. The response MUST include a
+ WWW-Authenticate header field (section 14.46) containing a challenge
+ applicable to the requested resource. The client MAY repeat the
+ request with a suitable Authorization header field (section 14.8). If
+ the request already included Authorization credentials, then the 401
+ response indicates that authorization has been refused for those
+ credentials. If the 401 response contains the same challenge as the
+ prior response, and the user agent has already attempted
+ authentication at least once, then the user SHOULD be presented the
+ entity that was given in the response, since that entity MAY include
+ relevant diagnostic information. HTTP access authentication is
+ explained in section 11.
+
+10.4.3 402 Payment Required
+
+ This code is reserved for future use.
+
+10.4.4 403 Forbidden
+
+ The server understood the request, but is refusing to fulfill it.
+ Authorization will not help and the request SHOULD NOT be repeated.
+ If the request method was not HEAD and the server wishes to make
+ public why the request has not been fulfilled, it SHOULD describe the
+ reason for the refusal in the entity. This status code is commonly
+ used when the server does not wish to reveal exactly why the request
+ has been refused, or when no other response is applicable.
+
+10.4.5 404 Not Found
+
+ The server has not found anything matching the Request-URI. No
+ indication is given of whether the condition is temporary or
+ permanent.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 60]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ If the server does not wish to make this information available to the
+ client, the status code 403 (Forbidden) can be used instead. The 410
+ (Gone) status code SHOULD be used if the server knows, through some
+ internally configurable mechanism, that an old resource is
+ permanently unavailable and has no forwarding address.
+
+10.4.6 405 Method Not Allowed
+
+ The method specified in the Request-Line is not allowed for the
+ resource identified by the Request-URI. The response MUST include an
+ Allow header containing a list of valid methods for the requested
+ resource.
+
+10.4.7 406 Not Acceptable
+
+ The resource identified by the request is only capable of generating
+ response entities which have content characteristics not acceptable
+ according to the accept headers sent in the request.
+
+ Unless it was a HEAD request, the response SHOULD include an entity
+ containing a list of available entity characteristics and location(s)
+ from which the user or user agent can choose the one most
+ appropriate. The entity format is specified by the media type given
+ in the Content-Type header field. Depending upon the format and the
+ capabilities of the user agent, selection of the most appropriate
+ choice may be performed automatically. However, this specification
+ does not define any standard for such automatic selection.
+
+ Note: HTTP/1.1 servers are allowed to return responses which are
+ not acceptable according to the accept headers sent in the request.
+ In some cases, this may even be preferable to sending a 406
+ response. User agents are encouraged to inspect the headers of an
+ incoming response to determine if it is acceptable. If the response
+ could be unacceptable, a user agent SHOULD temporarily stop receipt
+ of more data and query the user for a decision on further actions.
+
+10.4.8 407 Proxy Authentication Required
+
+ This code is similar to 401 (Unauthorized), but indicates that the
+ client MUST first authenticate itself with the proxy. The proxy MUST
+ return a Proxy-Authenticate header field (section 14.33) containing a
+ challenge applicable to the proxy for the requested resource. The
+ client MAY repeat the request with a suitable Proxy-Authorization
+ header field (section 14.34). HTTP access authentication is explained
+ in section 11.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 61]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+10.4.9 408 Request Timeout
+
+ The client did not produce a request within the time that the server
+ was prepared to wait. The client MAY repeat the request without
+ modifications at any later time.
+
+10.4.10 409 Conflict
+
+ The request could not be completed due to a conflict with the current
+ state of the resource. This code is only allowed in situations where
+ it is expected that the user might be able to resolve the conflict
+ and resubmit the request. The response body SHOULD include enough
+ information for the user to recognize the source of the conflict.
+ Ideally, the response entity would include enough information for the
+ user or user agent to fix the problem; however, that may not be
+ possible and is not required.
+
+ Conflicts are most likely to occur in response to a PUT request. If
+ versioning is being used and the entity being PUT includes changes to
+ a resource which conflict with those made by an earlier (third-party)
+ request, the server MAY use the 409 response to indicate that it
+ can't complete the request. In this case, the response entity SHOULD
+ contain a list of the differences between the two versions in a
+ format defined by the response Content-Type.
+
+10.4.11 410 Gone
+
+ The requested resource is no longer available at the server and no
+ forwarding address is known. This condition SHOULD be considered
+ permanent. Clients with link editing capabilities SHOULD delete
+ references to the Request-URI after user approval. If the server does
+ not know, or has no facility to determine, whether or not the
+ condition is permanent, the status code 404 (Not Found) SHOULD be
+ used instead. This response is cachable unless indicated otherwise.
+
+ The 410 response is primarily intended to assist the task of web
+ maintenance by notifying the recipient that the resource is
+ intentionally unavailable and that the server owners desire that
+ remote links to that resource be removed. Such an event is common for
+ limited-time, promotional services and for resources belonging to
+ individuals no longer working at the server's site. It is not
+ necessary to mark all permanently unavailable resources as "gone" or
+ to keep the mark for any length of time -- that is left to the
+ discretion of the server owner.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 62]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+10.4.12 411 Length Required
+
+ The server refuses to accept the request without a defined Content-
+ Length. The client MAY repeat the request if it adds a valid
+ Content-Length header field containing the length of the message-body
+ in the request message.
+
+10.4.13 412 Precondition Failed
+
+ The precondition given in one or more of the request-header fields
+ evaluated to false when it was tested on the server. This response
+ code allows the client to place preconditions on the current resource
+ metainformation (header field data) and thus prevent the requested
+ method from being applied to a resource other than the one intended.
+
+10.4.14 413 Request Entity Too Large
+
+ The server is refusing to process a request because the request
+ entity is larger than the server is willing or able to process. The
+ server may close the connection to prevent the client from continuing
+ the request.
+
+ If the condition is temporary, the server SHOULD include a Retry-
+ After header field to indicate that it is temporary and after what
+ time the client may try again.
+
+10.4.15 414 Request-URI Too Long
+
+ The server is refusing to service the request because the Request-URI
+ is longer than the server is willing to interpret. This rare
+ condition is only likely to occur when a client has improperly
+ converted a POST request to a GET request with long query
+ information, when the client has descended into a URL "black hole" of
+ redirection (e.g., a redirected URL prefix that points to a suffix of
+ itself), or when the server is under attack by a client attempting to
+ exploit security holes present in some servers using fixed-length
+ buffers for reading or manipulating the Request-URI.
+
+10.4.16 415 Unsupported Media Type
+
+ The server is refusing to service the request because the entity of
+ the request is in a format not supported by the requested resource
+ for the requested method.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 63]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+10.5 Server Error 5xx
+
+ Response status codes beginning with the digit "5" indicate cases in
+ which the server is aware that it has erred or is incapable of
+ performing the request. Except when responding to a HEAD request, the
+ server SHOULD include an entity containing an explanation of the
+ error situation, and whether it is a temporary or permanent
+ condition. User agents SHOULD display any included entity to the
+ user. These response codes are applicable to any request method.
+
+10.5.1 500 Internal Server Error
+
+ The server encountered an unexpected condition which prevented it
+ from fulfilling the request.
+
+10.5.2 501 Not Implemented
+
+ The server does not support the functionality required to fulfill the
+ request. This is the appropriate response when the server does not
+ recognize the request method and is not capable of supporting it for
+ any resource.
+
+10.5.3 502 Bad Gateway
+
+ The server, while acting as a gateway or proxy, received an invalid
+ response from the upstream server it accessed in attempting to
+ fulfill the request.
+
+10.5.4 503 Service Unavailable
+
+ The server is currently unable to handle the request due to a
+ temporary overloading or maintenance of the server. The implication
+ is that this is a temporary condition which will be alleviated after
+ some delay. If known, the length of the delay may be indicated in a
+ Retry-After header. If no Retry-After is given, the client SHOULD
+ handle the response as it would for a 500 response.
+
+ Note: The existence of the 503 status code does not imply that a
+ server must use it when becoming overloaded. Some servers may wish
+ to simply refuse the connection.
+
+10.5.5 504 Gateway Timeout
+
+ The server, while acting as a gateway or proxy, did not receive a
+ timely response from the upstream server it accessed in attempting to
+ complete the request.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 64]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+10.5.6 505 HTTP Version Not Supported
+
+ The server does not support, or refuses to support, the HTTP protocol
+ version that was used in the request message. The server is
+ indicating that it is unable or unwilling to complete the request
+ using the same major version as the client, as described in section
+ 3.1, other than with this error message. The response SHOULD contain
+ an entity describing why that version is not supported and what other
+ protocols are supported by that server.
+
+11 Access Authentication
+
+ HTTP provides a simple challenge-response authentication mechanism
+ which MAY be used by a server to challenge a client request and by a
+ client to provide authentication information. It uses an extensible,
+ case-insensitive token to identify the authentication scheme,
+ followed by a comma-separated list of attribute-value pairs which
+ carry the parameters necessary for achieving authentication via that
+ scheme.
+
+ auth-scheme = token
+
+ auth-param = token "=" quoted-string
+
+ The 401 (Unauthorized) response message is used by an origin server
+ to challenge the authorization of a user agent. This response MUST
+ include a WWW-Authenticate header field containing at least one
+ challenge applicable to the requested resource.
+
+ challenge = auth-scheme 1*SP realm *( "," auth-param )
+
+ realm = "realm" "=" realm-value
+ realm-value = quoted-string
+
+ The realm attribute (case-insensitive) is required for all
+ authentication schemes which issue a challenge. The realm value
+ (case-sensitive), in combination with the canonical root URL (see
+ section 5.1.2) of the server being accessed, defines the protection
+ space. These realms allow the protected resources on a server to be
+ partitioned into a set of protection spaces, each with its own
+ authentication scheme and/or authorization database. The realm value
+ is a string, generally assigned by the origin server, which may have
+ additional semantics specific to the authentication scheme.
+
+ A user agent that wishes to authenticate itself with a server--
+ usually, but not necessarily, after receiving a 401 or 411 response-
+ -MAY do so by including an Authorization header field with the
+ request. The Authorization field value consists of credentials
+
+
+
+Fielding, et. al. Standards Track [Page 65]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ containing the authentication information of the user agent for the
+ realm of the resource being requested.
+
+ credentials = basic-credentials
+ | auth-scheme #auth-param
+
+ The domain over which credentials can be automatically applied by a
+ user agent is determined by the protection space. If a prior request
+ has been authorized, the same credentials MAY be reused for all other
+ requests within that protection space for a period of time determined
+ by the authentication scheme, parameters, and/or user preference.
+ Unless otherwise defined by the authentication scheme, a single
+ protection space cannot extend outside the scope of its server.
+
+ If the server does not wish to accept the credentials sent with a
+ request, it SHOULD return a 401 (Unauthorized) response. The response
+ MUST include a WWW-Authenticate header field containing the (possibly
+ new) challenge applicable to the requested resource and an entity
+ explaining the refusal.
+
+ The HTTP protocol does not restrict applications to this simple
+ challenge-response mechanism for access authentication. Additional
+ mechanisms MAY be used, such as encryption at the transport level or
+ via message encapsulation, and with additional header fields
+ specifying authentication information. However, these additional
+ mechanisms are not defined by this specification.
+
+ Proxies MUST be completely transparent regarding user agent
+ authentication. That is, they MUST forward the WWW-Authenticate and
+ Authorization headers untouched, and follow the rules found in
+ section 14.8.
+
+ HTTP/1.1 allows a client to pass authentication information to and
+ from a proxy via the Proxy-Authenticate and Proxy-Authorization
+ headers.
+
+11.1 Basic Authentication Scheme
+
+ The "basic" authentication scheme is based on the model that the user
+ agent must authenticate itself with a user-ID and a password for each
+ realm. The realm value should be considered an opaque string which
+ can only be compared for equality with other realms on that server.
+ The server will service the request only if it can validate the
+ user-ID and password for the protection space of the Request-URI.
+ There are no optional authentication parameters.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 66]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Upon receipt of an unauthorized request for a URI within the
+ protection space, the server MAY respond with a challenge like the
+ following:
+
+ WWW-Authenticate: Basic realm="WallyWorld"
+
+ where "WallyWorld" is the string assigned by the server to identify
+ the protection space of the Request-URI.
+
+ To receive authorization, the client sends the userid and password,
+ separated by a single colon (":") character, within a base64 encoded
+ string in the credentials.
+
+ basic-credentials = "Basic" SP basic-cookie
+
+ basic-cookie = <base64 [7] encoding of user-pass,
+ except not limited to 76 char/line>
+
+ user-pass = userid ":" password
+
+ userid = *<TEXT excluding ":">
+
+ password = *TEXT
+
+ Userids might be case sensitive.
+
+ If the user agent wishes to send the userid "Aladdin" and password
+ "open sesame", it would use the following header field:
+
+ Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+
+ See section 15 for security considerations associated with Basic
+ authentication.
+
+11.2 Digest Authentication Scheme
+
+ A digest authentication for HTTP is specified in RFC 2069 [32].
+
+12 Content Negotiation
+
+ Most HTTP responses include an entity which contains information for
+ interpretation by a human user. Naturally, it is desirable to supply
+ the user with the "best available" entity corresponding to the
+ request. Unfortunately for servers and caches, not all users have
+ the same preferences for what is "best," and not all user agents are
+ equally capable of rendering all entity types. For that reason, HTTP
+ has provisions for several mechanisms for "content negotiation" --
+ the process of selecting the best representation for a given response
+
+
+
+Fielding, et. al. Standards Track [Page 67]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ when there are multiple representations available.
+
+ Note: This is not called "format negotiation" because the alternate
+ representations may be of the same media type, but use different
+ capabilities of that type, be in different languages, etc.
+
+ Any response containing an entity-body MAY be subject to negotiation,
+ including error responses.
+
+ There are two kinds of content negotiation which are possible in
+ HTTP: server-driven and agent-driven negotiation. These two kinds of
+ negotiation are orthogonal and thus may be used separately or in
+ combination. One method of combination, referred to as transparent
+ negotiation, occurs when a cache uses the agent-driven negotiation
+ information provided by the origin server in order to provide
+ server-driven negotiation for subsequent requests.
+
+12.1 Server-driven Negotiation
+
+ If the selection of the best representation for a response is made by
+ an algorithm located at the server, it is called server-driven
+ negotiation. Selection is based on the available representations of
+ the response (the dimensions over which it can vary; e.g. language,
+ content-coding, etc.) and the contents of particular header fields in
+ the request message or on other information pertaining to the request
+ (such as the network address of the client).
+
+ Server-driven negotiation is advantageous when the algorithm for
+ selecting from among the available representations is difficult to
+ describe to the user agent, or when the server desires to send its
+ "best guess" to the client along with the first response (hoping to
+ avoid the round-trip delay of a subsequent request if the "best
+ guess" is good enough for the user). In order to improve the server's
+ guess, the user agent MAY include request header fields (Accept,
+ Accept-Language, Accept-Encoding, etc.) which describe its
+ preferences for such a response.
+
+ Server-driven negotiation has disadvantages:
+
+1. It is impossible for the server to accurately determine what might be
+ "best" for any given user, since that would require complete
+ knowledge of both the capabilities of the user agent and the intended
+ use for the response (e.g., does the user want to view it on screen
+ or print it on paper?).
+
+2. Having the user agent describe its capabilities in every request can
+ be both very inefficient (given that only a small percentage of
+ responses have multiple representations) and a potential violation of
+
+
+
+Fielding, et. al. Standards Track [Page 68]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ the user's privacy.
+
+3. It complicates the implementation of an origin server and the
+ algorithms for generating responses to a request.
+
+4. It may limit a public cache's ability to use the same response for
+ multiple user's requests.
+
+ HTTP/1.1 includes the following request-header fields for enabling
+ server-driven negotiation through description of user agent
+ capabilities and user preferences: Accept (section 14.1), Accept-
+ Charset (section 14.2), Accept-Encoding (section 14.3), Accept-
+ Language (section 14.4), and User-Agent (section 14.42). However, an
+ origin server is not limited to these dimensions and MAY vary the
+ response based on any aspect of the request, including information
+ outside the request-header fields or within extension header fields
+ not defined by this specification.
+
+ HTTP/1.1 origin servers MUST include an appropriate Vary header field
+ (section 14.43) in any cachable response based on server-driven
+ negotiation. The Vary header field describes the dimensions over
+ which the response might vary (i.e. the dimensions over which the
+ origin server picks its "best guess" response from multiple
+ representations).
+
+ HTTP/1.1 public caches MUST recognize the Vary header field when it
+ is included in a response and obey the requirements described in
+ section 13.6 that describes the interactions between caching and
+ content negotiation.
+
+12.2 Agent-driven Negotiation
+
+ With agent-driven negotiation, selection of the best representation
+ for a response is performed by the user agent after receiving an
+ initial response from the origin server. Selection is based on a list
+ of the available representations of the response included within the
+ header fields (this specification reserves the field-name Alternates,
+ as described in appendix 19.6.2.1) or entity-body of the initial
+ response, with each representation identified by its own URI.
+ Selection from among the representations may be performed
+ automatically (if the user agent is capable of doing so) or manually
+ by the user selecting from a generated (possibly hypertext) menu.
+
+ Agent-driven negotiation is advantageous when the response would vary
+ over commonly-used dimensions (such as type, language, or encoding),
+ when the origin server is unable to determine a user agent's
+ capabilities from examining the request, and generally when public
+ caches are used to distribute server load and reduce network usage.
+
+
+
+Fielding, et. al. Standards Track [Page 69]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Agent-driven negotiation suffers from the disadvantage of needing a
+ second request to obtain the best alternate representation. This
+ second request is only efficient when caching is used. In addition,
+ this specification does not define any mechanism for supporting
+ automatic selection, though it also does not prevent any such
+ mechanism from being developed as an extension and used within
+ HTTP/1.1.
+
+ HTTP/1.1 defines the 300 (Multiple Choices) and 406 (Not Acceptable)
+ status codes for enabling agent-driven negotiation when the server is
+ unwilling or unable to provide a varying response using server-driven
+ negotiation.
+
+12.3 Transparent Negotiation
+
+ Transparent negotiation is a combination of both server-driven and
+ agent-driven negotiation. When a cache is supplied with a form of the
+ list of available representations of the response (as in agent-driven
+ negotiation) and the dimensions of variance are completely understood
+ by the cache, then the cache becomes capable of performing server-
+ driven negotiation on behalf of the origin server for subsequent
+ requests on that resource.
+
+ Transparent negotiation has the advantage of distributing the
+ negotiation work that would otherwise be required of the origin
+ server and also removing the second request delay of agent-driven
+ negotiation when the cache is able to correctly guess the right
+ response.
+
+ This specification does not define any mechanism for transparent
+ negotiation, though it also does not prevent any such mechanism from
+ being developed as an extension and used within HTTP/1.1. An HTTP/1.1
+ cache performing transparent negotiation MUST include a Vary header
+ field in the response (defining the dimensions of its variance) if it
+ is cachable to ensure correct interoperation with all HTTP/1.1
+ clients. The agent-driven negotiation information supplied by the
+ origin server SHOULD be included with the transparently negotiated
+ response.
+
+13 Caching in HTTP
+
+ HTTP is typically used for distributed information systems, where
+ performance can be improved by the use of response caches. The
+ HTTP/1.1 protocol includes a number of elements intended to make
+ caching work as well as possible. Because these elements are
+ inextricable from other aspects of the protocol, and because they
+ interact with each other, it is useful to describe the basic caching
+ design of HTTP separately from the detailed descriptions of methods,
+
+
+
+Fielding, et. al. Standards Track [Page 70]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ headers, response codes, etc.
+
+ Caching would be useless if it did not significantly improve
+ performance. The goal of caching in HTTP/1.1 is to eliminate the need
+ to send requests in many cases, and to eliminate the need to send
+ full responses in many other cases. The former reduces the number of
+ network round-trips required for many operations; we use an
+ "expiration" mechanism for this purpose (see section 13.2). The
+ latter reduces network bandwidth requirements; we use a "validation"
+ mechanism for this purpose (see section 13.3).
+
+ Requirements for performance, availability, and disconnected
+ operation require us to be able to relax the goal of semantic
+ transparency. The HTTP/1.1 protocol allows origin servers, caches,
+ and clients to explicitly reduce transparency when necessary.
+ However, because non-transparent operation may confuse non-expert
+ users, and may be incompatible with certain server applications (such
+ as those for ordering merchandise), the protocol requires that
+ transparency be relaxed
+
+ o only by an explicit protocol-level request when relaxed by client
+ or origin server
+
+ o only with an explicit warning to the end user when relaxed by cache
+ or client
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 71]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Therefore, the HTTP/1.1 protocol provides these important elements:
+
+ 1. Protocol features that provide full semantic transparency when this
+ is required by all parties.
+
+ 2. Protocol features that allow an origin server or user agent to
+ explicitly request and control non-transparent operation.
+
+ 3. Protocol features that allow a cache to attach warnings to
+ responses that do not preserve the requested approximation of
+ semantic transparency.
+
+ A basic principle is that it must be possible for the clients to
+ detect any potential relaxation of semantic transparency.
+
+ Note: The server, cache, or client implementer may be faced with
+ design decisions not explicitly discussed in this specification. If
+ a decision may affect semantic transparency, the implementer ought
+ to err on the side of maintaining transparency unless a careful and
+ complete analysis shows significant benefits in breaking
+ transparency.
+
+13.1.1 Cache Correctness
+
+ A correct cache MUST respond to a request with the most up-to-date
+ response held by the cache that is appropriate to the request (see
+ sections 13.2.5, 13.2.6, and 13.12) which meets one of the following
+ conditions:
+
+ 1. It has been checked for equivalence with what the origin server
+ would have returned by revalidating the response with the origin
+ server (section 13.3);
+
+ 2. It is "fresh enough" (see section 13.2). In the default case, this
+ means it meets the least restrictive freshness requirement of the
+ client, server, and cache (see section 14.9); if the origin server
+ so specifies, it is the freshness requirement of the origin server
+ alone.
+
+ 3. It includes a warning if the freshness demand of the client or the
+ origin server is violated (see section 13.1.5 and 14.45).
+
+ 4. It is an appropriate 304 (Not Modified), 305 (Proxy Redirect), or
+ error (4xx or 5xx) response message.
+
+ If the cache can not communicate with the origin server, then a
+ correct cache SHOULD respond as above if the response can be
+ correctly served from the cache; if not it MUST return an error or
+
+
+
+Fielding, et. al. Standards Track [Page 72]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ warning indicating that there was a communication failure.
+
+ If a cache receives a response (either an entire response, or a 304
+ (Not Modified) response) that it would normally forward to the
+ requesting client, and the received response is no longer fresh, the
+ cache SHOULD forward it to the requesting client without adding a new
+ Warning (but without removing any existing Warning headers). A cache
+ SHOULD NOT attempt to revalidate a response simply because that
+ response became stale in transit; this might lead to an infinite
+ loop. An user agent that receives a stale response without a Warning
+ MAY display a warning indication to the user.
+
+13.1.2 Warnings
+
+ Whenever a cache returns a response that is neither first-hand nor
+ "fresh enough" (in the sense of condition 2 in section 13.1.1), it
+ must attach a warning to that effect, using a Warning response-
+ header. This warning allows clients to take appropriate action.
+
+ Warnings may be used for other purposes, both cache-related and
+ otherwise. The use of a warning, rather than an error status code,
+ distinguish these responses from true failures.
+
+ Warnings are always cachable, because they never weaken the
+ transparency of a response. This means that warnings can be passed to
+ HTTP/1.0 caches without danger; such caches will simply pass the
+ warning along as an entity-header in the response.
+
+ Warnings are assigned numbers between 0 and 99. This specification
+ defines the code numbers and meanings of each currently assigned
+ warnings, allowing a client or cache to take automated action in some
+ (but not all) cases.
+
+ Warnings also carry a warning text. The text may be in any
+ appropriate natural language (perhaps based on the client's Accept
+ headers), and include an optional indication of what character set is
+ used.
+
+ Multiple warnings may be attached to a response (either by the origin
+ server or by a cache), including multiple warnings with the same code
+ number. For example, a server may provide the same warning with texts
+ in both English and Basque.
+
+ When multiple warnings are attached to a response, it may not be
+ practical or reasonable to display all of them to the user. This
+ version of HTTP does not specify strict priority rules for deciding
+ which warnings to display and in what order, but does suggest some
+ heuristics.
+
+
+
+Fielding, et. al. Standards Track [Page 73]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The Warning header and the currently defined warnings are described
+ in section 14.45.
+
+13.1.3 Cache-control Mechanisms
+
+ The basic cache mechanisms in HTTP/1.1 (server-specified expiration
+ times and validators) are implicit directives to caches. In some
+ cases, a server or client may need to provide explicit directives to
+ the HTTP caches. We use the Cache-Control header for this purpose.
+
+ The Cache-Control header allows a client or server to transmit a
+ variety of directives in either requests or responses. These
+ directives typically override the default caching algorithms. As a
+ general rule, if there is any apparent conflict between header
+ values, the most restrictive interpretation should be applied (that
+ is, the one that is most likely to preserve semantic transparency).
+ However, in some cases, Cache-Control directives are explicitly
+ specified as weakening the approximation of semantic transparency
+ (for example, "max-stale" or "public").
+
+ The Cache-Control directives are described in detail in section 14.9.
+
+13.1.4 Explicit User Agent Warnings
+
+ Many user agents make it possible for users to override the basic
+ caching mechanisms. For example, the user agent may allow the user to
+ specify that cached entities (even explicitly stale ones) are never
+ validated. Or the user agent might habitually add "Cache-Control:
+ max-stale=3600" to every request. The user should have to explicitly
+ request either non-transparent behavior, or behavior that results in
+ abnormally ineffective caching.
+
+ If the user has overridden the basic caching mechanisms, the user
+ agent should explicitly indicate to the user whenever this results in
+ the display of information that might not meet the server's
+ transparency requirements (in particular, if the displayed entity is
+ known to be stale). Since the protocol normally allows the user agent
+ to determine if responses are stale or not, this indication need only
+ be displayed when this actually happens. The indication need not be a
+ dialog box; it could be an icon (for example, a picture of a rotting
+ fish) or some other visual indicator.
+
+ If the user has overridden the caching mechanisms in a way that would
+ abnormally reduce the effectiveness of caches, the user agent should
+ continually display an indication (for example, a picture of currency
+ in flames) so that the user does not inadvertently consume excess
+ resources or suffer from excessive latency.
+
+
+
+
+Fielding, et. al. Standards Track [Page 74]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+13.1.5 Exceptions to the Rules and Warnings
+
+ In some cases, the operator of a cache may choose to configure it to
+ return stale responses even when not requested by clients. This
+ decision should not be made lightly, but may be necessary for reasons
+ of availability or performance, especially when the cache is poorly
+ connected to the origin server. Whenever a cache returns a stale
+ response, it MUST mark it as such (using a Warning header). This
+ allows the client software to alert the user that there may be a
+ potential problem.
+
+ It also allows the user agent to take steps to obtain a first-hand or
+ fresh response. For this reason, a cache SHOULD NOT return a stale
+ response if the client explicitly requests a first-hand or fresh one,
+ unless it is impossible to comply for technical or policy reasons.
+
+13.1.6 Client-controlled Behavior
+
+ While the origin server (and to a lesser extent, intermediate caches,
+ by their contribution to the age of a response) are the primary
+ source of expiration information, in some cases the client may need
+ to control a cache's decision about whether to return a cached
+ response without validating it. Clients do this using several
+ directives of the Cache-Control header.
+
+ A client's request may specify the maximum age it is willing to
+ accept of an unvalidated response; specifying a value of zero forces
+ the cache(s) to revalidate all responses. A client may also specify
+ the minimum time remaining before a response expires. Both of these
+ options increase constraints on the behavior of caches, and so cannot
+ further relax the cache's approximation of semantic transparency.
+
+ A client may also specify that it will accept stale responses, up to
+ some maximum amount of staleness. This loosens the constraints on the
+ caches, and so may violate the origin server's specified constraints
+ on semantic transparency, but may be necessary to support
+ disconnected operation, or high availability in the face of poor
+ connectivity.
+
+13.2 Expiration Model
+
+13.2.1 Server-Specified Expiration
+
+ HTTP caching works best when caches can entirely avoid making
+ requests to the origin server. The primary mechanism for avoiding
+ requests is for an origin server to provide an explicit expiration
+ time in the future, indicating that a response may be used to satisfy
+ subsequent requests. In other words, a cache can return a fresh
+
+
+
+Fielding, et. al. Standards Track [Page 75]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ response without first contacting the server.
+
+ Our expectation is that servers will assign future explicit
+ expiration times to responses in the belief that the entity is not
+ likely to change, in a semantically significant way, before the
+ expiration time is reached. This normally preserves semantic
+ transparency, as long as the server's expiration times are carefully
+ chosen.
+
+ The expiration mechanism applies only to responses taken from a cache
+ and not to first-hand responses forwarded immediately to the
+ requesting client.
+
+ If an origin server wishes to force a semantically transparent cache
+ to validate every request, it may assign an explicit expiration time
+ in the past. This means that the response is always stale, and so the
+ cache SHOULD validate it before using it for subsequent requests. See
+ section 14.9.4 for a more restrictive way to force revalidation.
+
+ If an origin server wishes to force any HTTP/1.1 cache, no matter how
+ it is configured, to validate every request, it should use the
+ "must-revalidate" Cache-Control directive (see section 14.9).
+
+ Servers specify explicit expiration times using either the Expires
+ header, or the max-age directive of the Cache-Control header.
+
+ An expiration time cannot be used to force a user agent to refresh
+ its display or reload a resource; its semantics apply only to caching
+ mechanisms, and such mechanisms need only check a resource's
+ expiration status when a new request for that resource is initiated.
+ See section 13.13 for explanation of the difference between caches
+ and history mechanisms.
+
+13.2.2 Heuristic Expiration
+
+ Since origin servers do not always provide explicit expiration times,
+ HTTP caches typically assign heuristic expiration times, employing
+ algorithms that use other header values (such as the Last-Modified
+ time) to estimate a plausible expiration time. The HTTP/1.1
+ specification does not provide specific algorithms, but does impose
+ worst-case constraints on their results. Since heuristic expiration
+ times may compromise semantic transparency, they should be used
+ cautiously, and we encourage origin servers to provide explicit
+ expiration times as much as possible.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 76]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+13.2.3 Age Calculations
+
+ In order to know if a cached entry is fresh, a cache needs to know if
+ its age exceeds its freshness lifetime. We discuss how to calculate
+ the latter in section 13.2.4; this section describes how to calculate
+ the age of a response or cache entry.
+
+ In this discussion, we use the term "now" to mean "the current value
+ of the clock at the host performing the calculation." Hosts that use
+ HTTP, but especially hosts running origin servers and caches, should
+ use NTP [28] or some similar protocol to synchronize their clocks to
+ a globally accurate time standard.
+
+ Also note that HTTP/1.1 requires origin servers to send a Date header
+ with every response, giving the time at which the response was
+ generated. We use the term "date_value" to denote the value of the
+ Date header, in a form appropriate for arithmetic operations.
+
+ HTTP/1.1 uses the Age response-header to help convey age information
+ between caches. The Age header value is the sender's estimate of the
+ amount of time since the response was generated at the origin server.
+ In the case of a cached response that has been revalidated with the
+ origin server, the Age value is based on the time of revalidation,
+ not of the original response.
+
+ In essence, the Age value is the sum of the time that the response
+ has been resident in each of the caches along the path from the
+ origin server, plus the amount of time it has been in transit along
+ network paths.
+
+ We use the term "age_value" to denote the value of the Age header, in
+ a form appropriate for arithmetic operations.
+
+ A response's age can be calculated in two entirely independent ways:
+
+ 1. now minus date_value, if the local clock is reasonably well
+ synchronized to the origin server's clock. If the result is
+ negative, the result is replaced by zero.
+
+ 2. age_value, if all of the caches along the response path
+ implement HTTP/1.1.
+
+ Given that we have two independent ways to compute the age of a
+ response when it is received, we can combine these as
+
+ corrected_received_age = max(now - date_value, age_value)
+
+ and as long as we have either nearly synchronized clocks or all-
+
+
+
+Fielding, et. al. Standards Track [Page 77]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ HTTP/1.1 paths, one gets a reliable (conservative) result.
+
+ Note that this correction is applied at each HTTP/1.1 cache along the
+ path, so that if there is an HTTP/1.0 cache in the path, the correct
+ received age is computed as long as the receiving cache's clock is
+ nearly in sync. We don't need end-to-end clock synchronization
+ (although it is good to have), and there is no explicit clock
+ synchronization step.
+
+ Because of network-imposed delays, some significant interval may pass
+ from the time that a server generates a response and the time it is
+ received at the next outbound cache or client. If uncorrected, this
+ delay could result in improperly low ages.
+
+ Because the request that resulted in the returned Age value must have
+ been initiated prior to that Age value's generation, we can correct
+ for delays imposed by the network by recording the time at which the
+ request was initiated. Then, when an Age value is received, it MUST
+ be interpreted relative to the time the request was initiated, not
+ the time that the response was received. This algorithm results in
+ conservative behavior no matter how much delay is experienced. So, we
+ compute:
+
+ corrected_initial_age = corrected_received_age
+ + (now - request_time)
+
+ where "request_time" is the time (according to the local clock) when
+ the request that elicited this response was sent.
+
+ Summary of age calculation algorithm, when a cache receives a
+ response:
+
+ /*
+ * age_value
+ * is the value of Age: header received by the cache with
+ * this response.
+ * date_value
+ * is the value of the origin server's Date: header
+ * request_time
+ * is the (local) time when the cache made the request
+ * that resulted in this cached response
+ * response_time
+ * is the (local) time when the cache received the
+ * response
+ * now
+ * is the current (local) time
+ */
+ apparent_age = max(0, response_time - date_value);
+
+
+
+Fielding, et. al. Standards Track [Page 78]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ corrected_received_age = max(apparent_age, age_value);
+ response_delay = response_time - request_time;
+ corrected_initial_age = corrected_received_age + response_delay;
+ resident_time = now - response_time;
+ current_age = corrected_initial_age + resident_time;
+
+ When a cache sends a response, it must add to the
+ corrected_initial_age the amount of time that the response was
+ resident locally. It must then transmit this total age, using the Age
+ header, to the next recipient cache.
+
+ Note that a client cannot reliably tell that a response is first-
+ hand, but the presence of an Age header indicates that a response
+ is definitely not first-hand. Also, if the Date in a response is
+ earlier than the client's local request time, the response is
+ probably not first-hand (in the absence of serious clock skew).
+
+13.2.4 Expiration Calculations
+
+ In order to decide whether a response is fresh or stale, we need to
+ compare its freshness lifetime to its age. The age is calculated as
+ described in section 13.2.3; this section describes how to calculate
+ the freshness lifetime, and to determine if a response has expired.
+ In the discussion below, the values can be represented in any form
+ appropriate for arithmetic operations.
+
+ We use the term "expires_value" to denote the value of the Expires
+ header. We use the term "max_age_value" to denote an appropriate
+ value of the number of seconds carried by the max-age directive of
+ the Cache-Control header in a response (see section 14.10.
+
+ The max-age directive takes priority over Expires, so if max-age is
+ present in a response, the calculation is simply:
+
+ freshness_lifetime = max_age_value
+
+ Otherwise, if Expires is present in the response, the calculation is:
+
+ freshness_lifetime = expires_value - date_value
+
+ Note that neither of these calculations is vulnerable to clock skew,
+ since all of the information comes from the origin server.
+
+ If neither Expires nor Cache-Control: max-age appears in the
+ response, and the response does not include other restrictions on
+ caching, the cache MAY compute a freshness lifetime using a
+ heuristic. If the value is greater than 24 hours, the cache must
+ attach Warning 13 to any response whose age is more than 24 hours if
+
+
+
+Fielding, et. al. Standards Track [Page 79]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ such warning has not already been added.
+
+ Also, if the response does have a Last-Modified time, the heuristic
+ expiration value SHOULD be no more than some fraction of the interval
+ since that time. A typical setting of this fraction might be 10%.
+
+ The calculation to determine if a response has expired is quite
+ simple:
+
+ response_is_fresh = (freshness_lifetime > current_age)
+
+13.2.5 Disambiguating Expiration Values
+
+ Because expiration values are assigned optimistically, it is possible
+ for two caches to contain fresh values for the same resource that are
+ different.
+
+ If a client performing a retrieval receives a non-first-hand response
+ for a request that was already fresh in its own cache, and the Date
+ header in its existing cache entry is newer than the Date on the new
+ response, then the client MAY ignore the response. If so, it MAY
+ retry the request with a "Cache-Control: max-age=0" directive (see
+ section 14.9), to force a check with the origin server.
+
+ If a cache has two fresh responses for the same representation with
+ different validators, it MUST use the one with the more recent Date
+ header. This situation may arise because the cache is pooling
+ responses from other caches, or because a client has asked for a
+ reload or a revalidation of an apparently fresh cache entry.
+
+13.2.6 Disambiguating Multiple Responses
+
+ Because a client may be receiving responses via multiple paths, so
+ that some responses flow through one set of caches and other
+ responses flow through a different set of caches, a client may
+ receive responses in an order different from that in which the origin
+ server sent them. We would like the client to use the most recently
+ generated response, even if older responses are still apparently
+ fresh.
+
+ Neither the entity tag nor the expiration value can impose an
+ ordering on responses, since it is possible that a later response
+ intentionally carries an earlier expiration time. However, the
+ HTTP/1.1 specification requires the transmission of Date headers on
+ every response, and the Date values are ordered to a granularity of
+ one second.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 80]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ When a client tries to revalidate a cache entry, and the response it
+ receives contains a Date header that appears to be older than the one
+ for the existing entry, then the client SHOULD repeat the request
+ unconditionally, and include
+
+ Cache-Control: max-age=0
+
+ to force any intermediate caches to validate their copies directly
+ with the origin server, or
+
+ Cache-Control: no-cache
+
+ to force any intermediate caches to obtain a new copy from the origin
+ server.
+
+ If the Date values are equal, then the client may use either response
+ (or may, if it is being extremely prudent, request a new response).
+ Servers MUST NOT depend on clients being able to choose
+ deterministically between responses generated during the same second,
+ if their expiration times overlap.
+
+13.3 Validation Model
+
+ When a cache has a stale entry that it would like to use as a
+ response to a client's request, it first has to check with the origin
+ server (or possibly an intermediate cache with a fresh response) to
+ see if its cached entry is still usable. We call this "validating"
+ the cache entry. Since we do not want to have to pay the overhead of
+ retransmitting the full response if the cached entry is good, and we
+ do not want to pay the overhead of an extra round trip if the cached
+ entry is invalid, the HTTP/1.1 protocol supports the use of
+ conditional methods.
+
+ The key protocol features for supporting conditional methods are
+ those concerned with "cache validators." When an origin server
+ generates a full response, it attaches some sort of validator to it,
+ which is kept with the cache entry. When a client (user agent or
+ proxy cache) makes a conditional request for a resource for which it
+ has a cache entry, it includes the associated validator in the
+ request.
+
+ The server then checks that validator against the current validator
+ for the entity, and, if they match, it responds with a special status
+ code (usually, 304 (Not Modified)) and no entity-body. Otherwise, it
+ returns a full response (including entity-body). Thus, we avoid
+ transmitting the full response if the validator matches, and we avoid
+ an extra round trip if it does not match.
+
+
+
+
+Fielding, et. al. Standards Track [Page 81]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Note: the comparison functions used to decide if validators match
+ are defined in section 13.3.3.
+
+ In HTTP/1.1, a conditional request looks exactly the same as a normal
+ request for the same resource, except that it carries a special
+ header (which includes the validator) that implicitly turns the
+ method (usually, GET) into a conditional.
+
+ The protocol includes both positive and negative senses of cache-
+ validating conditions. That is, it is possible to request either that
+ a method be performed if and only if a validator matches or if and
+ only if no validators match.
+
+ Note: a response that lacks a validator may still be cached, and
+ served from cache until it expires, unless this is explicitly
+ prohibited by a Cache-Control directive. However, a cache cannot do
+ a conditional retrieval if it does not have a validator for the
+ entity, which means it will not be refreshable after it expires.
+
+13.3.1 Last-modified Dates
+
+ The Last-Modified entity-header field value is often used as a cache
+ validator. In simple terms, a cache entry is considered to be valid
+ if the entity has not been modified since the Last-Modified value.
+
+13.3.2 Entity Tag Cache Validators
+
+ The ETag entity-header field value, an entity tag, provides for an
+ "opaque" cache validator. This may allow more reliable validation in
+ situations where it is inconvenient to store modification dates,
+ where the one-second resolution of HTTP date values is not
+ sufficient, or where the origin server wishes to avoid certain
+ paradoxes that may arise from the use of modification dates.
+
+ Entity Tags are described in section 3.11. The headers used with
+ entity tags are described in sections 14.20, 14.25, 14.26 and 14.43.
+
+13.3.3 Weak and Strong Validators
+
+ Since both origin servers and caches will compare two validators to
+ decide if they represent the same or different entities, one normally
+ would expect that if the entity (the entity-body or any entity-
+ headers) changes in any way, then the associated validator would
+ change as well. If this is true, then we call this validator a
+ "strong validator."
+
+ However, there may be cases when a server prefers to change the
+ validator only on semantically significant changes, and not when
+
+
+
+Fielding, et. al. Standards Track [Page 82]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ insignificant aspects of the entity change. A validator that does not
+ always change when the resource changes is a "weak validator."
+
+ Entity tags are normally "strong validators," but the protocol
+ provides a mechanism to tag an entity tag as "weak." One can think of
+ a strong validator as one that changes whenever the bits of an entity
+ changes, while a weak value changes whenever the meaning of an entity
+ changes. Alternatively, one can think of a strong validator as part
+ of an identifier for a specific entity, while a weak validator is
+ part of an identifier for a set of semantically equivalent entities.
+
+ Note: One example of a strong validator is an integer that is
+ incremented in stable storage every time an entity is changed.
+
+ An entity's modification time, if represented with one-second
+ resolution, could be a weak validator, since it is possible that
+ the resource may be modified twice during a single second.
+
+ Support for weak validators is optional; however, weak validators
+ allow for more efficient caching of equivalent objects; for
+ example, a hit counter on a site is probably good enough if it is
+ updated every few days or weeks, and any value during that period
+ is likely "good enough" to be equivalent.
+
+ A "use" of a validator is either when a client generates a request
+ and includes the validator in a validating header field, or when a
+ server compares two validators.
+
+ Strong validators are usable in any context. Weak validators are only
+ usable in contexts that do not depend on exact equality of an entity.
+ For example, either kind is usable for a conditional GET of a full
+ entity. However, only a strong validator is usable for a sub-range
+ retrieval, since otherwise the client may end up with an internally
+ inconsistent entity.
+
+ The only function that the HTTP/1.1 protocol defines on validators is
+ comparison. There are two validator comparison functions, depending
+ on whether the comparison context allows the use of weak validators
+ or not:
+
+ o The strong comparison function: in order to be considered equal,
+ both validators must be identical in every way, and neither may be
+ weak.
+ o The weak comparison function: in order to be considered equal, both
+ validators must be identical in every way, but either or both of
+ them may be tagged as "weak" without affecting the result.
+
+ The weak comparison function MAY be used for simple (non-subrange)
+
+
+
+Fielding, et. al. Standards Track [Page 83]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ GET requests. The strong comparison function MUST be used in all
+ other cases.
+
+ An entity tag is strong unless it is explicitly tagged as weak.
+ Section 3.11 gives the syntax for entity tags.
+
+ A Last-Modified time, when used as a validator in a request, is
+ implicitly weak unless it is possible to deduce that it is strong,
+ using the following rules:
+
+ o The validator is being compared by an origin server to the actual
+ current validator for the entity and,
+ o That origin server reliably knows that the associated entity did
+ not change twice during the second covered by the presented
+ validator.
+or
+
+ o The validator is about to be used by a client in an If-Modified-
+ Since or If-Unmodified-Since header, because the client has a cache
+ entry for the associated entity, and
+ o That cache entry includes a Date value, which gives the time when
+ the origin server sent the original response, and
+ o The presented Last-Modified time is at least 60 seconds before the
+ Date value.
+or
+
+ o The validator is being compared by an intermediate cache to the
+ validator stored in its cache entry for the entity, and
+ o That cache entry includes a Date value, which gives the time when
+ the origin server sent the original response, and
+ o The presented Last-Modified time is at least 60 seconds before the
+ Date value.
+
+ This method relies on the fact that if two different responses were
+ sent by the origin server during the same second, but both had the
+ same Last-Modified time, then at least one of those responses would
+ have a Date value equal to its Last-Modified time. The arbitrary 60-
+ second limit guards against the possibility that the Date and Last-
+ Modified values are generated from different clocks, or at somewhat
+ different times during the preparation of the response. An
+ implementation may use a value larger than 60 seconds, if it is
+ believed that 60 seconds is too short.
+
+ If a client wishes to perform a sub-range retrieval on a value for
+ which it has only a Last-Modified time and no opaque validator, it
+ may do this only if the Last-Modified time is strong in the sense
+ described here.
+
+
+
+
+Fielding, et. al. Standards Track [Page 84]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ A cache or origin server receiving a cache-conditional request, other
+ than a full-body GET request, MUST use the strong comparison function
+ to evaluate the condition.
+
+ These rules allow HTTP/1.1 caches and clients to safely perform sub-
+ range retrievals on values that have been obtained from HTTP/1.0
+ servers.
+
+13.3.4 Rules for When to Use Entity Tags and Last-modified Dates
+
+ We adopt a set of rules and recommendations for origin servers,
+ clients, and caches regarding when various validator types should be
+ used, and for what purposes.
+
+ HTTP/1.1 origin servers:
+
+ o SHOULD send an entity tag validator unless it is not feasible to
+ generate one.
+ o MAY send a weak entity tag instead of a strong entity tag, if
+ performance considerations support the use of weak entity tags, or
+ if it is unfeasible to send a strong entity tag.
+ o SHOULD send a Last-Modified value if it is feasible to send one,
+ unless the risk of a breakdown in semantic transparency that could
+ result from using this date in an If-Modified-Since header would
+ lead to serious problems.
+
+ In other words, the preferred behavior for an HTTP/1.1 origin server
+ is to send both a strong entity tag and a Last-Modified value.
+
+ In order to be legal, a strong entity tag MUST change whenever the
+ associated entity value changes in any way. A weak entity tag SHOULD
+ change whenever the associated entity changes in a semantically
+ significant way.
+
+ Note: in order to provide semantically transparent caching, an
+ origin server must avoid reusing a specific strong entity tag value
+ for two different entities, or reusing a specific weak entity tag
+ value for two semantically different entities. Cache entries may
+ persist for arbitrarily long periods, regardless of expiration
+ times, so it may be inappropriate to expect that a cache will never
+ again attempt to validate an entry using a validator that it
+ obtained at some point in the past.
+
+ HTTP/1.1 clients:
+
+ o If an entity tag has been provided by the origin server, MUST
+ use that entity tag in any cache-conditional request (using
+ If-Match or If-None-Match).
+
+
+
+Fielding, et. al. Standards Track [Page 85]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ o If only a Last-Modified value has been provided by the origin
+ server, SHOULD use that value in non-subrange cache-conditional
+ requests (using If-Modified-Since).
+ o If only a Last-Modified value has been provided by an HTTP/1.0
+ origin server, MAY use that value in subrange cache-conditional
+ requests (using If-Unmodified-Since:). The user agent should
+ provide a way to disable this, in case of difficulty.
+ o If both an entity tag and a Last-Modified value have been
+ provided by the origin server, SHOULD use both validators in
+ cache-conditional requests. This allows both HTTP/1.0 and
+ HTTP/1.1 caches to respond appropriately.
+
+ An HTTP/1.1 cache, upon receiving a request, MUST use the most
+ restrictive validator when deciding whether the client's cache entry
+ matches the cache's own cache entry. This is only an issue when the
+ request contains both an entity tag and a last-modified-date
+ validator (If-Modified-Since or If-Unmodified-Since).
+
+ A note on rationale: The general principle behind these rules is
+ that HTTP/1.1 servers and clients should transmit as much non-
+ redundant information as is available in their responses and
+ requests. HTTP/1.1 systems receiving this information will make the
+ most conservative assumptions about the validators they receive.
+
+ HTTP/1.0 clients and caches will ignore entity tags. Generally,
+ last-modified values received or used by these systems will support
+ transparent and efficient caching, and so HTTP/1.1 origin servers
+ should provide Last-Modified values. In those rare cases where the
+ use of a Last-Modified value as a validator by an HTTP/1.0 system
+ could result in a serious problem, then HTTP/1.1 origin servers
+ should not provide one.
+
+13.3.5 Non-validating Conditionals
+
+ The principle behind entity tags is that only the service author
+ knows the semantics of a resource well enough to select an
+ appropriate cache validation mechanism, and the specification of any
+ validator comparison function more complex than byte-equality would
+ open up a can of worms. Thus, comparisons of any other headers
+ (except Last-Modified, for compatibility with HTTP/1.0) are never
+ used for purposes of validating a cache entry.
+
+13.4 Response Cachability
+
+ Unless specifically constrained by a Cache-Control (section 14.9)
+ directive, a caching system may always store a successful response
+ (see section 13.8) as a cache entry, may return it without validation
+ if it is fresh, and may return it after successful validation. If
+
+
+
+Fielding, et. al. Standards Track [Page 86]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ there is neither a cache validator nor an explicit expiration time
+ associated with a response, we do not expect it to be cached, but
+ certain caches may violate this expectation (for example, when little
+ or no network connectivity is available). A client can usually detect
+ that such a response was taken from a cache by comparing the Date
+ header to the current time.
+
+ Note that some HTTP/1.0 caches are known to violate this
+ expectation without providing any Warning.
+
+ However, in some cases it may be inappropriate for a cache to retain
+ an entity, or to return it in response to a subsequent request. This
+ may be because absolute semantic transparency is deemed necessary by
+ the service author, or because of security or privacy considerations.
+ Certain Cache-Control directives are therefore provided so that the
+ server can indicate that certain resource entities, or portions
+ thereof, may not be cached regardless of other considerations.
+
+ Note that section 14.8 normally prevents a shared cache from saving
+ and returning a response to a previous request if that request
+ included an Authorization header.
+
+ A response received with a status code of 200, 203, 206, 300, 301 or
+ 410 may be stored by a cache and used in reply to a subsequent
+ request, subject to the expiration mechanism, unless a Cache-Control
+ directive prohibits caching. However, a cache that does not support
+ the Range and Content-Range headers MUST NOT cache 206 (Partial
+ Content) responses.
+
+ A response received with any other status code MUST NOT be returned
+ in a reply to a subsequent request unless there are Cache-Control
+ directives or another header(s) that explicitly allow it. For
+ example, these include the following: an Expires header (section
+ 14.21); a "max-age", "must-revalidate", "proxy-revalidate", "public"
+ or "private" Cache-Control directive (section 14.9).
+
+13.5 Constructing Responses From Caches
+
+ The purpose of an HTTP cache is to store information received in
+ response to requests, for use in responding to future requests. In
+ many cases, a cache simply returns the appropriate parts of a
+ response to the requester. However, if the cache holds a cache entry
+ based on a previous response, it may have to combine parts of a new
+ response with what is held in the cache entry.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 87]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+13.5.1 End-to-end and Hop-by-hop Headers
+
+ For the purpose of defining the behavior of caches and non-caching
+ proxies, we divide HTTP headers into two categories:
+
+ o End-to-end headers, which must be transmitted to the
+ ultimate recipient of a request or response. End-to-end
+ headers in responses must be stored as part of a cache entry
+ and transmitted in any response formed from a cache entry.
+ o Hop-by-hop headers, which are meaningful only for a single
+ transport-level connection, and are not stored by caches or
+ forwarded by proxies.
+
+ The following HTTP/1.1 headers are hop-by-hop headers:
+
+ o Connection
+ o Keep-Alive
+ o Public
+ o Proxy-Authenticate
+ o Transfer-Encoding
+ o Upgrade
+
+ All other headers defined by HTTP/1.1 are end-to-end headers.
+
+ Hop-by-hop headers introduced in future versions of HTTP MUST be
+ listed in a Connection header, as described in section 14.10.
+
+13.5.2 Non-modifiable Headers
+
+ Some features of the HTTP/1.1 protocol, such as Digest
+ Authentication, depend on the value of certain end-to-end headers. A
+ cache or non-caching proxy SHOULD NOT modify an end-to-end header
+ unless the definition of that header requires or specifically allows
+ that.
+
+ A cache or non-caching proxy MUST NOT modify any of the following
+ fields in a request or response, nor may it add any of these fields
+ if not already present:
+
+ o Content-Location
+ o ETag
+ o Expires
+ o Last-Modified
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 88]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ A cache or non-caching proxy MUST NOT modify or add any of the
+ following fields in a response that contains the no-transform Cache-
+ Control directive, or in any request:
+
+ o Content-Encoding
+ o Content-Length
+ o Content-Range
+ o Content-Type
+
+ A cache or non-caching proxy MAY modify or add these fields in a
+ response that does not include no-transform, but if it does so, it
+ MUST add a Warning 14 (Transformation applied) if one does not
+ already appear in the response.
+
+ Warning: unnecessary modification of end-to-end headers may cause
+ authentication failures if stronger authentication mechanisms are
+ introduced in later versions of HTTP. Such authentication
+ mechanisms may rely on the values of header fields not listed here.
+
+13.5.3 Combining Headers
+
+ When a cache makes a validating request to a server, and the server
+ provides a 304 (Not Modified) response, the cache must construct a
+ response to send to the requesting client. The cache uses the
+ entity-body stored in the cache entry as the entity-body of this
+ outgoing response. The end-to-end headers stored in the cache entry
+ are used for the constructed response, except that any end-to-end
+ headers provided in the 304 response MUST replace the corresponding
+ headers from the cache entry. Unless the cache decides to remove the
+ cache entry, it MUST also replace the end-to-end headers stored with
+ the cache entry with corresponding headers received in the incoming
+ response.
+
+ In other words, the set of end-to-end headers received in the
+ incoming response overrides all corresponding end-to-end headers
+ stored with the cache entry. The cache may add Warning headers (see
+ section 14.45) to this set.
+
+ If a header field-name in the incoming response matches more than one
+ header in the cache entry, all such old headers are replaced.
+
+ Note: this rule allows an origin server to use a 304 (Not Modified)
+ response to update any header associated with a previous response
+ for the same entity, although it might not always be meaningful or
+ correct to do so. This rule does not allow an origin server to use
+ a 304 (not Modified) response to entirely delete a header that it
+ had provided with a previous response.
+
+
+
+
+Fielding, et. al. Standards Track [Page 89]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+13.5.4 Combining Byte Ranges
+
+ A response may transfer only a subrange of the bytes of an entity-
+ body, either because the request included one or more Range
+ specifications, or because a connection was broken prematurely. After
+ several such transfers, a cache may have received several ranges of
+ the same entity-body.
+
+ If a cache has a stored non-empty set of subranges for an entity, and
+ an incoming response transfers another subrange, the cache MAY
+ combine the new subrange with the existing set if both the following
+ conditions are met:
+
+ o Both the incoming response and the cache entry must have a cache
+ validator.
+ o The two cache validators must match using the strong comparison
+ function (see section 13.3.3).
+
+ If either requirement is not meant, the cache must use only the most
+ recent partial response (based on the Date values transmitted with
+ every response, and using the incoming response if these values are
+ equal or missing), and must discard the other partial information.
+
+13.6 Caching Negotiated Responses
+
+ Use of server-driven content negotiation (section 12), as indicated
+ by the presence of a Vary header field in a response, alters the
+ conditions and procedure by which a cache can use the response for
+ subsequent requests.
+
+ A server MUST use the Vary header field (section 14.43) to inform a
+ cache of what header field dimensions are used to select among
+ multiple representations of a cachable response. A cache may use the
+ selected representation (the entity included with that particular
+ response) for replying to subsequent requests on that resource only
+ when the subsequent requests have the same or equivalent values for
+ all header fields specified in the Vary response-header. Requests
+ with a different value for one or more of those header fields would
+ be forwarded toward the origin server.
+
+ If an entity tag was assigned to the representation, the forwarded
+ request SHOULD be conditional and include the entity tags in an If-
+ None-Match header field from all its cache entries for the Request-
+ URI. This conveys to the server the set of entities currently held by
+ the cache, so that if any one of these entities matches the requested
+ entity, the server can use the ETag header in its 304 (Not Modified)
+ response to tell the cache which entry is appropriate. If the
+ entity-tag of the new response matches that of an existing entry, the
+
+
+
+Fielding, et. al. Standards Track [Page 90]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ new response SHOULD be used to update the header fields of the
+ existing entry, and the result MUST be returned to the client.
+
+ The Vary header field may also inform the cache that the
+ representation was selected using criteria not limited to the
+ request-headers; in this case, a cache MUST NOT use the response in a
+ reply to a subsequent request unless the cache relays the new request
+ to the origin server in a conditional request and the server responds
+ with 304 (Not Modified), including an entity tag or Content-Location
+ that indicates which entity should be used.
+
+ If any of the existing cache entries contains only partial content
+ for the associated entity, its entity-tag SHOULD NOT be included in
+ the If-None-Match header unless the request is for a range that would
+ be fully satisfied by that entry.
+
+ If a cache receives a successful response whose Content-Location
+ field matches that of an existing cache entry for the same Request-
+ URI, whose entity-tag differs from that of the existing entry, and
+ whose Date is more recent than that of the existing entry, the
+ existing entry SHOULD NOT be returned in response to future requests,
+ and should be deleted from the cache.
+
+13.7 Shared and Non-Shared Caches
+
+ For reasons of security and privacy, it is necessary to make a
+ distinction between "shared" and "non-shared" caches. A non-shared
+ cache is one that is accessible only to a single user. Accessibility
+ in this case SHOULD be enforced by appropriate security mechanisms.
+ All other caches are considered to be "shared." Other sections of
+ this specification place certain constraints on the operation of
+ shared caches in order to prevent loss of privacy or failure of
+ access controls.
+
+13.8 Errors or Incomplete Response Cache Behavior
+
+ A cache that receives an incomplete response (for example, with fewer
+ bytes of data than specified in a Content-Length header) may store
+ the response. However, the cache MUST treat this as a partial
+ response. Partial responses may be combined as described in section
+ 13.5.4; the result might be a full response or might still be
+ partial. A cache MUST NOT return a partial response to a client
+ without explicitly marking it as such, using the 206 (Partial
+ Content) status code. A cache MUST NOT return a partial response
+ using a status code of 200 (OK).
+
+ If a cache receives a 5xx response while attempting to revalidate an
+ entry, it may either forward this response to the requesting client,
+
+
+
+Fielding, et. al. Standards Track [Page 91]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ or act as if the server failed to respond. In the latter case, it MAY
+ return a previously received response unless the cached entry
+ includes the "must-revalidate" Cache-Control directive (see section
+ 14.9).
+
+13.9 Side Effects of GET and HEAD
+
+ Unless the origin server explicitly prohibits the caching of their
+ responses, the application of GET and HEAD methods to any resources
+ SHOULD NOT have side effects that would lead to erroneous behavior if
+ these responses are taken from a cache. They may still have side
+ effects, but a cache is not required to consider such side effects in
+ its caching decisions. Caches are always expected to observe an
+ origin server's explicit restrictions on caching.
+
+ We note one exception to this rule: since some applications have
+ traditionally used GETs and HEADs with query URLs (those containing a
+ "?" in the rel_path part) to perform operations with significant side
+ effects, caches MUST NOT treat responses to such URLs as fresh unless
+ the server provides an explicit expiration time. This specifically
+ means that responses from HTTP/1.0 servers for such URIs should not
+ be taken from a cache. See section 9.1.1 for related information.
+
+13.10 Invalidation After Updates or Deletions
+
+ The effect of certain methods at the origin server may cause one or
+ more existing cache entries to become non-transparently invalid. That
+ is, although they may continue to be "fresh," they do not accurately
+ reflect what the origin server would return for a new request.
+
+ There is no way for the HTTP protocol to guarantee that all such
+ cache entries are marked invalid. For example, the request that
+ caused the change at the origin server may not have gone through the
+ proxy where a cache entry is stored. However, several rules help
+ reduce the likelihood of erroneous behavior.
+
+ In this section, the phrase "invalidate an entity" means that the
+ cache should either remove all instances of that entity from its
+ storage, or should mark these as "invalid" and in need of a mandatory
+ revalidation before they can be returned in response to a subsequent
+ request.
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 92]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Some HTTP methods may invalidate an entity. This is either the entity
+ referred to by the Request-URI, or by the Location or Content-
+ Location response-headers (if present). These methods are:
+
+ o PUT
+ o DELETE
+ o POST
+
+ In order to prevent denial of service attacks, an invalidation based
+ on the URI in a Location or Content-Location header MUST only be
+ performed if the host part is the same as in the Request-URI.
+
+13.11 Write-Through Mandatory
+
+ All methods that may be expected to cause modifications to the origin
+ server's resources MUST be written through to the origin server. This
+ currently includes all methods except for GET and HEAD. A cache MUST
+ NOT reply to such a request from a client before having transmitted
+ the request to the inbound server, and having received a
+ corresponding response from the inbound server. This does not prevent
+ a cache from sending a 100 (Continue) response before the inbound
+ server has replied.
+
+ The alternative (known as "write-back" or "copy-back" caching) is not
+ allowed in HTTP/1.1, due to the difficulty of providing consistent
+ updates and the problems arising from server, cache, or network
+ failure prior to write-back.
+
+13.12 Cache Replacement
+
+ If a new cachable (see sections 14.9.2, 13.2.5, 13.2.6 and 13.8)
+ response is received from a resource while any existing responses for
+ the same resource are cached, the cache SHOULD use the new response
+ to reply to the current request. It may insert it into cache storage
+ and may, if it meets all other requirements, use it to respond to any
+ future requests that would previously have caused the old response to
+ be returned. If it inserts the new response into cache storage it
+ should follow the rules in section 13.5.3.
+
+ Note: a new response that has an older Date header value than
+ existing cached responses is not cachable.
+
+13.13 History Lists
+
+ User agents often have history mechanisms, such as "Back" buttons and
+ history lists, which can be used to redisplay an entity retrieved
+ earlier in a session.
+
+
+
+
+Fielding, et. al. Standards Track [Page 93]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ History mechanisms and caches are different. In particular history
+ mechanisms SHOULD NOT try to show a semantically transparent view of
+ the current state of a resource. Rather, a history mechanism is meant
+ to show exactly what the user saw at the time when the resource was
+ retrieved.
+
+ By default, an expiration time does not apply to history mechanisms.
+ If the entity is still in storage, a history mechanism should display
+ it even if the entity has expired, unless the user has specifically
+ configured the agent to refresh expired history documents.
+
+ This should not be construed to prohibit the history mechanism from
+ telling the user that a view may be stale.
+
+ Note: if history list mechanisms unnecessarily prevent users from
+ viewing stale resources, this will tend to force service authors to
+ avoid using HTTP expiration controls and cache controls when they
+ would otherwise like to. Service authors may consider it important
+ that users not be presented with error messages or warning messages
+ when they use navigation controls (such as BACK) to view previously
+ fetched resources. Even though sometimes such resources ought not
+ to cached, or ought to expire quickly, user interface
+ considerations may force service authors to resort to other means
+ of preventing caching (e.g. "once-only" URLs) in order not to
+ suffer the effects of improperly functioning history mechanisms.
+
+14 Header Field Definitions
+
+ This section defines the syntax and semantics of all standard
+ HTTP/1.1 header fields. For entity-header fields, both sender and
+ recipient refer to either the client or the server, depending on who
+ sends and who receives the entity.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 94]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+14.1 Accept
+
+ The Accept request-header field can be used to specify certain media
+ types which are acceptable for the response. Accept headers can be
+ used to indicate that the request is specifically limited to a small
+ set of desired types, as in the case of a request for an in-line
+ image.
+
+ Accept = "Accept" ":"
+ #( media-range [ accept-params ] )
+
+ media-range = ( "*/*"
+ | ( type "/" "*" )
+ | ( type "/" subtype )
+ ) *( ";" parameter )
+
+ accept-params = ";" "q" "=" qvalue *( accept-extension )
+
+ accept-extension = ";" token [ "=" ( token | quoted-string ) ]
+
+ The asterisk "*" character is used to group media types into ranges,
+ with "*/*" indicating all media types and "type/*" indicating all
+ subtypes of that type. The media-range MAY include media type
+ parameters that are applicable to that range.
+
+ Each media-range MAY be followed by one or more accept-params,
+ beginning with the "q" parameter for indicating a relative quality
+ factor. The first "q" parameter (if any) separates the media-range
+ parameter(s) from the accept-params. Quality factors allow the user
+ or user agent to indicate the relative degree of preference for that
+ media-range, using the qvalue scale from 0 to 1 (section 3.9). The
+ default value is q=1.
+
+ Note: Use of the "q" parameter name to separate media type
+ parameters from Accept extension parameters is due to historical
+ practice. Although this prevents any media type parameter named
+ "q" from being used with a media range, such an event is believed
+ to be unlikely given the lack of any "q" parameters in the IANA
+ media type registry and the rare usage of any media type parameters
+ in Accept. Future media types should be discouraged from
+ registering any parameter named "q".
+
+ The example
+
+ Accept: audio/*; q=0.2, audio/basic
+
+ SHOULD be interpreted as "I prefer audio/basic, but send me any audio
+ type if it is the best available after an 80% mark-down in quality."
+
+
+
+Fielding, et. al. Standards Track [Page 95]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ If no Accept header field is present, then it is assumed that the
+ client accepts all media types. If an Accept header field is present,
+ and if the server cannot send a response which is acceptable
+ according to the combined Accept field value, then the server SHOULD
+ send a 406 (not acceptable) response.
+
+ A more elaborate example is
+
+ Accept: text/plain; q=0.5, text/html,
+ text/x-dvi; q=0.8, text/x-c
+
+ Verbally, this would be interpreted as "text/html and text/x-c are
+ the preferred media types, but if they do not exist, then send the
+ text/x-dvi entity, and if that does not exist, send the text/plain
+ entity."
+
+ Media ranges can be overridden by more specific media ranges or
+ specific media types. If more than one media range applies to a given
+ type, the most specific reference has precedence. For example,
+
+ Accept: text/*, text/html, text/html;level=1, */*
+
+ have the following precedence:
+
+ 1) text/html;level=1
+ 2) text/html
+ 3) text/*
+ 4) */*
+
+ The media type quality factor associated with a given type is
+ determined by finding the media range with the highest precedence
+ which matches that type. For example,
+
+ Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1,
+ text/html;level=2;q=0.4, */*;q=0.5
+
+ would cause the following values to be associated:
+
+ text/html;level=1 = 1
+ text/html = 0.7
+ text/plain = 0.3
+ image/jpeg = 0.5
+ text/html;level=2 = 0.4
+ text/html;level=3 = 0.7
+
+ Note: A user agent may be provided with a default set of quality
+ values for certain media ranges. However, unless the user agent is
+ a closed system which cannot interact with other rendering agents,
+
+
+
+Fielding, et. al. Standards Track [Page 96]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ this default set should be configurable by the user.
+
+14.2 Accept-Charset
+
+ The Accept-Charset request-header field can be used to indicate what
+ character sets are acceptable for the response. This field allows
+ clients capable of understanding more comprehensive or special-
+ purpose character sets to signal that capability to a server which is
+ capable of representing documents in those character sets. The ISO-
+ 8859-1 character set can be assumed to be acceptable to all user
+ agents.
+
+ Accept-Charset = "Accept-Charset" ":"
+ 1#( charset [ ";" "q" "=" qvalue ] )
+
+ Character set values are described in section 3.4. Each charset may
+ be given an associated quality value which represents the user's
+ preference for that charset. The default value is q=1. An example is
+
+ Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
+
+ If no Accept-Charset header is present, the default is that any
+ character set is acceptable. If an Accept-Charset header is present,
+ and if the server cannot send a response which is acceptable
+ according to the Accept-Charset header, then the server SHOULD send
+ an error response with the 406 (not acceptable) status code, though
+ the sending of an unacceptable response is also allowed.
+
+14.3 Accept-Encoding
+
+ The Accept-Encoding request-header field is similar to Accept, but
+ restricts the content-coding values (section 14.12) which are
+ acceptable in the response.
+
+ Accept-Encoding = "Accept-Encoding" ":"
+ #( content-coding )
+
+ An example of its use is
+
+ Accept-Encoding: compress, gzip
+
+ If no Accept-Encoding header is present in a request, the server MAY
+ assume that the client will accept any content coding. If an Accept-
+ Encoding header is present, and if the server cannot send a response
+ which is acceptable according to the Accept-Encoding header, then the
+ server SHOULD send an error response with the 406 (Not Acceptable)
+ status code.
+
+
+
+
+Fielding, et. al. Standards Track [Page 97]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ An empty Accept-Encoding value indicates none are acceptable.
+
+14.4 Accept-Language
+
+ The Accept-Language request-header field is similar to Accept, but
+ restricts the set of natural languages that are preferred as a
+ response to the request.
+
+ Accept-Language = "Accept-Language" ":"
+ 1#( language-range [ ";" "q" "=" qvalue ] )
+
+ language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
+
+ Each language-range MAY be given an associated quality value which
+ represents an estimate of the user's preference for the languages
+ specified by that range. The quality value defaults to "q=1". For
+ example,
+
+ Accept-Language: da, en-gb;q=0.8, en;q=0.7
+
+ would mean: "I prefer Danish, but will accept British English and
+ other types of English." A language-range matches a language-tag if
+ it exactly equals the tag, or if it exactly equals a prefix of the
+ tag such that the first tag character following the prefix is "-".
+ The special range "*", if present in the Accept-Language field,
+ matches every tag not matched by any other range present in the
+ Accept-Language field.
+
+ Note: This use of a prefix matching rule does not imply that
+ language tags are assigned to languages in such a way that it is
+ always true that if a user understands a language with a certain
+ tag, then this user will also understand all languages with tags
+ for which this tag is a prefix. The prefix rule simply allows the
+ use of prefix tags if this is the case.
+
+ The language quality factor assigned to a language-tag by the
+ Accept-Language field is the quality value of the longest language-
+ range in the field that matches the language-tag. If no language-
+ range in the field matches the tag, the language quality factor
+ assigned is 0. If no Accept-Language header is present in the
+ request, the server SHOULD assume that all languages are equally
+ acceptable. If an Accept-Language header is present, then all
+ languages which are assigned a quality factor greater than 0 are
+ acceptable.
+
+ It may be contrary to the privacy expectations of the user to send an
+ Accept-Language header with the complete linguistic preferences of
+ the user in every request. For a discussion of this issue, see
+
+
+
+Fielding, et. al. Standards Track [Page 98]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ section 15.7.
+
+ Note: As intelligibility is highly dependent on the individual
+ user, it is recommended that client applications make the choice of
+ linguistic preference available to the user. If the choice is not
+ made available, then the Accept-Language header field must not be
+ given in the request.
+
+14.5 Accept-Ranges
+
+ The Accept-Ranges response-header field allows the server to indicate
+ its acceptance of range requests for a resource:
+
+ Accept-Ranges = "Accept-Ranges" ":" acceptable-ranges
+
+ acceptable-ranges = 1#range-unit | "none"
+
+ Origin servers that accept byte-range requests MAY send
+
+ Accept-Ranges: bytes
+
+ but are not required to do so. Clients MAY generate byte-range
+ requests without having received this header for the resource
+ involved.
+
+ Servers that do not accept any kind of range request for a resource
+ MAY send
+
+ Accept-Ranges: none
+
+ to advise the client not to attempt a range request.
+
+14.6 Age
+
+ The Age response-header field conveys the sender's estimate of the
+ amount of time since the response (or its revalidation) was generated
+ at the origin server. A cached response is "fresh" if its age does
+ not exceed its freshness lifetime. Age values are calculated as
+ specified in section 13.2.3.
+
+ Age = "Age" ":" age-value
+
+ age-value = delta-seconds
+
+ Age values are non-negative decimal integers, representing time in
+ seconds.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 99]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ If a cache receives a value larger than the largest positive integer
+ it can represent, or if any of its age calculations overflows, it
+ MUST transmit an Age header with a value of 2147483648 (2^31).
+ HTTP/1.1 caches MUST send an Age header in every response. Caches
+ SHOULD use an arithmetic type of at least 31 bits of range.
+
+14.7 Allow
+
+ The Allow entity-header field lists the set of methods supported by
+ the resource identified by the Request-URI. The purpose of this field
+ is strictly to inform the recipient of valid methods associated with
+ the resource. An Allow header field MUST be present in a 405 (Method
+ Not Allowed) response.
+
+ Allow = "Allow" ":" 1#method
+
+ Example of use:
+
+ Allow: GET, HEAD, PUT
+
+ This field cannot prevent a client from trying other methods.
+ However, the indications given by the Allow header field value SHOULD
+ be followed. The actual set of allowed methods is defined by the
+ origin server at the time of each request.
+
+ The Allow header field MAY be provided with a PUT request to
+ recommend the methods to be supported by the new or modified
+ resource. The server is not required to support these methods and
+ SHOULD include an Allow header in the response giving the actual
+ supported methods.
+
+ A proxy MUST NOT modify the Allow header field even if it does not
+ understand all the methods specified, since the user agent MAY have
+ other means of communicating with the origin server.
+
+ The Allow header field does not indicate what methods are implemented
+ at the server level. Servers MAY use the Public response-header field
+ (section 14.35) to describe what methods are implemented on the
+ server as a whole.
+
+14.8 Authorization
+
+ A user agent that wishes to authenticate itself with a server--
+ usually, but not necessarily, after receiving a 401 response--MAY do
+ so by including an Authorization request-header field with the
+ request. The Authorization field value consists of credentials
+ containing the authentication information of the user agent for the
+ realm of the resource being requested.
+
+
+
+Fielding, et. al. Standards Track [Page 100]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Authorization = "Authorization" ":" credentials
+
+ HTTP access authentication is described in section 11. If a request
+ is authenticated and a realm specified, the same credentials SHOULD
+ be valid for all other requests within this realm.
+
+ When a shared cache (see section 13.7) receives a request containing
+ an Authorization field, it MUST NOT return the corresponding response
+ as a reply to any other request, unless one of the following specific
+ exceptions holds:
+
+ 1. If the response includes the "proxy-revalidate" Cache-Control
+ directive, the cache MAY use that response in replying to a
+ subsequent request, but a proxy cache MUST first revalidate it with
+ the origin server, using the request-headers from the new request
+ to allow the origin server to authenticate the new request.
+ 2. If the response includes the "must-revalidate" Cache-Control
+ directive, the cache MAY use that response in replying to a
+ subsequent request, but all caches MUST first revalidate it with
+ the origin server, using the request-headers from the new request
+ to allow the origin server to authenticate the new request.
+ 3. If the response includes the "public" Cache-Control directive, it
+ may be returned in reply to any subsequent request.
+
+14.9 Cache-Control
+
+ The Cache-Control general-header field is used to specify directives
+ that MUST be obeyed by all caching mechanisms along the
+ request/response chain. The directives specify behavior intended to
+ prevent caches from adversely interfering with the request or
+ response. These directives typically override the default caching
+ algorithms. Cache directives are unidirectional in that the presence
+ of a directive in a request does not imply that the same directive
+ should be given in the response.
+
+ Note that HTTP/1.0 caches may not implement Cache-Control and may
+ only implement Pragma: no-cache (see section 14.32).
+
+ Cache directives must be passed through by a proxy or gateway
+ application, regardless of their significance to that application,
+ since the directives may be applicable to all recipients along the
+ request/response chain. It is not possible to specify a cache-
+ directive for a specific cache.
+
+ Cache-Control = "Cache-Control" ":" 1#cache-directive
+
+ cache-directive = cache-request-directive
+ | cache-response-directive
+
+
+
+Fielding, et. al. Standards Track [Page 101]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ cache-request-directive =
+ "no-cache" [ "=" <"> 1#field-name <"> ]
+ | "no-store"
+ | "max-age" "=" delta-seconds
+ | "max-stale" [ "=" delta-seconds ]
+ | "min-fresh" "=" delta-seconds
+ | "only-if-cached"
+ | cache-extension
+
+ cache-response-directive =
+ "public"
+ | "private" [ "=" <"> 1#field-name <"> ]
+ | "no-cache" [ "=" <"> 1#field-name <"> ]
+ | "no-store"
+ | "no-transform"
+ | "must-revalidate"
+ | "proxy-revalidate"
+ | "max-age" "=" delta-seconds
+ | cache-extension
+
+ cache-extension = token [ "=" ( token | quoted-string ) ]
+
+ When a directive appears without any 1#field-name parameter, the
+ directive applies to the entire request or response. When such a
+ directive appears with a 1#field-name parameter, it applies only to
+ the named field or fields, and not to the rest of the request or
+ response. This mechanism supports extensibility; implementations of
+ future versions of the HTTP protocol may apply these directives to
+ header fields not defined in HTTP/1.1.
+
+ The cache-control directives can be broken down into these general
+ categories:
+
+ o Restrictions on what is cachable; these may only be imposed by the
+ origin server.
+ o Restrictions on what may be stored by a cache; these may be imposed
+ by either the origin server or the user agent.
+ o Modifications of the basic expiration mechanism; these may be
+ imposed by either the origin server or the user agent.
+ o Controls over cache revalidation and reload; these may only be
+ imposed by a user agent.
+ o Control over transformation of entities.
+ o Extensions to the caching system.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 102]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+14.9.1 What is Cachable
+
+ By default, a response is cachable if the requirements of the request
+ method, request header fields, and the response status indicate that
+ it is cachable. Section 13.4 summarizes these defaults for
+ cachability. The following Cache-Control response directives allow an
+ origin server to override the default cachability of a response:
+
+public
+ Indicates that the response is cachable by any cache, even if it
+ would normally be non-cachable or cachable only within a non-shared
+ cache. (See also Authorization, section 14.8, for additional
+ details.)
+
+private
+ Indicates that all or part of the response message is intended for a
+ single user and MUST NOT be cached by a shared cache. This allows an
+ origin server to state that the specified parts of the response are
+ intended for only one user and are not a valid response for requests
+ by other users. A private (non-shared) cache may cache the response.
+
+ Note: This usage of the word private only controls where the
+ response may be cached, and cannot ensure the privacy of the
+ message content.
+
+no-cache
+ Indicates that all or part of the response message MUST NOT be cached
+ anywhere. This allows an origin server to prevent caching even by
+ caches that have been configured to return stale responses to client
+ requests.
+
+ Note: Most HTTP/1.0 caches will not recognize or obey this
+ directive.
+
+14.9.2 What May be Stored by Caches
+
+ The purpose of the no-store directive is to prevent the inadvertent
+ release or retention of sensitive information (for example, on backup
+ tapes). The no-store directive applies to the entire message, and may
+ be sent either in a response or in a request. If sent in a request, a
+ cache MUST NOT store any part of either this request or any response
+ to it. If sent in a response, a cache MUST NOT store any part of
+ either this response or the request that elicited it. This directive
+ applies to both non-shared and shared caches. "MUST NOT store" in
+ this context means that the cache MUST NOT intentionally store the
+ information in non-volatile storage, and MUST make a best-effort
+ attempt to remove the information from volatile storage as promptly
+ as possible after forwarding it.
+
+
+
+Fielding, et. al. Standards Track [Page 103]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Even when this directive is associated with a response, users may
+ explicitly store such a response outside of the caching system (e.g.,
+ with a "Save As" dialog). History buffers may store such responses as
+ part of their normal operation.
+
+ The purpose of this directive is to meet the stated requirements of
+ certain users and service authors who are concerned about accidental
+ releases of information via unanticipated accesses to cache data
+ structures. While the use of this directive may improve privacy in
+ some cases, we caution that it is NOT in any way a reliable or
+ sufficient mechanism for ensuring privacy. In particular, malicious
+ or compromised caches may not recognize or obey this directive; and
+ communications networks may be vulnerable to eavesdropping.
+
+14.9.3 Modifications of the Basic Expiration Mechanism
+
+ The expiration time of an entity may be specified by the origin
+ server using the Expires header (see section 14.21). Alternatively,
+ it may be specified using the max-age directive in a response.
+
+ If a response includes both an Expires header and a max-age
+ directive, the max-age directive overrides the Expires header, even
+ if the Expires header is more restrictive. This rule allows an origin
+ server to provide, for a given response, a longer expiration time to
+ an HTTP/1.1 (or later) cache than to an HTTP/1.0 cache. This may be
+ useful if certain HTTP/1.0 caches improperly calculate ages or
+ expiration times, perhaps due to desynchronized clocks.
+
+ Note: most older caches, not compliant with this specification, do
+ not implement any Cache-Control directives. An origin server
+ wishing to use a Cache-Control directive that restricts, but does
+ not prevent, caching by an HTTP/1.1-compliant cache may exploit the
+ requirement that the max-age directive overrides the Expires
+ header, and the fact that non-HTTP/1.1-compliant caches do not
+ observe the max-age directive.
+
+ Other directives allow an user agent to modify the basic expiration
+ mechanism. These directives may be specified on a request:
+
+ max-age
+ Indicates that the client is willing to accept a response whose age
+ is no greater than the specified time in seconds. Unless max-stale
+ directive is also included, the client is not willing to accept a
+ stale response.
+
+ min-fresh
+ Indicates that the client is willing to accept a response whose
+ freshness lifetime is no less than its current age plus the
+
+
+
+Fielding, et. al. Standards Track [Page 104]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ specified time in seconds. That is, the client wants a response
+ that will still be fresh for at least the specified number of
+ seconds.
+
+ max-stale
+ Indicates that the client is willing to accept a response that has
+ exceeded its expiration time. If max-stale is assigned a value,
+ then the client is willing to accept a response that has exceeded
+ its expiration time by no more than the specified number of
+ seconds. If no value is assigned to max-stale, then the client is
+ willing to accept a stale response of any age.
+
+ If a cache returns a stale response, either because of a max-stale
+ directive on a request, or because the cache is configured to
+ override the expiration time of a response, the cache MUST attach a
+ Warning header to the stale response, using Warning 10 (Response is
+ stale).
+
+14.9.4 Cache Revalidation and Reload Controls
+
+ Sometimes an user agent may want or need to insist that a cache
+ revalidate its cache entry with the origin server (and not just with
+ the next cache along the path to the origin server), or to reload its
+ cache entry from the origin server. End-to-end revalidation may be
+ necessary if either the cache or the origin server has overestimated
+ the expiration time of the cached response. End-to-end reload may be
+ necessary if the cache entry has become corrupted for some reason.
+
+ End-to-end revalidation may be requested either when the client does
+ not have its own local cached copy, in which case we call it
+ "unspecified end-to-end revalidation", or when the client does have a
+ local cached copy, in which case we call it "specific end-to-end
+ revalidation."
+
+ The client can specify these three kinds of action using Cache-
+ Control request directives:
+
+ End-to-end reload
+ The request includes a "no-cache" Cache-Control directive or, for
+ compatibility with HTTP/1.0 clients, "Pragma: no-cache". No field
+ names may be included with the no-cache directive in a request. The
+ server MUST NOT use a cached copy when responding to such a
+ request.
+
+ Specific end-to-end revalidation
+ The request includes a "max-age=0" Cache-Control directive, which
+ forces each cache along the path to the origin server to revalidate
+ its own entry, if any, with the next cache or server. The initial
+
+
+
+Fielding, et. al. Standards Track [Page 105]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ request includes a cache-validating conditional with the client's
+ current validator.
+
+ Unspecified end-to-end revalidation
+ The request includes "max-age=0" Cache-Control directive, which
+ forces each cache along the path to the origin server to revalidate
+ its own entry, if any, with the next cache or server. The initial
+ request does not include a cache-validating conditional; the first
+ cache along the path (if any) that holds a cache entry for this
+ resource includes a cache-validating conditional with its current
+ validator.
+
+ When an intermediate cache is forced, by means of a max-age=0
+ directive, to revalidate its own cache entry, and the client has
+ supplied its own validator in the request, the supplied validator may
+ differ from the validator currently stored with the cache entry. In
+ this case, the cache may use either validator in making its own
+ request without affecting semantic transparency.
+
+ However, the choice of validator may affect performance. The best
+ approach is for the intermediate cache to use its own validator when
+ making its request. If the server replies with 304 (Not Modified),
+ then the cache should return its now validated copy to the client
+ with a 200 (OK) response. If the server replies with a new entity and
+ cache validator, however, the intermediate cache should compare the
+ returned validator with the one provided in the client's request,
+ using the strong comparison function. If the client's validator is
+ equal to the origin server's, then the intermediate cache simply
+ returns 304 (Not Modified). Otherwise, it returns the new entity with
+ a 200 (OK) response.
+
+ If a request includes the no-cache directive, it should not include
+ min-fresh, max-stale, or max-age.
+
+ In some cases, such as times of extremely poor network connectivity,
+ a client may want a cache to return only those responses that it
+ currently has stored, and not to reload or revalidate with the origin
+ server. To do this, the client may include the only-if-cached
+ directive in a request. If it receives this directive, a cache SHOULD
+ either respond using a cached entry that is consistent with the other
+ constraints of the request, or respond with a 504 (Gateway Timeout)
+ status. However, if a group of caches is being operated as a unified
+ system with good internal connectivity, such a request MAY be
+ forwarded within that group of caches.
+
+ Because a cache may be configured to ignore a server's specified
+ expiration time, and because a client request may include a max-stale
+ directive (which has a similar effect), the protocol also includes a
+
+
+
+Fielding, et. al. Standards Track [Page 106]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ mechanism for the origin server to require revalidation of a cache
+ entry on any subsequent use. When the must-revalidate directive is
+ present in a response received by a cache, that cache MUST NOT use
+ the entry after it becomes stale to respond to a subsequent request
+ without first revalidating it with the origin server. (I.e., the
+ cache must do an end-to-end revalidation every time, if, based solely
+ on the origin server's Expires or max-age value, the cached response
+ is stale.)
+
+ The must-revalidate directive is necessary to support reliable
+ operation for certain protocol features. In all circumstances an
+ HTTP/1.1 cache MUST obey the must-revalidate directive; in
+ particular, if the cache cannot reach the origin server for any
+ reason, it MUST generate a 504 (Gateway Timeout) response.
+
+ Servers should send the must-revalidate directive if and only if
+ failure to revalidate a request on the entity could result in
+ incorrect operation, such as a silently unexecuted financial
+ transaction. Recipients MUST NOT take any automated action that
+ violates this directive, and MUST NOT automatically provide an
+ unvalidated copy of the entity if revalidation fails.
+
+ Although this is not recommended, user agents operating under severe
+ connectivity constraints may violate this directive but, if so, MUST
+ explicitly warn the user that an unvalidated response has been
+ provided. The warning MUST be provided on each unvalidated access,
+ and SHOULD require explicit user confirmation.
+
+ The proxy-revalidate directive has the same meaning as the must-
+ revalidate directive, except that it does not apply to non-shared
+ user agent caches. It can be used on a response to an authenticated
+ request to permit the user's cache to store and later return the
+ response without needing to revalidate it (since it has already been
+ authenticated once by that user), while still requiring proxies that
+ service many users to revalidate each time (in order to make sure
+ that each user has been authenticated). Note that such authenticated
+ responses also need the public cache control directive in order to
+ allow them to be cached at all.
+
+14.9.5 No-Transform Directive
+
+ Implementers of intermediate caches (proxies) have found it useful to
+ convert the media type of certain entity bodies. A proxy might, for
+ example, convert between image formats in order to save cache space
+ or to reduce the amount of traffic on a slow link. HTTP has to date
+ been silent on these transformations.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 107]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Serious operational problems have already occurred, however, when
+ these transformations have been applied to entity bodies intended for
+ certain kinds of applications. For example, applications for medical
+ imaging, scientific data analysis and those using end-to-end
+ authentication, all depend on receiving an entity body that is bit
+ for bit identical to the original entity-body.
+
+ Therefore, if a response includes the no-transform directive, an
+ intermediate cache or proxy MUST NOT change those headers that are
+ listed in section 13.5.2 as being subject to the no-transform
+ directive. This implies that the cache or proxy must not change any
+ aspect of the entity-body that is specified by these headers.
+
+14.9.6 Cache Control Extensions
+
+ The Cache-Control header field can be extended through the use of one
+ or more cache-extension tokens, each with an optional assigned value.
+ Informational extensions (those which do not require a change in
+ cache behavior) may be added without changing the semantics of other
+ directives. Behavioral extensions are designed to work by acting as
+ modifiers to the existing base of cache directives. Both the new
+ directive and the standard directive are supplied, such that
+ applications which do not understand the new directive will default
+ to the behavior specified by the standard directive, and those that
+ understand the new directive will recognize it as modifying the
+ requirements associated with the standard directive. In this way,
+ extensions to the Cache-Control directives can be made without
+ requiring changes to the base protocol.
+
+ This extension mechanism depends on a HTTP cache obeying all of the
+ cache-control directives defined for its native HTTP-version, obeying
+ certain extensions, and ignoring all directives that it does not
+ understand.
+
+ For example, consider a hypothetical new response directive called
+ "community" which acts as a modifier to the "private" directive. We
+ define this new directive to mean that, in addition to any non-shared
+ cache, any cache which is shared only by members of the community
+ named within its value may cache the response. An origin server
+ wishing to allow the "UCI" community to use an otherwise private
+ response in their shared cache(s) may do so by including
+
+ Cache-Control: private, community="UCI"
+
+ A cache seeing this header field will act correctly even if the cache
+ does not understand the "community" cache-extension, since it will
+ also see and understand the "private" directive and thus default to
+ the safe behavior.
+
+
+
+Fielding, et. al. Standards Track [Page 108]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Unrecognized cache-directives MUST be ignored; it is assumed that any
+ cache-directive likely to be unrecognized by an HTTP/1.1 cache will
+ be combined with standard directives (or the response's default
+ cachability) such that the cache behavior will remain minimally
+ correct even if the cache does not understand the extension(s).
+
+14.10 Connection
+
+ The Connection general-header field allows the sender to specify
+ options that are desired for that particular connection and MUST NOT
+ be communicated by proxies over further connections.
+
+ The Connection header has the following grammar:
+
+ Connection-header = "Connection" ":" 1#(connection-token)
+ connection-token = token
+
+ HTTP/1.1 proxies MUST parse the Connection header field before a
+ message is forwarded and, for each connection-token in this field,
+ remove any header field(s) from the message with the same name as the
+ connection-token. Connection options are signaled by the presence of
+ a connection-token in the Connection header field, not by any
+ corresponding additional header field(s), since the additional header
+ field may not be sent if there are no parameters associated with that
+ connection option. HTTP/1.1 defines the "close" connection option
+ for the sender to signal that the connection will be closed after
+ completion of the response. For example,
+
+ Connection: close
+
+ in either the request or the response header fields indicates that
+ the connection should not be considered `persistent' (section 8.1)
+ after the current request/response is complete.
+
+ HTTP/1.1 applications that do not support persistent connections MUST
+ include the "close" connection option in every message.
+
+14.11 Content-Base
+
+ The Content-Base entity-header field may be used to specify the base
+ URI for resolving relative URLs within the entity. This header field
+ is described as Base in RFC 1808, which is expected to be revised.
+
+ Content-Base = "Content-Base" ":" absoluteURI
+
+ If no Content-Base field is present, the base URI of an entity is
+ defined either by its Content-Location (if that Content-Location URI
+ is an absolute URI) or the URI used to initiate the request, in that
+
+
+
+Fielding, et. al. Standards Track [Page 109]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ order of precedence. Note, however, that the base URI of the contents
+ within the entity-body may be redefined within that entity-body.
+
+14.12 Content-Encoding
+
+ The Content-Encoding entity-header field is used as a modifier to the
+ media-type. When present, its value indicates what additional content
+ codings have been applied to the entity-body, and thus what decoding
+ mechanisms MUST be applied in order to obtain the media-type
+ referenced by the Content-Type header field. Content-Encoding is
+ primarily used to allow a document to be compressed without losing
+ the identity of its underlying media type.
+
+ Content-Encoding = "Content-Encoding" ":" 1#content-coding
+
+ Content codings are defined in section 3.5. An example of its use is
+
+ Content-Encoding: gzip
+
+ The Content-Encoding is a characteristic of the entity identified by
+ the Request-URI. Typically, the entity-body is stored with this
+ encoding and is only decoded before rendering or analogous usage.
+
+ If multiple encodings have been applied to an entity, the content
+ codings MUST be listed in the order in which they were applied.
+
+ Additional information about the encoding parameters MAY be provided
+ by other entity-header fields not defined by this specification.
+
+14.13 Content-Language
+
+ The Content-Language entity-header field describes the natural
+ language(s) of the intended audience for the enclosed entity. Note
+ that this may not be equivalent to all the languages used within the
+ entity-body.
+
+ Content-Language = "Content-Language" ":" 1#language-tag
+
+ Language tags are defined in section 3.10. The primary purpose of
+ Content-Language is to allow a user to identify and differentiate
+ entities according to the user's own preferred language. Thus, if the
+ body content is intended only for a Danish-literate audience, the
+ appropriate field is
+
+ Content-Language: da
+
+ If no Content-Language is specified, the default is that the content
+ is intended for all language audiences. This may mean that the sender
+
+
+
+Fielding, et. al. Standards Track [Page 110]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ does not consider it to be specific to any natural language, or that
+ the sender does not know for which language it is intended.
+
+ Multiple languages MAY be listed for content that is intended for
+ multiple audiences. For example, a rendition of the "Treaty of
+ Waitangi," presented simultaneously in the original Maori and English
+ versions, would call for
+
+ Content-Language: mi, en
+
+ However, just because multiple languages are present within an entity
+ does not mean that it is intended for multiple linguistic audiences.
+ An example would be a beginner's language primer, such as "A First
+ Lesson in Latin," which is clearly intended to be used by an
+ English-literate audience. In this case, the Content-Language should
+ only include "en".
+
+ Content-Language may be applied to any media type -- it is not
+ limited to textual documents.
+
+14.14 Content-Length
+
+ The Content-Length entity-header field indicates the size of the
+ message-body, in decimal number of octets, sent to the recipient or,
+ in the case of the HEAD method, the size of the entity-body that
+ would have been sent had the request been a GET.
+
+ Content-Length = "Content-Length" ":" 1*DIGIT
+
+ An example is
+
+ Content-Length: 3495
+
+ Applications SHOULD use this field to indicate the size of the
+ message-body to be transferred, regardless of the media type of the
+ entity. It must be possible for the recipient to reliably determine
+ the end of HTTP/1.1 requests containing an entity-body, e.g., because
+ the request has a valid Content-Length field, uses Transfer-Encoding:
+ chunked or a multipart body.
+
+ Any Content-Length greater than or equal to zero is a valid value.
+ Section 4.4 describes how to determine the length of a message-body
+ if a Content-Length is not given.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 111]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Note: The meaning of this field is significantly different from the
+ corresponding definition in MIME, where it is an optional field
+ used within the "message/external-body" content-type. In HTTP, it
+ SHOULD be sent whenever the message's length can be determined
+ prior to being transferred.
+
+14.15 Content-Location
+
+ The Content-Location entity-header field may be used to supply the
+ resource location for the entity enclosed in the message. In the case
+ where a resource has multiple entities associated with it, and those
+ entities actually have separate locations by which they might be
+ individually accessed, the server should provide a Content-Location
+ for the particular variant which is returned. In addition, a server
+ SHOULD provide a Content-Location for the resource corresponding to
+ the response entity.
+
+ Content-Location = "Content-Location" ":"
+ ( absoluteURI | relativeURI )
+
+ If no Content-Base header field is present, the value of Content-
+ Location also defines the base URL for the entity (see section
+ 14.11).
+
+ The Content-Location value is not a replacement for the original
+ requested URI; it is only a statement of the location of the resource
+ corresponding to this particular entity at the time of the request.
+ Future requests MAY use the Content-Location URI if the desire is to
+ identify the source of that particular entity.
+
+ A cache cannot assume that an entity with a Content-Location
+ different from the URI used to retrieve it can be used to respond to
+ later requests on that Content-Location URI. However, the Content-
+ Location can be used to differentiate between multiple entities
+ retrieved from a single requested resource, as described in section
+ 13.6.
+
+ If the Content-Location is a relative URI, the URI is interpreted
+ relative to any Content-Base URI provided in the response. If no
+ Content-Base is provided, the relative URI is interpreted relative to
+ the Request-URI.
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 112]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+14.16 Content-MD5
+
+ The Content-MD5 entity-header field, as defined in RFC 1864 [23], is
+ an MD5 digest of the entity-body for the purpose of providing an
+ end-to-end message integrity check (MIC) of the entity-body. (Note: a
+ MIC is good for detecting accidental modification of the entity-body
+ in transit, but is not proof against malicious attacks.)
+
+ Content-MD5 = "Content-MD5" ":" md5-digest
+
+ md5-digest = <base64 of 128 bit MD5 digest as per RFC 1864>
+
+ The Content-MD5 header field may be generated by an origin server to
+ function as an integrity check of the entity-body. Only origin
+ servers may generate the Content-MD5 header field; proxies and
+ gateways MUST NOT generate it, as this would defeat its value as an
+ end-to-end integrity check. Any recipient of the entity-body,
+ including gateways and proxies, MAY check that the digest value in
+ this header field matches that of the entity-body as received.
+
+ The MD5 digest is computed based on the content of the entity-body,
+ including any Content-Encoding that has been applied, but not
+ including any Transfer-Encoding that may have been applied to the
+ message-body. If the message is received with a Transfer-Encoding,
+ that encoding must be removed prior to checking the Content-MD5 value
+ against the received entity.
+
+ This has the result that the digest is computed on the octets of the
+ entity-body exactly as, and in the order that, they would be sent if
+ no Transfer-Encoding were being applied.
+
+ HTTP extends RFC 1864 to permit the digest to be computed for MIME
+ composite media-types (e.g., multipart/* and message/rfc822), but
+ this does not change how the digest is computed as defined in the
+ preceding paragraph.
+
+ Note: There are several consequences of this. The entity-body for
+ composite types may contain many body-parts, each with its own MIME
+ and HTTP headers (including Content-MD5, Content-Transfer-Encoding,
+ and Content-Encoding headers). If a body-part has a Content-
+ Transfer-Encoding or Content-Encoding header, it is assumed that
+ the content of the body-part has had the encoding applied, and the
+ body-part is included in the Content-MD5 digest as is -- i.e.,
+ after the application. The Transfer-Encoding header field is not
+ allowed within body-parts.
+
+ Note: while the definition of Content-MD5 is exactly the same for
+ HTTP as in RFC 1864 for MIME entity-bodies, there are several ways
+
+
+
+Fielding, et. al. Standards Track [Page 113]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ in which the application of Content-MD5 to HTTP entity-bodies
+ differs from its application to MIME entity-bodies. One is that
+ HTTP, unlike MIME, does not use Content-Transfer-Encoding, and does
+ use Transfer-Encoding and Content-Encoding. Another is that HTTP
+ more frequently uses binary content types than MIME, so it is worth
+ noting that, in such cases, the byte order used to compute the
+ digest is the transmission byte order defined for the type. Lastly,
+ HTTP allows transmission of text types with any of several line
+ break conventions and not just the canonical form using CRLF.
+ Conversion of all line breaks to CRLF should not be done before
+ computing or checking the digest: the line break convention used in
+ the text actually transmitted should be left unaltered when
+ computing the digest.
+
+14.17 Content-Range
+
+ The Content-Range entity-header is sent with a partial entity-body to
+ specify where in the full entity-body the partial body should be
+ inserted. It also indicates the total size of the full entity-body.
+ When a server returns a partial response to a client, it must
+ describe both the extent of the range covered by the response, and
+ the length of the entire entity-body.
+
+ Content-Range = "Content-Range" ":" content-range-spec
+
+ content-range-spec = byte-content-range-spec
+
+ byte-content-range-spec = bytes-unit SP first-byte-pos "-"
+ last-byte-pos "/" entity-length
+
+ entity-length = 1*DIGIT
+
+ Unlike byte-ranges-specifier values, a byte-content-range-spec may
+ only specify one range, and must contain absolute byte positions for
+ both the first and last byte of the range.
+
+ A byte-content-range-spec whose last-byte-pos value is less than its
+ first-byte-pos value, or whose entity-length value is less than or
+ equal to its last-byte-pos value, is invalid. The recipient of an
+ invalid byte-content-range-spec MUST ignore it and any content
+ transferred along with it.
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 114]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Examples of byte-content-range-spec values, assuming that the entity
+ contains a total of 1234 bytes:
+
+ o The first 500 bytes:
+
+ bytes 0-499/1234
+
+ o The second 500 bytes:
+
+ bytes 500-999/1234
+
+ o All except for the first 500 bytes:
+
+ bytes 500-1233/1234
+
+ o The last 500 bytes:
+
+ bytes 734-1233/1234
+
+ When an HTTP message includes the content of a single range (for
+ example, a response to a request for a single range, or to a request
+ for a set of ranges that overlap without any holes), this content is
+ transmitted with a Content-Range header, and a Content-Length header
+ showing the number of bytes actually transferred. For example,
+
+ HTTP/1.1 206 Partial content
+ Date: Wed, 15 Nov 1995 06:25:24 GMT
+ Last-modified: Wed, 15 Nov 1995 04:58:08 GMT
+ Content-Range: bytes 21010-47021/47022
+ Content-Length: 26012
+ Content-Type: image/gif
+
+ When an HTTP message includes the content of multiple ranges (for
+ example, a response to a request for multiple non-overlapping
+ ranges), these are transmitted as a multipart MIME message. The
+ multipart MIME content-type used for this purpose is defined in this
+ specification to be "multipart/byteranges". See appendix 19.2 for its
+ definition.
+
+ A client that cannot decode a MIME multipart/byteranges message
+ should not ask for multiple byte-ranges in a single request.
+
+ When a client requests multiple byte-ranges in one request, the
+ server SHOULD return them in the order that they appeared in the
+ request.
+
+ If the server ignores a byte-range-spec because it is invalid, the
+ server should treat the request as if the invalid Range header field
+
+
+
+Fielding, et. al. Standards Track [Page 115]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ did not exist. (Normally, this means return a 200 response containing
+ the full entity). The reason is that the only time a client will make
+ such an invalid request is when the entity is smaller than the entity
+ retrieved by a prior request.
+
+14.18 Content-Type
+
+ The Content-Type entity-header field indicates the media type of the
+ entity-body sent to the recipient or, in the case of the HEAD method,
+ the media type that would have been sent had the request been a GET.
+
+ Content-Type = "Content-Type" ":" media-type
+ Media types are defined in section 3.7. An example of the field is
+
+ Content-Type: text/html; charset=ISO-8859-4
+
+ Further discussion of methods for identifying the media type of an
+ entity is provided in section 7.2.1.
+
+14.19 Date
+
+ The Date general-header field represents the date and time at which
+ the message was originated, having the same semantics as orig-date in
+ RFC 822. The field value is an HTTP-date, as described in section
+ 3.3.1.
+
+ Date = "Date" ":" HTTP-date
+
+ An example is
+
+ Date: Tue, 15 Nov 1994 08:12:31 GMT
+
+ If a message is received via direct connection with the user agent
+ (in the case of requests) or the origin server (in the case of
+ responses), then the date can be assumed to be the current date at
+ the receiving end. However, since the date--as it is believed by the
+ origin--is important for evaluating cached responses, origin servers
+ MUST include a Date header field in all responses. Clients SHOULD
+ only send a Date header field in messages that include an entity-
+ body, as in the case of the PUT and POST requests, and even then it
+ is optional. A received message which does not have a Date header
+ field SHOULD be assigned one by the recipient if the message will be
+ cached by that recipient or gatewayed via a protocol which requires a
+ Date.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 116]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ In theory, the date SHOULD represent the moment just before the
+ entity is generated. In practice, the date can be generated at any
+ time during the message origination without affecting its semantic
+ value.
+
+ The format of the Date is an absolute date and time as defined by
+ HTTP-date in section 3.3; it MUST be sent in RFC1123 [8]-date format.
+
+14.20 ETag
+
+ The ETag entity-header field defines the entity tag for the
+ associated entity. The headers used with entity tags are described in
+ sections 14.20, 14.25, 14.26 and 14.43. The entity tag may be used
+ for comparison with other entities from the same resource (see
+ section 13.3.2).
+
+ ETag = "ETag" ":" entity-tag
+
+ Examples:
+
+ ETag: "xyzzy"
+ ETag: W/"xyzzy"
+ ETag: ""
+
+14.21 Expires
+
+ The Expires entity-header field gives the date/time after which the
+ response should be considered stale. A stale cache entry may not
+ normally be returned by a cache (either a proxy cache or an user
+ agent cache) unless it is first validated with the origin server (or
+ with an intermediate cache that has a fresh copy of the entity). See
+ section 13.2 for further discussion of the expiration model.
+
+ The presence of an Expires field does not imply that the original
+ resource will change or cease to exist at, before, or after that
+ time.
+
+ The format is an absolute date and time as defined by HTTP-date in
+ section 3.3; it MUST be in RFC1123-date format:
+
+ Expires = "Expires" ":" HTTP-date
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 117]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ An example of its use is
+
+ Expires: Thu, 01 Dec 1994 16:00:00 GMT
+
+ Note: if a response includes a Cache-Control field with the max-age
+ directive, that directive overrides the Expires field.
+
+ HTTP/1.1 clients and caches MUST treat other invalid date formats,
+ especially including the value "0", as in the past (i.e., "already
+ expired").
+
+ To mark a response as "already expired," an origin server should use
+ an Expires date that is equal to the Date header value. (See the
+ rules for expiration calculations in section 13.2.4.)
+
+ To mark a response as "never expires," an origin server should use an
+ Expires date approximately one year from the time the response is
+ sent. HTTP/1.1 servers should not send Expires dates more than one
+ year in the future.
+
+ The presence of an Expires header field with a date value of some
+ time in the future on an response that otherwise would by default be
+ non-cacheable indicates that the response is cachable, unless
+ indicated otherwise by a Cache-Control header field (section 14.9).
+
+14.22 From
+
+ The From request-header field, if given, SHOULD contain an Internet
+ e-mail address for the human user who controls the requesting user
+ agent. The address SHOULD be machine-usable, as defined by mailbox
+ in RFC 822 (as updated by RFC 1123 ):
+
+ From = "From" ":" mailbox
+
+ An example is:
+
+ From: webmaster@w3.org
+
+ This header field MAY be used for logging purposes and as a means for
+ identifying the source of invalid or unwanted requests. It SHOULD NOT
+ be used as an insecure form of access protection. The interpretation
+ of this field is that the request is being performed on behalf of the
+ person given, who accepts responsibility for the method performed. In
+ particular, robot agents SHOULD include this header so that the
+ person responsible for running the robot can be contacted if problems
+ occur on the receiving end.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 118]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The Internet e-mail address in this field MAY be separate from the
+ Internet host which issued the request. For example, when a request
+ is passed through a proxy the original issuer's address SHOULD be
+ used.
+
+ Note: The client SHOULD not send the From header field without the
+ user's approval, as it may conflict with the user's privacy
+ interests or their site's security policy. It is strongly
+ recommended that the user be able to disable, enable, and modify
+ the value of this field at any time prior to a request.
+
+14.23 Host
+
+ The Host request-header field specifies the Internet host and port
+ number of the resource being requested, as obtained from the original
+ URL given by the user or referring resource (generally an HTTP URL,
+ as described in section 3.2.2). The Host field value MUST represent
+ the network location of the origin server or gateway given by the
+ original URL. This allows the origin server or gateway to
+ differentiate between internally-ambiguous URLs, such as the root "/"
+ URL of a server for multiple host names on a single IP address.
+
+ Host = "Host" ":" host [ ":" port ] ; Section 3.2.2
+
+ A "host" without any trailing port information implies the default
+ port for the service requested (e.g., "80" for an HTTP URL). For
+ example, a request on the origin server for
+ <http://www.w3.org/pub/WWW/> MUST include:
+
+ GET /pub/WWW/ HTTP/1.1
+ Host: www.w3.org
+
+ A client MUST include a Host header field in all HTTP/1.1 request
+ messages on the Internet (i.e., on any message corresponding to a
+ request for a URL which includes an Internet host address for the
+ service being requested). If the Host field is not already present,
+ an HTTP/1.1 proxy MUST add a Host field to the request message prior
+ to forwarding it on the Internet. All Internet-based HTTP/1.1 servers
+ MUST respond with a 400 status code to any HTTP/1.1 request message
+ which lacks a Host header field.
+
+ See sections 5.2 and 19.5.1 for other requirements relating to Host.
+
+14.24 If-Modified-Since
+
+ The If-Modified-Since request-header field is used with the GET
+ method to make it conditional: if the requested variant has not been
+ modified since the time specified in this field, an entity will not
+
+
+
+Fielding, et. al. Standards Track [Page 119]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ be returned from the server; instead, a 304 (not modified) response
+ will be returned without any message-body.
+
+ If-Modified-Since = "If-Modified-Since" ":" HTTP-date
+
+ An example of the field is:
+
+ If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+
+ A GET method with an If-Modified-Since header and no Range header
+ requests that the identified entity be transferred only if it has
+ been modified since the date given by the If-Modified-Since header.
+ The algorithm for determining this includes the following cases:
+
+ a)If the request would normally result in anything other than a 200
+ (OK) status, or if the passed If-Modified-Since date is invalid, the
+ response is exactly the same as for a normal GET. A date which is
+ later than the server's current time is invalid.
+
+ b)If the variant has been modified since the If-Modified-Since date,
+ the response is exactly the same as for a normal GET.
+
+ c)If the variant has not been modified since a valid If-Modified-Since
+ date, the server MUST return a 304 (Not Modified) response.
+
+ The purpose of this feature is to allow efficient updates of cached
+ information with a minimum amount of transaction overhead.
+
+ Note that the Range request-header field modifies the meaning of
+ If-Modified-Since; see section 14.36 for full details.
+
+ Note that If-Modified-Since times are interpreted by the server,
+ whose clock may not be synchronized with the client.
+
+ Note that if a client uses an arbitrary date in the If-Modified-Since
+ header instead of a date taken from the Last-Modified header for the
+ same request, the client should be aware of the fact that this date
+ is interpreted in the server's understanding of time. The client
+ should consider unsynchronized clocks and rounding problems due to
+ the different encodings of time between the client and server. This
+ includes the possibility of race conditions if the document has
+ changed between the time it was first requested and the If-Modified-
+ Since date of a subsequent request, and the possibility of clock-
+ skew-related problems if the If-Modified-Since date is derived from
+ the client's clock without correction to the server's clock.
+ Corrections for different time bases between client and server are at
+ best approximate due to network latency.
+
+
+
+
+Fielding, et. al. Standards Track [Page 120]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+14.25 If-Match
+
+ The If-Match request-header field is used with a method to make it
+ conditional. A client that has one or more entities previously
+ obtained from the resource can verify that one of those entities is
+ current by including a list of their associated entity tags in the
+ If-Match header field. The purpose of this feature is to allow
+ efficient updates of cached information with a minimum amount of
+ transaction overhead. It is also used, on updating requests, to
+ prevent inadvertent modification of the wrong version of a resource.
+ As a special case, the value "*" matches any current entity of the
+ resource.
+
+ If-Match = "If-Match" ":" ( "*" | 1#entity-tag )
+
+ If any of the entity tags match the entity tag of the entity that
+ would have been returned in the response to a similar GET request
+ (without the If-Match header) on that resource, or if "*" is given
+ and any current entity exists for that resource, then the server MAY
+ perform the requested method as if the If-Match header field did not
+ exist.
+
+ A server MUST use the strong comparison function (see section 3.11)
+ to compare the entity tags in If-Match.
+
+ If none of the entity tags match, or if "*" is given and no current
+ entity exists, the server MUST NOT perform the requested method, and
+ MUST return a 412 (Precondition Failed) response. This behavior is
+ most useful when the client wants to prevent an updating method, such
+ as PUT, from modifying a resource that has changed since the client
+ last retrieved it.
+
+ If the request would, without the If-Match header field, result in
+ anything other than a 2xx status, then the If-Match header MUST be
+ ignored.
+
+ The meaning of "If-Match: *" is that the method SHOULD be performed
+ if the representation selected by the origin server (or by a cache,
+ possibly using the Vary mechanism, see section 14.43) exists, and
+ MUST NOT be performed if the representation does not exist.
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 121]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ A request intended to update a resource (e.g., a PUT) MAY include an
+ If-Match header field to signal that the request method MUST NOT be
+ applied if the entity corresponding to the If-Match value (a single
+ entity tag) is no longer a representation of that resource. This
+ allows the user to indicate that they do not wish the request to be
+ successful if the resource has been changed without their knowledge.
+ Examples:
+
+ If-Match: "xyzzy"
+ If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
+ If-Match: *
+
+14.26 If-None-Match
+
+ The If-None-Match request-header field is used with a method to make
+ it conditional. A client that has one or more entities previously
+ obtained from the resource can verify that none of those entities is
+ current by including a list of their associated entity tags in the
+ If-None-Match header field. The purpose of this feature is to allow
+ efficient updates of cached information with a minimum amount of
+ transaction overhead. It is also used, on updating requests, to
+ prevent inadvertent modification of a resource which was not known to
+ exist.
+
+ As a special case, the value "*" matches any current entity of the
+ resource.
+
+ If-None-Match = "If-None-Match" ":" ( "*" | 1#entity-tag )
+
+ If any of the entity tags match the entity tag of the entity that
+ would have been returned in the response to a similar GET request
+ (without the If-None-Match header) on that resource, or if "*" is
+ given and any current entity exists for that resource, then the
+ server MUST NOT perform the requested method. Instead, if the request
+ method was GET or HEAD, the server SHOULD respond with a 304 (Not
+ Modified) response, including the cache-related entity-header fields
+ (particularly ETag) of one of the entities that matched. For all
+ other request methods, the server MUST respond with a status of 412
+ (Precondition Failed).
+
+ See section 13.3.3 for rules on how to determine if two entity tags
+ match. The weak comparison function can only be used with GET or HEAD
+ requests.
+
+ If none of the entity tags match, or if "*" is given and no current
+ entity exists, then the server MAY perform the requested method as if
+ the If-None-Match header field did not exist.
+
+
+
+
+Fielding, et. al. Standards Track [Page 122]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ If the request would, without the If-None-Match header field, result
+ in anything other than a 2xx status, then the If-None-Match header
+ MUST be ignored.
+
+ The meaning of "If-None-Match: *" is that the method MUST NOT be
+ performed if the representation selected by the origin server (or by
+ a cache, possibly using the Vary mechanism, see section 14.43)
+ exists, and SHOULD be performed if the representation does not exist.
+ This feature may be useful in preventing races between PUT
+ operations.
+
+ Examples:
+
+ If-None-Match: "xyzzy"
+ If-None-Match: W/"xyzzy"
+ If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
+ If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
+ If-None-Match: *
+
+14.27 If-Range
+
+ If a client has a partial copy of an entity in its cache, and wishes
+ to have an up-to-date copy of the entire entity in its cache, it
+ could use the Range request-header with a conditional GET (using
+ either or both of If-Unmodified-Since and If-Match.) However, if the
+ condition fails because the entity has been modified, the client
+ would then have to make a second request to obtain the entire current
+ entity-body.
+
+ The If-Range header allows a client to "short-circuit" the second
+ request. Informally, its meaning is `if the entity is unchanged, send
+ me the part(s) that I am missing; otherwise, send me the entire new
+ entity.'
+
+ If-Range = "If-Range" ":" ( entity-tag | HTTP-date )
+
+ If the client has no entity tag for an entity, but does have a Last-
+ Modified date, it may use that date in a If-Range header. (The server
+ can distinguish between a valid HTTP-date and any form of entity-tag
+ by examining no more than two characters.) The If-Range header should
+ only be used together with a Range header, and must be ignored if the
+ request does not include a Range header, or if the server does not
+ support the sub-range operation.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 123]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ If the entity tag given in the If-Range header matches the current
+ entity tag for the entity, then the server should provide the
+ specified sub-range of the entity using a 206 (Partial content)
+ response. If the entity tag does not match, then the server should
+ return the entire entity using a 200 (OK) response.
+
+14.28 If-Unmodified-Since
+
+ The If-Unmodified-Since request-header field is used with a method to
+ make it conditional. If the requested resource has not been modified
+ since the time specified in this field, the server should perform the
+ requested operation as if the If-Unmodified-Since header were not
+ present.
+
+ If the requested variant has been modified since the specified time,
+ the server MUST NOT perform the requested operation, and MUST return
+ a 412 (Precondition Failed).
+
+ If-Unmodified-Since = "If-Unmodified-Since" ":" HTTP-date
+
+ An example of the field is:
+
+ If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+
+ If the request normally (i.e., without the If-Unmodified-Since
+ header) would result in anything other than a 2xx status, the If-
+ Unmodified-Since header should be ignored.
+
+ If the specified date is invalid, the header is ignored.
+
+14.29 Last-Modified
+
+ The Last-Modified entity-header field indicates the date and time at
+ which the origin server believes the variant was last modified.
+
+ Last-Modified = "Last-Modified" ":" HTTP-date
+
+ An example of its use is
+
+ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
+
+ The exact meaning of this header field depends on the implementation
+ of the origin server and the nature of the original resource. For
+ files, it may be just the file system last-modified time. For
+ entities with dynamically included parts, it may be the most recent
+ of the set of last-modify times for its component parts. For database
+ gateways, it may be the last-update time stamp of the record. For
+ virtual objects, it may be the last time the internal state changed.
+
+
+
+Fielding, et. al. Standards Track [Page 124]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ An origin server MUST NOT send a Last-Modified date which is later
+ than the server's time of message origination. In such cases, where
+ the resource's last modification would indicate some time in the
+ future, the server MUST replace that date with the message
+ origination date.
+
+ An origin server should obtain the Last-Modified value of the entity
+ as close as possible to the time that it generates the Date value of
+ its response. This allows a recipient to make an accurate assessment
+ of the entity's modification time, especially if the entity changes
+ near the time that the response is generated.
+
+ HTTP/1.1 servers SHOULD send Last-Modified whenever feasible.
+
+14.30 Location
+
+ The Location response-header field is used to redirect the recipient
+ to a location other than the Request-URI for completion of the
+ request or identification of a new resource. For 201 (Created)
+ responses, the Location is that of the new resource which was created
+ by the request. For 3xx responses, the location SHOULD indicate the
+ server's preferred URL for automatic redirection to the resource. The
+ field value consists of a single absolute URL.
+
+ Location = "Location" ":" absoluteURI
+
+ An example is
+
+ Location: http://www.w3.org/pub/WWW/People.html
+
+ Note: The Content-Location header field (section 14.15) differs
+ from Location in that the Content-Location identifies the original
+ location of the entity enclosed in the request. It is therefore
+ possible for a response to contain header fields for both Location
+ and Content-Location. Also see section 13.10 for cache requirements
+ of some methods.
+
+14.31 Max-Forwards
+
+ The Max-Forwards request-header field may be used with the TRACE
+ method (section 14.31) to limit the number of proxies or gateways
+ that can forward the request to the next inbound server. This can be
+ useful when the client is attempting to trace a request chain which
+ appears to be failing or looping in mid-chain.
+
+ Max-Forwards = "Max-Forwards" ":" 1*DIGIT
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 125]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The Max-Forwards value is a decimal integer indicating the remaining
+ number of times this request message may be forwarded.
+
+ Each proxy or gateway recipient of a TRACE request containing a Max-
+ Forwards header field SHOULD check and update its value prior to
+ forwarding the request. If the received value is zero (0), the
+ recipient SHOULD NOT forward the request; instead, it SHOULD respond
+ as the final recipient with a 200 (OK) response containing the
+ received request message as the response entity-body (as described in
+ section 9.8). If the received Max-Forwards value is greater than
+ zero, then the forwarded message SHOULD contain an updated Max-
+ Forwards field with a value decremented by one (1).
+
+ The Max-Forwards header field SHOULD be ignored for all other methods
+ defined by this specification and for any extension methods for which
+ it is not explicitly referred to as part of that method definition.
+
+14.32 Pragma
+
+ The Pragma general-header field is used to include implementation-
+ specific directives that may apply to any recipient along the
+ request/response chain. All pragma directives specify optional
+ behavior from the viewpoint of the protocol; however, some systems
+ MAY require that behavior be consistent with the directives.
+
+ Pragma = "Pragma" ":" 1#pragma-directive
+
+ pragma-directive = "no-cache" | extension-pragma
+ extension-pragma = token [ "=" ( token | quoted-string ) ]
+
+ When the no-cache directive is present in a request message, an
+ application SHOULD forward the request toward the origin server even
+ if it has a cached copy of what is being requested. This pragma
+ directive has the same semantics as the no-cache cache-directive (see
+ section 14.9) and is defined here for backwards compatibility with
+ HTTP/1.0. Clients SHOULD include both header fields when a no-cache
+ request is sent to a server not known to be HTTP/1.1 compliant.
+
+ Pragma directives MUST be passed through by a proxy or gateway
+ application, regardless of their significance to that application,
+ since the directives may be applicable to all recipients along the
+ request/response chain. It is not possible to specify a pragma for a
+ specific recipient; however, any pragma directive not relevant to a
+ recipient SHOULD be ignored by that recipient.
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 126]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ HTTP/1.1 clients SHOULD NOT send the Pragma request-header. HTTP/1.1
+ caches SHOULD treat "Pragma: no-cache" as if the client had sent
+ "Cache-Control: no-cache". No new Pragma directives will be defined
+ in HTTP.
+
+14.33 Proxy-Authenticate
+
+ The Proxy-Authenticate response-header field MUST be included as part
+ of a 407 (Proxy Authentication Required) response. The field value
+ consists of a challenge that indicates the authentication scheme and
+ parameters applicable to the proxy for this Request-URI.
+
+ Proxy-Authenticate = "Proxy-Authenticate" ":" challenge
+
+ The HTTP access authentication process is described in section 11.
+ Unlike WWW-Authenticate, the Proxy-Authenticate header field applies
+ only to the current connection and SHOULD NOT be passed on to
+ downstream clients. However, an intermediate proxy may need to obtain
+ its own credentials by requesting them from the downstream client,
+ which in some circumstances will appear as if the proxy is forwarding
+ the Proxy-Authenticate header field.
+
+14.34 Proxy-Authorization
+
+ The Proxy-Authorization request-header field allows the client to
+ identify itself (or its user) to a proxy which requires
+ authentication. The Proxy-Authorization field value consists of
+ credentials containing the authentication information of the user
+ agent for the proxy and/or realm of the resource being requested.
+
+ Proxy-Authorization = "Proxy-Authorization" ":" credentials
+
+ The HTTP access authentication process is described in section 11.
+ Unlike Authorization, the Proxy-Authorization header field applies
+ only to the next outbound proxy that demanded authentication using
+ the Proxy-Authenticate field. When multiple proxies are used in a
+ chain, the Proxy-Authorization header field is consumed by the first
+ outbound proxy that was expecting to receive credentials. A proxy MAY
+ relay the credentials from the client request to the next proxy if
+ that is the mechanism by which the proxies cooperatively authenticate
+ a given request.
+
+14.35 Public
+
+ The Public response-header field lists the set of methods supported
+ by the server. The purpose of this field is strictly to inform the
+ recipient of the capabilities of the server regarding unusual
+ methods. The methods listed may or may not be applicable to the
+
+
+
+Fielding, et. al. Standards Track [Page 127]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Request-URI; the Allow header field (section 14.7) MAY be used to
+ indicate methods allowed for a particular URI.
+
+ Public = "Public" ":" 1#method
+
+ Example of use:
+
+ Public: OPTIONS, MGET, MHEAD, GET, HEAD
+
+ This header field applies only to the server directly connected to
+ the client (i.e., the nearest neighbor in a chain of connections). If
+ the response passes through a proxy, the proxy MUST either remove the
+ Public header field or replace it with one applicable to its own
+ capabilities.
+
+14.36 Range
+
+14.36.1 Byte Ranges
+
+ Since all HTTP entities are represented in HTTP messages as sequences
+ of bytes, the concept of a byte range is meaningful for any HTTP
+ entity. (However, not all clients and servers need to support byte-
+ range operations.)
+
+ Byte range specifications in HTTP apply to the sequence of bytes in
+ the entity-body (not necessarily the same as the message-body).
+
+ A byte range operation may specify a single range of bytes, or a set
+ of ranges within a single entity.
+
+ ranges-specifier = byte-ranges-specifier
+
+ byte-ranges-specifier = bytes-unit "=" byte-range-set
+
+ byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
+
+ byte-range-spec = first-byte-pos "-" [last-byte-pos]
+
+ first-byte-pos = 1*DIGIT
+
+ last-byte-pos = 1*DIGIT
+
+ The first-byte-pos value in a byte-range-spec gives the byte-offset
+ of the first byte in a range. The last-byte-pos value gives the
+ byte-offset of the last byte in the range; that is, the byte
+ positions specified are inclusive. Byte offsets start at zero.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 128]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ If the last-byte-pos value is present, it must be greater than or
+ equal to the first-byte-pos in that byte-range-spec, or the byte-
+ range-spec is invalid. The recipient of an invalid byte-range-spec
+ must ignore it.
+
+ If the last-byte-pos value is absent, or if the value is greater than
+ or equal to the current length of the entity-body, last-byte-pos is
+ taken to be equal to one less than the current length of the entity-
+ body in bytes.
+
+ By its choice of last-byte-pos, a client can limit the number of
+ bytes retrieved without knowing the size of the entity.
+
+ suffix-byte-range-spec = "-" suffix-length
+
+ suffix-length = 1*DIGIT
+
+ A suffix-byte-range-spec is used to specify the suffix of the
+ entity-body, of a length given by the suffix-length value. (That is,
+ this form specifies the last N bytes of an entity-body.) If the
+ entity is shorter than the specified suffix-length, the entire
+ entity-body is used.
+
+ Examples of byte-ranges-specifier values (assuming an entity-body of
+ length 10000):
+
+ o The first 500 bytes (byte offsets 0-499, inclusive):
+
+ bytes=0-499
+
+ o The second 500 bytes (byte offsets 500-999, inclusive):
+
+ bytes=500-999
+
+ o The final 500 bytes (byte offsets 9500-9999, inclusive):
+
+ bytes=-500
+
+ o Or
+
+ bytes=9500-
+
+ o The first and last bytes only (bytes 0 and 9999):
+
+ bytes=0-0,-1
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 129]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ o Several legal but not canonical specifications of the second
+ 500 bytes (byte offsets 500-999, inclusive):
+
+ bytes=500-600,601-999
+
+ bytes=500-700,601-999
+
+14.36.2 Range Retrieval Requests
+
+ HTTP retrieval requests using conditional or unconditional GET
+ methods may request one or more sub-ranges of the entity, instead of
+ the entire entity, using the Range request header, which applies to
+ the entity returned as the result of the request:
+
+ Range = "Range" ":" ranges-specifier
+
+ A server MAY ignore the Range header. However, HTTP/1.1 origin
+ servers and intermediate caches SHOULD support byte ranges when
+ possible, since Range supports efficient recovery from partially
+ failed transfers, and supports efficient partial retrieval of large
+ entities.
+
+ If the server supports the Range header and the specified range or
+ ranges are appropriate for the entity:
+
+ o The presence of a Range header in an unconditional GET modifies
+ what is returned if the GET is otherwise successful. In other
+ words, the response carries a status code of 206 (Partial
+ Content) instead of 200 (OK).
+
+ o The presence of a Range header in a conditional GET (a request
+ using one or both of If-Modified-Since and If-None-Match, or
+ one or both of If-Unmodified-Since and If-Match) modifies what
+ is returned if the GET is otherwise successful and the condition
+ is true. It does not affect the 304 (Not Modified) response
+ returned if the conditional is false.
+
+ In some cases, it may be more appropriate to use the If-Range header
+ (see section 14.27) in addition to the Range header.
+
+ If a proxy that supports ranges receives a Range request, forwards
+ the request to an inbound server, and receives an entire entity in
+ reply, it SHOULD only return the requested range to its client. It
+ SHOULD store the entire received response in its cache, if that is
+ consistent with its cache allocation policies.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 130]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+14.37 Referer
+
+ The Referer[sic] request-header field allows the client to specify,
+ for the server's benefit, the address (URI) of the resource from
+ which the Request-URI was obtained (the "referrer", although the
+ header field is misspelled.) The Referer request-header allows a
+ server to generate lists of back-links to resources for interest,
+ logging, optimized caching, etc. It also allows obsolete or mistyped
+ links to be traced for maintenance. The Referer field MUST NOT be
+ sent if the Request-URI was obtained from a source that does not have
+ its own URI, such as input from the user keyboard.
+
+ Referer = "Referer" ":" ( absoluteURI | relativeURI )
+
+ Example:
+
+ Referer: http://www.w3.org/hypertext/DataSources/Overview.html
+
+ If the field value is a partial URI, it SHOULD be interpreted
+ relative to the Request-URI. The URI MUST NOT include a fragment.
+
+ Note: Because the source of a link may be private information or
+ may reveal an otherwise private information source, it is strongly
+ recommended that the user be able to select whether or not the
+ Referer field is sent. For example, a browser client could have a
+ toggle switch for browsing openly/anonymously, which would
+ respectively enable/disable the sending of Referer and From
+ information.
+
+14.38 Retry-After
+
+ The Retry-After response-header field can be used with a 503 (Service
+ Unavailable) response to indicate how long the service is expected to
+ be unavailable to the requesting client. The value of this field can
+ be either an HTTP-date or an integer number of seconds (in decimal)
+ after the time of the response.
+
+ Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
+
+ Two examples of its use are
+
+ Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
+ Retry-After: 120
+
+ In the latter example, the delay is 2 minutes.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 131]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+14.39 Server
+
+ The Server response-header field contains information about the
+ software used by the origin server to handle the request. The field
+ can contain multiple product tokens (section 3.8) and comments
+ identifying the server and any significant subproducts. The product
+ tokens are listed in order of their significance for identifying the
+ application.
+
+ Server = "Server" ":" 1*( product | comment )
+
+ Example:
+
+ Server: CERN/3.0 libwww/2.17
+
+ If the response is being forwarded through a proxy, the proxy
+ application MUST NOT modify the Server response-header. Instead, it
+ SHOULD include a Via field (as described in section 14.44).
+
+ Note: Revealing the specific software version of the server may
+ allow the server machine to become more vulnerable to attacks
+ against software that is known to contain security holes. Server
+ implementers are encouraged to make this field a configurable
+ option.
+
+14.40 Transfer-Encoding
+
+ The Transfer-Encoding general-header field indicates what (if any)
+ type of transformation has been applied to the message body in order
+ to safely transfer it between the sender and the recipient. This
+ differs from the Content-Encoding in that the transfer coding is a
+ property of the message, not of the entity.
+
+ Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-
+ coding
+
+ Transfer codings are defined in section 3.6. An example is:
+
+ Transfer-Encoding: chunked
+
+ Many older HTTP/1.0 applications do not understand the Transfer-
+ Encoding header.
+
+14.41 Upgrade
+
+ The Upgrade general-header allows the client to specify what
+ additional communication protocols it supports and would like to use
+ if the server finds it appropriate to switch protocols. The server
+
+
+
+Fielding, et. al. Standards Track [Page 132]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ MUST use the Upgrade header field within a 101 (Switching Protocols)
+ response to indicate which protocol(s) are being switched.
+
+ Upgrade = "Upgrade" ":" 1#product
+
+ For example,
+
+ Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
+
+ The Upgrade header field is intended to provide a simple mechanism
+ for transition from HTTP/1.1 to some other, incompatible protocol. It
+ does so by allowing the client to advertise its desire to use another
+ protocol, such as a later version of HTTP with a higher major version
+ number, even though the current request has been made using HTTP/1.1.
+ This eases the difficult transition between incompatible protocols by
+ allowing the client to initiate a request in the more commonly
+ supported protocol while indicating to the server that it would like
+ to use a "better" protocol if available (where "better" is determined
+ by the server, possibly according to the nature of the method and/or
+ resource being requested).
+
+ The Upgrade header field only applies to switching application-layer
+ protocols upon the existing transport-layer connection. Upgrade
+ cannot be used to insist on a protocol change; its acceptance and use
+ by the server is optional. The capabilities and nature of the
+ application-layer communication after the protocol change is entirely
+ dependent upon the new protocol chosen, although the first action
+ after changing the protocol MUST be a response to the initial HTTP
+ request containing the Upgrade header field.
+
+ The Upgrade header field only applies to the immediate connection.
+ Therefore, the upgrade keyword MUST be supplied within a Connection
+ header field (section 14.10) whenever Upgrade is present in an
+ HTTP/1.1 message.
+
+ The Upgrade header field cannot be used to indicate a switch to a
+ protocol on a different connection. For that purpose, it is more
+ appropriate to use a 301, 302, 303, or 305 redirection response.
+
+ This specification only defines the protocol name "HTTP" for use by
+ the family of Hypertext Transfer Protocols, as defined by the HTTP
+ version rules of section 3.1 and future updates to this
+ specification. Any token can be used as a protocol name; however, it
+ will only be useful if both the client and server associate the name
+ with the same protocol.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 133]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+14.42 User-Agent
+
+ The User-Agent request-header field contains information about the
+ user agent originating the request. This is for statistical purposes,
+ the tracing of protocol violations, and automated recognition of user
+ agents for the sake of tailoring responses to avoid particular user
+ agent limitations. User agents SHOULD include this field with
+ requests. The field can contain multiple product tokens (section 3.8)
+ and comments identifying the agent and any subproducts which form a
+ significant part of the user agent. By convention, the product tokens
+ are listed in order of their significance for identifying the
+ application.
+
+ User-Agent = "User-Agent" ":" 1*( product | comment )
+
+ Example:
+
+ User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+
+14.43 Vary
+
+ The Vary response-header field is used by a server to signal that the
+ response entity was selected from the available representations of
+ the response using server-driven negotiation (section 12). Field-
+ names listed in Vary headers are those of request-headers. The Vary
+ field value indicates either that the given set of header fields
+ encompass the dimensions over which the representation might vary, or
+ that the dimensions of variance are unspecified ("*") and thus may
+ vary over any aspect of future requests.
+
+ Vary = "Vary" ":" ( "*" | 1#field-name )
+
+ An HTTP/1.1 server MUST include an appropriate Vary header field with
+ any cachable response that is subject to server-driven negotiation.
+ Doing so allows a cache to properly interpret future requests on that
+ resource and informs the user agent about the presence of negotiation
+ on that resource. A server SHOULD include an appropriate Vary header
+ field with a non-cachable response that is subject to server-driven
+ negotiation, since this might provide the user agent with useful
+ information about the dimensions over which the response might vary.
+
+ The set of header fields named by the Vary field value is known as
+ the "selecting" request-headers.
+
+ When the cache receives a subsequent request whose Request-URI
+ specifies one or more cache entries including a Vary header, the
+ cache MUST NOT use such a cache entry to construct a response to the
+ new request unless all of the headers named in the cached Vary header
+
+
+
+Fielding, et. al. Standards Track [Page 134]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ are present in the new request, and all of the stored selecting
+ request-headers from the previous request match the corresponding
+ headers in the new request.
+
+ The selecting request-headers from two requests are defined to match
+ if and only if the selecting request-headers in the first request can
+ be transformed to the selecting request-headers in the second request
+ by adding or removing linear whitespace (LWS) at places where this is
+ allowed by the corresponding BNF, and/or combining multiple message-
+ header fields with the same field name following the rules about
+ message headers in section 4.2.
+
+ A Vary field value of "*" signals that unspecified parameters,
+ possibly other than the contents of request-header fields (e.g., the
+ network address of the client), play a role in the selection of the
+ response representation. Subsequent requests on that resource can
+ only be properly interpreted by the origin server, and thus a cache
+ MUST forward a (possibly conditional) request even when it has a
+ fresh response cached for the resource. See section 13.6 for use of
+ the Vary header by caches.
+
+ A Vary field value consisting of a list of field-names signals that
+ the representation selected for the response is based on a selection
+ algorithm which considers ONLY the listed request-header field values
+ in selecting the most appropriate representation. A cache MAY assume
+ that the same selection will be made for future requests with the
+ same values for the listed field names, for the duration of time in
+ which the response is fresh.
+
+ The field-names given are not limited to the set of standard
+ request-header fields defined by this specification. Field names are
+ case-insensitive.
+
+14.44 Via
+
+ The Via general-header field MUST be used by gateways and proxies to
+ indicate the intermediate protocols and recipients between the user
+ agent and the server on requests, and between the origin server and
+ the client on responses. It is analogous to the "Received" field of
+ RFC 822 and is intended to be used for tracking message forwards,
+ avoiding request loops, and identifying the protocol capabilities of
+ all senders along the request/response chain.
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 135]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
+
+ received-protocol = [ protocol-name "/" ] protocol-version
+ protocol-name = token
+ protocol-version = token
+ received-by = ( host [ ":" port ] ) | pseudonym
+ pseudonym = token
+
+ The received-protocol indicates the protocol version of the message
+ received by the server or client along each segment of the
+ request/response chain. The received-protocol version is appended to
+ the Via field value when the message is forwarded so that information
+ about the protocol capabilities of upstream applications remains
+ visible to all recipients.
+
+ The protocol-name is optional if and only if it would be "HTTP". The
+ received-by field is normally the host and optional port number of a
+ recipient server or client that subsequently forwarded the message.
+ However, if the real host is considered to be sensitive information,
+ it MAY be replaced by a pseudonym. If the port is not given, it MAY
+ be assumed to be the default port of the received-protocol.
+
+ Multiple Via field values represent each proxy or gateway that has
+ forwarded the message. Each recipient MUST append its information
+ such that the end result is ordered according to the sequence of
+ forwarding applications.
+
+ Comments MAY be used in the Via header field to identify the software
+ of the recipient proxy or gateway, analogous to the User-Agent and
+ Server header fields. However, all comments in the Via field are
+ optional and MAY be removed by any recipient prior to forwarding the
+ message.
+
+ For example, a request message could be sent from an HTTP/1.0 user
+ agent to an internal proxy code-named "fred", which uses HTTP/1.1 to
+ forward the request to a public proxy at nowhere.com, which completes
+ the request by forwarding it to the origin server at www.ics.uci.edu.
+ The request received by www.ics.uci.edu would then have the following
+ Via header field:
+
+ Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
+
+ Proxies and gateways used as a portal through a network firewall
+ SHOULD NOT, by default, forward the names and ports of hosts within
+ the firewall region. This information SHOULD only be propagated if
+ explicitly enabled. If not enabled, the received-by host of any host
+ behind the firewall SHOULD be replaced by an appropriate pseudonym
+ for that host.
+
+
+
+Fielding, et. al. Standards Track [Page 136]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ For organizations that have strong privacy requirements for hiding
+ internal structures, a proxy MAY combine an ordered subsequence of
+ Via header field entries with identical received-protocol values into
+ a single such entry. For example,
+
+ Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy
+
+ could be collapsed to
+
+ Via: 1.0 ricky, 1.1 mertz, 1.0 lucy
+
+ Applications SHOULD NOT combine multiple entries unless they are all
+ under the same organizational control and the hosts have already been
+ replaced by pseudonyms. Applications MUST NOT combine entries which
+ have different received-protocol values.
+
+14.45 Warning
+
+ The Warning response-header field is used to carry additional
+ information about the status of a response which may not be reflected
+ by the response status code. This information is typically, though
+ not exclusively, used to warn about a possible lack of semantic
+ transparency from caching operations.
+
+ Warning headers are sent with responses using:
+
+ Warning = "Warning" ":" 1#warning-value
+
+ warning-value = warn-code SP warn-agent SP warn-text
+ warn-code = 2DIGIT
+ warn-agent = ( host [ ":" port ] ) | pseudonym
+ ; the name or pseudonym of the server adding
+ ; the Warning header, for use in debugging
+ warn-text = quoted-string
+
+ A response may carry more than one Warning header.
+
+ The warn-text should be in a natural language and character set that
+ is most likely to be intelligible to the human user receiving the
+ response. This decision may be based on any available knowledge,
+ such as the location of the cache or user, the Accept-Language field
+ in a request, the Content-Language field in a response, etc. The
+ default language is English and the default character set is ISO-
+ 8859-1.
+
+ If a character set other than ISO-8859-1 is used, it MUST be encoded
+ in the warn-text using the method described in RFC 1522 [14].
+
+
+
+
+Fielding, et. al. Standards Track [Page 137]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Any server or cache may add Warning headers to a response. New
+ Warning headers should be added after any existing Warning headers. A
+ cache MUST NOT delete any Warning header that it received with a
+ response. However, if a cache successfully validates a cache entry,
+ it SHOULD remove any Warning headers previously attached to that
+ entry except as specified for specific Warning codes. It MUST then
+ add any Warning headers received in the validating response. In other
+ words, Warning headers are those that would be attached to the most
+ recent relevant response.
+
+ When multiple Warning headers are attached to a response, the user
+ agent SHOULD display as many of them as possible, in the order that
+ they appear in the response. If it is not possible to display all of
+ the warnings, the user agent should follow these heuristics:
+
+ o Warnings that appear early in the response take priority over those
+ appearing later in the response.
+ o Warnings in the user's preferred character set take priority over
+ warnings in other character sets but with identical warn-codes and
+ warn-agents.
+
+ Systems that generate multiple Warning headers should order them with
+ this user agent behavior in mind.
+
+ This is a list of the currently-defined warn-codes, each with a
+ recommended warn-text in English, and a description of its meaning.
+
+10 Response is stale
+ MUST be included whenever the returned response is stale. A cache may
+ add this warning to any response, but may never remove it until the
+ response is known to be fresh.
+
+11 Revalidation failed
+ MUST be included if a cache returns a stale response because an
+ attempt to revalidate the response failed, due to an inability to
+ reach the server. A cache may add this warning to any response, but
+ may never remove it until the response is successfully revalidated.
+
+12 Disconnected operation
+ SHOULD be included if the cache is intentionally disconnected from
+ the rest of the network for a period of time.
+
+13 Heuristic expiration
+ MUST be included if the cache heuristically chose a freshness
+ lifetime greater than 24 hours and the response's age is greater than
+ 24 hours.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 138]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+14 Transformation applied
+ MUST be added by an intermediate cache or proxy if it applies any
+ transformation changing the content-coding (as specified in the
+ Content-Encoding header) or media-type (as specified in the
+ Content-Type header) of the response, unless this Warning code
+ already appears in the response. MUST NOT be deleted from a response
+ even after revalidation.
+
+99 Miscellaneous warning
+ The warning text may include arbitrary information to be presented to
+ a human user, or logged. A system receiving this warning MUST NOT
+ take any automated action.
+
+14.46 WWW-Authenticate
+
+ The WWW-Authenticate response-header field MUST be included in 401
+ (Unauthorized) response messages. The field value consists of at
+ least one challenge that indicates the authentication scheme(s) and
+ parameters applicable to the Request-URI.
+
+ WWW-Authenticate = "WWW-Authenticate" ":" 1#challenge
+
+ The HTTP access authentication process is described in section 11.
+ User agents MUST take special care in parsing the WWW-Authenticate
+ field value if it contains more than one challenge, or if more than
+ one WWW-Authenticate header field is provided, since the contents of
+ a challenge may itself contain a comma-separated list of
+ authentication parameters.
+
+15 Security Considerations
+
+ This section is meant to inform application developers, information
+ providers, and users of the security limitations in HTTP/1.1 as
+ described by this document. The discussion does not include
+ definitive solutions to the problems revealed, though it does make
+ some suggestions for reducing security risks.
+
+15.1 Authentication of Clients
+
+ The Basic authentication scheme is not a secure method of user
+ authentication, nor does it in any way protect the entity, which is
+ transmitted in clear text across the physical network used as the
+ carrier. HTTP does not prevent additional authentication schemes and
+ encryption mechanisms from being employed to increase security or the
+ addition of enhancements (such as schemes to use one-time passwords)
+ to Basic authentication.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 139]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The most serious flaw in Basic authentication is that it results in
+ the essentially clear text transmission of the user's password over
+ the physical network. It is this problem which Digest Authentication
+ attempts to address.
+
+ Because Basic authentication involves the clear text transmission of
+ passwords it SHOULD never be used (without enhancements) to protect
+ sensitive or valuable information.
+
+ A common use of Basic authentication is for identification purposes
+ -- requiring the user to provide a user name and password as a means
+ of identification, for example, for purposes of gathering accurate
+ usage statistics on a server. When used in this way it is tempting to
+ think that there is no danger in its use if illicit access to the
+ protected documents is not a major concern. This is only correct if
+ the server issues both user name and password to the users and in
+ particular does not allow the user to choose his or her own password.
+ The danger arises because naive users frequently reuse a single
+ password to avoid the task of maintaining multiple passwords.
+
+ If a server permits users to select their own passwords, then the
+ threat is not only illicit access to documents on the server but also
+ illicit access to the accounts of all users who have chosen to use
+ their account password. If users are allowed to choose their own
+ password that also means the server must maintain files containing
+ the (presumably encrypted) passwords. Many of these may be the
+ account passwords of users perhaps at distant sites. The owner or
+ administrator of such a system could conceivably incur liability if
+ this information is not maintained in a secure fashion.
+
+ Basic Authentication is also vulnerable to spoofing by counterfeit
+ servers. If a user can be led to believe that he is connecting to a
+ host containing information protected by basic authentication when in
+ fact he is connecting to a hostile server or gateway then the
+ attacker can request a password, store it for later use, and feign an
+ error. This type of attack is not possible with Digest Authentication
+ [32]. Server implementers SHOULD guard against the possibility of
+ this sort of counterfeiting by gateways or CGI scripts. In particular
+ it is very dangerous for a server to simply turn over a connection to
+ a gateway since that gateway can then use the persistent connection
+ mechanism to engage in multiple transactions with the client while
+ impersonating the original server in a way that is not detectable by
+ the client.
+
+15.2 Offering a Choice of Authentication Schemes
+
+ An HTTP/1.1 server may return multiple challenges with a 401
+ (Authenticate) response, and each challenge may use a different
+
+
+
+Fielding, et. al. Standards Track [Page 140]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ scheme. The order of the challenges returned to the user agent is in
+ the order that the server would prefer they be chosen. The server
+ should order its challenges with the "most secure" authentication
+ scheme first. A user agent should choose as the challenge to be made
+ to the user the first one that the user agent understands.
+
+ When the server offers choices of authentication schemes using the
+ WWW-Authenticate header, the "security" of the authentication is only
+ as malicious user could capture the set of challenges and try to
+ authenticate him/herself using the weakest of the authentication
+ schemes. Thus, the ordering serves more to protect the user's
+ credentials than the server's information.
+
+ A possible man-in-the-middle (MITM) attack would be to add a weak
+ authentication scheme to the set of choices, hoping that the client
+ will use one that exposes the user's credentials (e.g. password). For
+ this reason, the client should always use the strongest scheme that
+ it understands from the choices accepted.
+
+ An even better MITM attack would be to remove all offered choices,
+ and to insert a challenge that requests Basic authentication. For
+ this reason, user agents that are concerned about this kind of attack
+ could remember the strongest authentication scheme ever requested by
+ a server and produce a warning message that requires user
+ confirmation before using a weaker one. A particularly insidious way
+ to mount such a MITM attack would be to offer a "free" proxy caching
+ service to gullible users.
+
+15.3 Abuse of Server Log Information
+
+ A server is in the position to save personal data about a user's
+ requests which may identify their reading patterns or subjects of
+ interest. This information is clearly confidential in nature and its
+ handling may be constrained by law in certain countries. People using
+ the HTTP protocol to provide data are responsible for ensuring that
+ such material is not distributed without the permission of any
+ individuals that are identifiable by the published results.
+
+15.4 Transfer of Sensitive Information
+
+ Like any generic data transfer protocol, HTTP cannot regulate the
+ content of the data that is transferred, nor is there any a priori
+ method of determining the sensitivity of any particular piece of
+ information within the context of any given request. Therefore,
+ applications SHOULD supply as much control over this information as
+ possible to the provider of that information. Four header fields are
+ worth special mention in this context: Server, Via, Referer and From.
+
+
+
+
+Fielding, et. al. Standards Track [Page 141]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Revealing the specific software version of the server may allow the
+ server machine to become more vulnerable to attacks against software
+ that is known to contain security holes. Implementers SHOULD make the
+ Server header field a configurable option.
+
+ Proxies which serve as a portal through a network firewall SHOULD
+ take special precautions regarding the transfer of header information
+ that identifies the hosts behind the firewall. In particular, they
+ SHOULD remove, or replace with sanitized versions, any Via fields
+ generated behind the firewall.
+
+ The Referer field allows reading patterns to be studied and reverse
+ links drawn. Although it can be very useful, its power can be abused
+ if user details are not separated from the information contained in
+ the Referer. Even when the personal information has been removed, the
+ Referer field may indicate a private document's URI whose publication
+ would be inappropriate.
+
+ The information sent in the From field might conflict with the user's
+ privacy interests or their site's security policy, and hence it
+ SHOULD NOT be transmitted without the user being able to disable,
+ enable, and modify the contents of the field. The user MUST be able
+ to set the contents of this field within a user preference or
+ application defaults configuration.
+
+ We suggest, though do not require, that a convenient toggle interface
+ be provided for the user to enable or disable the sending of From and
+ Referer information.
+
+15.5 Attacks Based On File and Path Names
+
+ Implementations of HTTP origin servers SHOULD be careful to restrict
+ the documents returned by HTTP requests to be only those that were
+ intended by the server administrators. If an HTTP server translates
+ HTTP URIs directly into file system calls, the server MUST take
+ special care not to serve files that were not intended to be
+ delivered to HTTP clients. For example, UNIX, Microsoft Windows, and
+ other operating systems use ".." as a path component to indicate a
+ directory level above the current one. On such a system, an HTTP
+ server MUST disallow any such construct in the Request-URI if it
+ would otherwise allow access to a resource outside those intended to
+ be accessible via the HTTP server. Similarly, files intended for
+ reference only internally to the server (such as access control
+ files, configuration files, and script code) MUST be protected from
+ inappropriate retrieval, since they might contain sensitive
+ information. Experience has shown that minor bugs in such HTTP server
+ implementations have turned into security risks.
+
+
+
+
+Fielding, et. al. Standards Track [Page 142]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+15.6 Personal Information
+
+ HTTP clients are often privy to large amounts of personal information
+ (e.g. the user's name, location, mail address, passwords, encryption
+ keys, etc.), and SHOULD be very careful to prevent unintentional
+ leakage of this information via the HTTP protocol to other sources.
+ We very strongly recommend that a convenient interface be provided
+ for the user to control dissemination of such information, and that
+ designers and implementers be particularly careful in this area.
+ History shows that errors in this area are often both serious
+ security and/or privacy problems, and often generate highly adverse
+ publicity for the implementer's company.
+
+15.7 Privacy Issues Connected to Accept Headers
+
+ Accept request-headers can reveal information about the user to all
+ servers which are accessed. The Accept-Language header in particular
+ can reveal information the user would consider to be of a private
+ nature, because the understanding of particular languages is often
+ strongly correlated to the membership of a particular ethnic group.
+ User agents which offer the option to configure the contents of an
+ Accept-Language header to be sent in every request are strongly
+ encouraged to let the configuration process include a message which
+ makes the user aware of the loss of privacy involved.
+
+ An approach that limits the loss of privacy would be for a user agent
+ to omit the sending of Accept-Language headers by default, and to ask
+ the user whether it should start sending Accept-Language headers to a
+ server if it detects, by looking for any Vary response-header fields
+ generated by the server, that such sending could improve the quality
+ of service.
+
+ Elaborate user-customized accept header fields sent in every request,
+ in particular if these include quality values, can be used by servers
+ as relatively reliable and long-lived user identifiers. Such user
+ identifiers would allow content providers to do click-trail tracking,
+ and would allow collaborating content providers to match cross-server
+ click-trails or form submissions of individual users. Note that for
+ many users not behind a proxy, the network address of the host
+ running the user agent will also serve as a long-lived user
+ identifier. In environments where proxies are used to enhance
+ privacy, user agents should be conservative in offering accept header
+ configuration options to end users. As an extreme privacy measure,
+ proxies could filter the accept headers in relayed requests. General
+ purpose user agents which provide a high degree of header
+ configurability should warn users about the loss of privacy which can
+ be involved.
+
+
+
+
+Fielding, et. al. Standards Track [Page 143]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+15.8 DNS Spoofing
+
+ Clients using HTTP rely heavily on the Domain Name Service, and are
+ thus generally prone to security attacks based on the deliberate
+ mis-association of IP addresses and DNS names. Clients need to be
+ cautious in assuming the continuing validity of an IP number/DNS name
+ association.
+
+ In particular, HTTP clients SHOULD rely on their name resolver for
+ confirmation of an IP number/DNS name association, rather than
+ caching the result of previous host name lookups. Many platforms
+ already can cache host name lookups locally when appropriate, and
+ they SHOULD be configured to do so. These lookups should be cached,
+ however, only when the TTL (Time To Live) information reported by the
+ name server makes it likely that the cached information will remain
+ useful.
+
+ If HTTP clients cache the results of host name lookups in order to
+ achieve a performance improvement, they MUST observe the TTL
+ information reported by DNS.
+
+ If HTTP clients do not observe this rule, they could be spoofed when
+ a previously-accessed server's IP address changes. As network
+ renumbering is expected to become increasingly common, the
+ possibility of this form of attack will grow. Observing this
+ requirement thus reduces this potential security vulnerability.
+
+ This requirement also improves the load-balancing behavior of clients
+ for replicated servers using the same DNS name and reduces the
+ likelihood of a user's experiencing failure in accessing sites which
+ use that strategy.
+
+15.9 Location Headers and Spoofing
+
+ If a single server supports multiple organizations that do not trust
+ one another, then it must check the values of Location and Content-
+ Location headers in responses that are generated under control of
+ said organizations to make sure that they do not attempt to
+ invalidate resources over which they have no authority.
+
+16 Acknowledgments
+
+ This specification makes heavy use of the augmented BNF and generic
+ constructs defined by David H. Crocker for RFC 822. Similarly, it
+ reuses many of the definitions provided by Nathaniel Borenstein and
+ Ned Freed for MIME. We hope that their inclusion in this
+ specification will help reduce past confusion over the relationship
+ between HTTP and Internet mail message formats.
+
+
+
+Fielding, et. al. Standards Track [Page 144]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The HTTP protocol has evolved considerably over the past four years.
+ It has benefited from a large and active developer community--the
+ many people who have participated on the www-talk mailing list--and
+ it is that community which has been most responsible for the success
+ of HTTP and of the World-Wide Web in general. Marc Andreessen, Robert
+ Cailliau, Daniel W. Connolly, Bob Denny, John Franks, Jean-Francois
+ Groff, Phillip M. Hallam-Baker, Hakon W. Lie, Ari Luotonen, Rob
+ McCool, Lou Montulli, Dave Raggett, Tony Sanders, and Marc
+ VanHeyningen deserve special recognition for their efforts in
+ defining early aspects of the protocol.
+
+ This document has benefited greatly from the comments of all those
+ participating in the HTTP-WG. In addition to those already mentioned,
+ the following individuals have contributed to this specification:
+
+ Gary Adams Albert Lunde
+ Harald Tveit Alvestrand John C. Mallery
+ Keith Ball Jean-Philippe Martin-Flatin
+ Brian Behlendorf Larry Masinter
+ Paul Burchard Mitra
+ Maurizio Codogno David Morris
+ Mike Cowlishaw Gavin Nicol
+ Roman Czyborra Bill Perry
+ Michael A. Dolan Jeffrey Perry
+ David J. Fiander Scott Powers
+ Alan Freier Owen Rees
+ Marc Hedlund Luigi Rizzo
+ Greg Herlihy David Robinson
+ Koen Holtman Marc Salomon
+ Alex Hopmann Rich Salz
+ Bob Jernigan Allan M. Schiffman
+ Shel Kaphan Jim Seidman
+ Rohit Khare Chuck Shotton
+ John Klensin Eric W. Sink
+ Martijn Koster Simon E. Spero
+ Alexei Kosut Richard N. Taylor
+ David M. Kristol Robert S. Thau
+ Daniel LaLiberte Bill (BearHeart) Weinman
+ Ben Laurie Francois Yergeau
+ Paul J. Leach Mary Ellen Zurko
+ Daniel DuBois
+
+ Much of the content and presentation of the caching design is due to
+ suggestions and comments from individuals including: Shel Kaphan,
+ Paul Leach, Koen Holtman, David Morris, and Larry Masinter.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 145]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Most of the specification of ranges is based on work originally done
+ by Ari Luotonen and John Franks, with additional input from Steve
+ Zilles.
+
+ Thanks to the "cave men" of Palo Alto. You know who you are.
+
+ Jim Gettys (the current editor of this document) wishes particularly
+ to thank Roy Fielding, the previous editor of this document, along
+ with John Klensin, Jeff Mogul, Paul Leach, Dave Kristol, Koen
+ Holtman, John Franks, Alex Hopmann, and Larry Masinter for their
+ help.
+
+17 References
+
+ [1] Alvestrand, H., "Tags for the identification of languages", RFC
+ 1766, UNINETT, March 1995.
+
+ [2] Anklesaria, F., McCahill, M., Lindner, P., Johnson, D., Torrey,
+ D., and B. Alberti. "The Internet Gopher Protocol: (a distributed
+ document search and retrieval protocol)", RFC 1436, University of
+ Minnesota, March 1993.
+
+ [3] Berners-Lee, T., "Universal Resource Identifiers in WWW", A
+ Unifying Syntax for the Expression of Names and Addresses of Objects
+ on the Network as used in the World-Wide Web", RFC 1630, CERN, June
+ 1994.
+
+ [4] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform Resource
+ Locators (URL)", RFC 1738, CERN, Xerox PARC, University of Minnesota,
+ December 1994.
+
+ [5] Berners-Lee, T., and D. Connolly, "HyperText Markup Language
+ Specification - 2.0", RFC 1866, MIT/LCS, November 1995.
+
+ [6] Berners-Lee, T., Fielding, R., and H. Frystyk, "Hypertext
+ Transfer Protocol -- HTTP/1.0.", RFC 1945 MIT/LCS, UC Irvine, May
+ 1996.
+
+ [7] Freed, N., and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies", RFC
+ 2045, Innosoft, First Virtual, November 1996.
+
+ [8] Braden, R., "Requirements for Internet hosts - application and
+ support", STD 3, RFC 1123, IETF, October 1989.
+
+ [9] Crocker, D., "Standard for the Format of ARPA Internet Text
+ Messages", STD 11, RFC 822, UDEL, August 1982.
+
+
+
+
+Fielding, et. al. Standards Track [Page 146]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ [10] Davis, F., Kahle, B., Morris, H., Salem, J., Shen, T., Wang, R.,
+ Sui, J., and M. Grinbaum. "WAIS Interface Protocol Prototype
+ Functional Specification", (v1.5), Thinking Machines Corporation,
+ April 1990.
+
+ [11] Fielding, R., "Relative Uniform Resource Locators", RFC 1808, UC
+ Irvine, June 1995.
+
+ [12] Horton, M., and R. Adams. "Standard for interchange of USENET
+ messages", RFC 1036, AT&T Bell Laboratories, Center for Seismic
+ Studies, December 1987.
+
+ [13] Kantor, B., and P. Lapsley. "Network News Transfer Protocol." A
+ Proposed Standard for the Stream-Based Transmission of News", RFC
+ 977, UC San Diego, UC Berkeley, February 1986.
+
+ [14] Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part
+ Three: Message Header Extensions for Non-ASCII Text", RFC 2047,
+ University of Tennessee, November 1996.
+
+ [15] Nebel, E., and L. Masinter. "Form-based File Upload in HTML",
+ RFC 1867, Xerox Corporation, November 1995.
+
+ [16] Postel, J., "Simple Mail Transfer Protocol", STD 10, RFC 821,
+ USC/ISI, August 1982.
+
+ [17] Postel, J., "Media Type Registration Procedure", RFC 2048,
+ USC/ISI, November 1996.
+
+ [18] Postel, J., and J. Reynolds, "File Transfer Protocol (FTP)", STD
+ 9, RFC 959, USC/ISI, October 1985.
+
+ [19] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC
+ 1700, USC/ISI, October 1994.
+
+ [20] Sollins, K., and L. Masinter, "Functional Requirements for
+ Uniform Resource Names", RFC 1737, MIT/LCS, Xerox Corporation,
+ December 1994.
+
+ [21] US-ASCII. Coded Character Set - 7-Bit American Standard Code for
+ Information Interchange. Standard ANSI X3.4-1986, ANSI, 1986.
+
+ [22] ISO-8859. International Standard -- Information Processing --
+ 8-bit Single-Byte Coded Graphic Character Sets --
+ Part 1: Latin alphabet No. 1, ISO 8859-1:1987.
+ Part 2: Latin alphabet No. 2, ISO 8859-2, 1987.
+ Part 3: Latin alphabet No. 3, ISO 8859-3, 1988.
+ Part 4: Latin alphabet No. 4, ISO 8859-4, 1988.
+
+
+
+Fielding, et. al. Standards Track [Page 147]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ Part 5: Latin/Cyrillic alphabet, ISO 8859-5, 1988.
+ Part 6: Latin/Arabic alphabet, ISO 8859-6, 1987.
+ Part 7: Latin/Greek alphabet, ISO 8859-7, 1987.
+ Part 8: Latin/Hebrew alphabet, ISO 8859-8, 1988.
+ Part 9: Latin alphabet No. 5, ISO 8859-9, 1990.
+
+ [23] Meyers, J., and M. Rose "The Content-MD5 Header Field", RFC
+ 1864, Carnegie Mellon, Dover Beach Consulting, October, 1995.
+
+ [24] Carpenter, B., and Y. Rekhter, "Renumbering Needs Work", RFC
+ 1900, IAB, February 1996.
+
+ [25] Deutsch, P., "GZIP file format specification version 4.3." RFC
+ 1952, Aladdin Enterprises, May 1996.
+
+ [26] Venkata N. Padmanabhan and Jeffrey C. Mogul. Improving HTTP
+ Latency. Computer Networks and ISDN Systems, v. 28, pp. 25-35, Dec.
+ 1995. Slightly revised version of paper in Proc. 2nd International
+ WWW Conf. '94: Mosaic and the Web, Oct. 1994, which is available at
+ http://www.ncsa.uiuc.edu/SDG/IT94/Proceedings/DDay/mogul/
+ HTTPLatency.html.
+
+ [27] Joe Touch, John Heidemann, and Katia Obraczka, "Analysis of HTTP
+ Performance", <URL: http://www.isi.edu/lsam/ib/http-perf/>,
+ USC/Information Sciences Institute, June 1996
+
+ [28] Mills, D., "Network Time Protocol, Version 3, Specification,
+ Implementation and Analysis", RFC 1305, University of Delaware, March
+ 1992.
+
+ [29] Deutsch, P., "DEFLATE Compressed Data Format Specification
+ version 1.3." RFC 1951, Aladdin Enterprises, May 1996.
+
+ [30] Spero, S., "Analysis of HTTP Performance Problems"
+ <URL:http://sunsite.unc.edu/mdma-release/http-prob.html>.
+
+ [31] Deutsch, P., and J-L. Gailly, "ZLIB Compressed Data Format
+ Specification version 3.3", RFC 1950, Aladdin Enterprises, Info-ZIP,
+ May 1996.
+
+ [32] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P.,
+ Luotonen, A., Sink, E., and L. Stewart, "An Extension to HTTP :
+ Digest Access Authentication", RFC 2069, January 1997.
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 148]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+18 Authors' Addresses
+
+ Roy T. Fielding
+ Department of Information and Computer Science
+ University of California
+ Irvine, CA 92717-3425, USA
+
+ Fax: +1 (714) 824-4056
+ EMail: fielding@ics.uci.edu
+
+
+ Jim Gettys
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: jg@w3.org
+
+
+ Jeffrey C. Mogul
+ Western Research Laboratory
+ Digital Equipment Corporation
+ 250 University Avenue
+ Palo Alto, California, 94305, USA
+
+ EMail: mogul@wrl.dec.com
+
+
+ Henrik Frystyk Nielsen
+ W3 Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: frystyk@w3.org
+
+
+ Tim Berners-Lee
+ Director, W3 Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: timbl@w3.org
+
+
+
+
+Fielding, et. al. Standards Track [Page 149]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+19 Appendices
+
+19.1 Internet Media Type message/http
+
+ In addition to defining the HTTP/1.1 protocol, this document serves
+ as the specification for the Internet media type "message/http". The
+ following is to be registered with IANA.
+
+ Media Type name: message
+ Media subtype name: http
+ Required parameters: none
+ Optional parameters: version, msgtype
+
+ version: The HTTP-Version number of the enclosed message
+ (e.g., "1.1"). If not present, the version can be
+ determined from the first line of the body.
+
+ msgtype: The message type -- "request" or "response". If not
+ present, the type can be determined from the first
+ line of the body.
+
+ Encoding considerations: only "7bit", "8bit", or "binary" are
+ permitted
+
+ Security considerations: none
+
+19.2 Internet Media Type multipart/byteranges
+
+ When an HTTP message includes the content of multiple ranges (for
+ example, a response to a request for multiple non-overlapping
+ ranges), these are transmitted as a multipart MIME message. The
+ multipart media type for this purpose is called
+ "multipart/byteranges".
+
+ The multipart/byteranges media type includes two or more parts, each
+ with its own Content-Type and Content-Range fields. The parts are
+ separated using a MIME boundary parameter.
+
+ Media Type name: multipart
+ Media subtype name: byteranges
+ Required parameters: boundary
+ Optional parameters: none
+
+ Encoding considerations: only "7bit", "8bit", or "binary" are
+ permitted
+
+ Security considerations: none
+
+
+
+
+Fielding, et. al. Standards Track [Page 150]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+For example:
+
+ HTTP/1.1 206 Partial content
+ Date: Wed, 15 Nov 1995 06:25:24 GMT
+ Last-modified: Wed, 15 Nov 1995 04:58:08 GMT
+ Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
+
+ --THIS_STRING_SEPARATES
+ Content-type: application/pdf
+ Content-range: bytes 500-999/8000
+
+ ...the first range...
+ --THIS_STRING_SEPARATES
+ Content-type: application/pdf
+ Content-range: bytes 7000-7999/8000
+
+ ...the second range
+ --THIS_STRING_SEPARATES--
+
+19.3 Tolerant Applications
+
+ Although this document specifies the requirements for the generation
+ of HTTP/1.1 messages, not all applications will be correct in their
+ implementation. We therefore recommend that operational applications
+ be tolerant of deviations whenever those deviations can be
+ interpreted unambiguously.
+
+ Clients SHOULD be tolerant in parsing the Status-Line and servers
+ tolerant when parsing the Request-Line. In particular, they SHOULD
+ accept any amount of SP or HT characters between fields, even though
+ only a single SP is required.
+
+ The line terminator for message-header fields is the sequence CRLF.
+ However, we recommend that applications, when parsing such headers,
+ recognize a single LF as a line terminator and ignore the leading CR.
+
+ The character set of an entity-body should be labeled as the lowest
+ common denominator of the character codes used within that body, with
+ the exception that no label is preferred over the labels US-ASCII or
+ ISO-8859-1.
+
+ Additional rules for requirements on parsing and encoding of dates
+ and other potential problems with date encodings include:
+
+ o HTTP/1.1 clients and caches should assume that an RFC-850 date
+ which appears to be more than 50 years in the future is in fact
+ in the past (this helps solve the "year 2000" problem).
+
+
+
+
+Fielding, et. al. Standards Track [Page 151]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ o An HTTP/1.1 implementation may internally represent a parsed
+ Expires date as earlier than the proper value, but MUST NOT
+ internally represent a parsed Expires date as later than the
+ proper value.
+
+ o All expiration-related calculations must be done in GMT. The
+ local time zone MUST NOT influence the calculation or comparison
+ of an age or expiration time.
+
+ o If an HTTP header incorrectly carries a date value with a time
+ zone other than GMT, it must be converted into GMT using the most
+ conservative possible conversion.
+
+19.4 Differences Between HTTP Entities and MIME Entities
+
+ HTTP/1.1 uses many of the constructs defined for Internet Mail (RFC
+ 822) and the Multipurpose Internet Mail Extensions (MIME ) to allow
+ entities to be transmitted in an open variety of representations and
+ with extensible mechanisms. However, MIME [7] discusses mail, and
+ HTTP has a few features that are different from those described in
+ MIME. These differences were carefully chosen to optimize
+ performance over binary connections, to allow greater freedom in the
+ use of new media types, to make date comparisons easier, and to
+ acknowledge the practice of some early HTTP servers and clients.
+
+ This appendix describes specific areas where HTTP differs from MIME.
+ Proxies and gateways to strict MIME environments SHOULD be aware of
+ these differences and provide the appropriate conversions where
+ necessary. Proxies and gateways from MIME environments to HTTP also
+ need to be aware of the differences because some conversions may be
+ required.
+
+19.4.1 Conversion to Canonical Form
+
+ MIME requires that an Internet mail entity be converted to canonical
+ form prior to being transferred. Section 3.7.1 of this document
+ describes the forms allowed for subtypes of the "text" media type
+ when transmitted over HTTP. MIME requires that content with a type of
+ "text" represent line breaks as CRLF and forbids the use of CR or LF
+ outside of line break sequences. HTTP allows CRLF, bare CR, and bare
+ LF to indicate a line break within text content when a message is
+ transmitted over HTTP.
+
+ Where it is possible, a proxy or gateway from HTTP to a strict MIME
+ environment SHOULD translate all line breaks within the text media
+ types described in section 3.7.1 of this document to the MIME
+ canonical form of CRLF. Note, however, that this may be complicated
+ by the presence of a Content-Encoding and by the fact that HTTP
+
+
+
+Fielding, et. al. Standards Track [Page 152]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ allows the use of some character sets which do not use octets 13 and
+ 10 to represent CR and LF, as is the case for some multi-byte
+ character sets.
+
+19.4.2 Conversion of Date Formats
+
+ HTTP/1.1 uses a restricted set of date formats (section 3.3.1) to
+ simplify the process of date comparison. Proxies and gateways from
+ other protocols SHOULD ensure that any Date header field present in a
+ message conforms to one of the HTTP/1.1 formats and rewrite the date
+ if necessary.
+
+19.4.3 Introduction of Content-Encoding
+
+ MIME does not include any concept equivalent to HTTP/1.1's Content-
+ Encoding header field. Since this acts as a modifier on the media
+ type, proxies and gateways from HTTP to MIME-compliant protocols MUST
+ either change the value of the Content-Type header field or decode
+ the entity-body before forwarding the message. (Some experimental
+ applications of Content-Type for Internet mail have used a media-type
+ parameter of ";conversions=<content-coding>" to perform an equivalent
+ function as Content-Encoding. However, this parameter is not part of
+ MIME.)
+
+19.4.4 No Content-Transfer-Encoding
+
+ HTTP does not use the Content-Transfer-Encoding (CTE) field of MIME.
+ Proxies and gateways from MIME-compliant protocols to HTTP MUST
+ remove any non-identity CTE ("quoted-printable" or "base64") encoding
+ prior to delivering the response message to an HTTP client.
+
+ Proxies and gateways from HTTP to MIME-compliant protocols are
+ responsible for ensuring that the message is in the correct format
+ and encoding for safe transport on that protocol, where "safe
+ transport" is defined by the limitations of the protocol being used.
+ Such a proxy or gateway SHOULD label the data with an appropriate
+ Content-Transfer-Encoding if doing so will improve the likelihood of
+ safe transport over the destination protocol.
+
+19.4.5 HTTP Header Fields in Multipart Body-Parts
+
+ In MIME, most header fields in multipart body-parts are generally
+ ignored unless the field name begins with "Content-". In HTTP/1.1,
+ multipart body-parts may contain any HTTP header fields which are
+ significant to the meaning of that part.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 153]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+19.4.6 Introduction of Transfer-Encoding
+
+ HTTP/1.1 introduces the Transfer-Encoding header field (section
+ 14.40). Proxies/gateways MUST remove any transfer coding prior to
+ forwarding a message via a MIME-compliant protocol.
+
+ A process for decoding the "chunked" transfer coding (section 3.6)
+ can be represented in pseudo-code as:
+
+ length := 0
+ read chunk-size, chunk-ext (if any) and CRLF
+ while (chunk-size > 0) {
+ read chunk-data and CRLF
+ append chunk-data to entity-body
+ length := length + chunk-size
+ read chunk-size and CRLF
+ }
+ read entity-header
+ while (entity-header not empty) {
+ append entity-header to existing header fields
+ read entity-header
+ }
+ Content-Length := length
+ Remove "chunked" from Transfer-Encoding
+
+19.4.7 MIME-Version
+
+ HTTP is not a MIME-compliant protocol (see appendix 19.4). However,
+ HTTP/1.1 messages may include a single MIME-Version general-header
+ field to indicate what version of the MIME protocol was used to
+ construct the message. Use of the MIME-Version header field indicates
+ that the message is in full compliance with the MIME protocol.
+ Proxies/gateways are responsible for ensuring full compliance (where
+ possible) when exporting HTTP messages to strict MIME environments.
+
+ MIME-Version = "MIME-Version" ":" 1*DIGIT "." 1*DIGIT
+
+ MIME version "1.0" is the default for use in HTTP/1.1. However,
+ HTTP/1.1 message parsing and semantics are defined by this document
+ and not the MIME specification.
+
+19.5 Changes from HTTP/1.0
+
+ This section summarizes major differences between versions HTTP/1.0
+ and HTTP/1.1.
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 154]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+19.5.1 Changes to Simplify Multi-homed Web Servers and Conserve IP
+ Addresses
+
+ The requirements that clients and servers support the Host request-
+ header, report an error if the Host request-header (section 14.23) is
+ missing from an HTTP/1.1 request, and accept absolute URIs (section
+ 5.1.2) are among the most important changes defined by this
+ specification.
+
+ Older HTTP/1.0 clients assumed a one-to-one relationship of IP
+ addresses and servers; there was no other established mechanism for
+ distinguishing the intended server of a request than the IP address
+ to which that request was directed. The changes outlined above will
+ allow the Internet, once older HTTP clients are no longer common, to
+ support multiple Web sites from a single IP address, greatly
+ simplifying large operational Web servers, where allocation of many
+ IP addresses to a single host has created serious problems. The
+ Internet will also be able to recover the IP addresses that have been
+ allocated for the sole purpose of allowing special-purpose domain
+ names to be used in root-level HTTP URLs. Given the rate of growth of
+ the Web, and the number of servers already deployed, it is extremely
+ important that all implementations of HTTP (including updates to
+ existing HTTP/1.0 applications) correctly implement these
+ requirements:
+
+ o Both clients and servers MUST support the Host request-header.
+
+ o Host request-headers are required in HTTP/1.1 requests.
+
+ o Servers MUST report a 400 (Bad Request) error if an HTTP/1.1
+ request does not include a Host request-header.
+
+ o Servers MUST accept absolute URIs.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 155]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+19.6 Additional Features
+
+ This appendix documents protocol elements used by some existing HTTP
+ implementations, but not consistently and correctly across most
+ HTTP/1.1 applications. Implementers should be aware of these
+ features, but cannot rely upon their presence in, or interoperability
+ with, other HTTP/1.1 applications. Some of these describe proposed
+ experimental features, and some describe features that experimental
+ deployment found lacking that are now addressed in the base HTTP/1.1
+ specification.
+
+19.6.1 Additional Request Methods
+
+19.6.1.1 PATCH
+
+ The PATCH method is similar to PUT except that the entity contains a
+ list of differences between the original version of the resource
+ identified by the Request-URI and the desired content of the resource
+ after the PATCH action has been applied. The list of differences is
+ in a format defined by the media type of the entity (e.g.,
+ "application/diff") and MUST include sufficient information to allow
+ the server to recreate the changes necessary to convert the original
+ version of the resource to the desired version.
+
+ If the request passes through a cache and the Request-URI identifies
+ a currently cached entity, that entity MUST be removed from the
+ cache. Responses to this method are not cachable.
+
+ The actual method for determining how the patched resource is placed,
+ and what happens to its predecessor, is defined entirely by the
+ origin server. If the original version of the resource being patched
+ included a Content-Version header field, the request entity MUST
+ include a Derived-From header field corresponding to the value of the
+ original Content-Version header field. Applications are encouraged to
+ use these fields for constructing versioning relationships and
+ resolving version conflicts.
+
+ PATCH requests must obey the message transmission requirements set
+ out in section 8.2.
+
+ Caches that implement PATCH should invalidate cached responses as
+ defined in section 13.10 for PUT.
+
+19.6.1.2 LINK
+
+ The LINK method establishes one or more Link relationships between
+ the existing resource identified by the Request-URI and other
+ existing resources. The difference between LINK and other methods
+
+
+
+Fielding, et. al. Standards Track [Page 156]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ allowing links to be established between resources is that the LINK
+ method does not allow any message-body to be sent in the request and
+ does not directly result in the creation of new resources.
+
+ If the request passes through a cache and the Request-URI identifies
+ a currently cached entity, that entity MUST be removed from the
+ cache. Responses to this method are not cachable.
+
+ Caches that implement LINK should invalidate cached responses as
+ defined in section 13.10 for PUT.
+
+19.6.1.3 UNLINK
+
+ The UNLINK method removes one or more Link relationships from the
+ existing resource identified by the Request-URI. These relationships
+ may have been established using the LINK method or by any other
+ method supporting the Link header. The removal of a link to a
+ resource does not imply that the resource ceases to exist or becomes
+ inaccessible for future references.
+
+ If the request passes through a cache and the Request-URI identifies
+ a currently cached entity, that entity MUST be removed from the
+ cache. Responses to this method are not cachable.
+
+ Caches that implement UNLINK should invalidate cached responses as
+ defined in section 13.10 for PUT.
+
+19.6.2 Additional Header Field Definitions
+
+19.6.2.1 Alternates
+
+ The Alternates response-header field has been proposed as a means for
+ the origin server to inform the client about other available
+ representations of the requested resource, along with their
+ distinguishing attributes, and thus providing a more reliable means
+ for a user agent to perform subsequent selection of another
+ representation which better fits the desires of its user (described
+ as agent-driven negotiation in section 12).
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 157]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ The Alternates header field is orthogonal to the Vary header field in
+ that both may coexist in a message without affecting the
+ interpretation of the response or the available representations. It
+ is expected that Alternates will provide a significant improvement
+ over the server-driven negotiation provided by the Vary field for
+ those resources that vary over common dimensions like type and
+ language.
+
+ The Alternates header field will be defined in a future
+ specification.
+
+19.6.2.2 Content-Version
+
+ The Content-Version entity-header field defines the version tag
+ associated with a rendition of an evolving entity. Together with the
+ Derived-From field described in section 19.6.2.3, it allows a group
+ of people to work simultaneously on the creation of a work as an
+ iterative process. The field should be used to allow evolution of a
+ particular work along a single path rather than derived works or
+ renditions in different representations.
+
+ Content-Version = "Content-Version" ":" quoted-string
+
+ Examples of the Content-Version field include:
+
+ Content-Version: "2.1.2"
+ Content-Version: "Fred 19950116-12:26:48"
+ Content-Version: "2.5a4-omega7"
+
+19.6.2.3 Derived-From
+
+ The Derived-From entity-header field can be used to indicate the
+ version tag of the resource from which the enclosed entity was
+ derived before modifications were made by the sender. This field is
+ used to help manage the process of merging successive changes to a
+ resource, particularly when such changes are being made in parallel
+ and from multiple sources.
+
+ Derived-From = "Derived-From" ":" quoted-string
+
+ An example use of the field is:
+
+ Derived-From: "2.1.1"
+
+ The Derived-From field is required for PUT and PATCH requests if the
+ entity being sent was previously retrieved from the same URI and a
+ Content-Version header was included with the entity when it was last
+ retrieved.
+
+
+
+Fielding, et. al. Standards Track [Page 158]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+19.6.2.4 Link
+
+ The Link entity-header field provides a means for describing a
+ relationship between two resources, generally between the requested
+ resource and some other resource. An entity MAY include multiple Link
+ values. Links at the metainformation level typically indicate
+ relationships like hierarchical structure and navigation paths. The
+ Link field is semantically equivalent to the <LINK> element in
+ HTML.[5]
+
+ Link = "Link" ":" #("<" URI ">" *( ";" link-param )
+
+ link-param = ( ( "rel" "=" relationship )
+ | ( "rev" "=" relationship )
+ | ( "title" "=" quoted-string )
+ | ( "anchor" "=" <"> URI <"> )
+ | ( link-extension ) )
+
+ link-extension = token [ "=" ( token | quoted-string ) ]
+
+ relationship = sgml-name
+ | ( <"> sgml-name *( SP sgml-name) <"> )
+
+ sgml-name = ALPHA *( ALPHA | DIGIT | "." | "-" )
+
+ Relationship values are case-insensitive and MAY be extended within
+ the constraints of the sgml-name syntax. The title parameter MAY be
+ used to label the destination of a link such that it can be used as
+ identification within a human-readable menu. The anchor parameter MAY
+ be used to indicate a source anchor other than the entire current
+ resource, such as a fragment of this resource or a third resource.
+
+ Examples of usage include:
+
+ Link: <http://www.cern.ch/TheBook/chapter2>; rel="Previous"
+
+ Link: <mailto:timbl@w3.org>; rev="Made"; title="Tim Berners-Lee"
+
+ The first example indicates that chapter2 is previous to this
+ resource in a logical navigation path. The second indicates that the
+ person responsible for making the resource available is identified by
+ the given e-mail address.
+
+19.6.2.5 URI
+
+ The URI header field has, in past versions of this specification,
+ been used as a combination of the existing Location, Content-
+ Location, and Vary header fields as well as the future Alternates
+
+
+
+Fielding, et. al. Standards Track [Page 159]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+ field (above). Its primary purpose has been to include a list of
+ additional URIs for the resource, including names and mirror
+ locations. However, it has become clear that the combination of many
+ different functions within this single field has been a barrier to
+ consistently and correctly implementing any of those functions.
+ Furthermore, we believe that the identification of names and mirror
+ locations would be better performed via the Link header field. The
+ URI header field is therefore deprecated in favor of those other
+ fields.
+
+ URI-header = "URI" ":" 1#( "<" URI ">" )
+
+19.7 Compatibility with Previous Versions
+
+ It is beyond the scope of a protocol specification to mandate
+ compliance with previous versions. HTTP/1.1 was deliberately
+ designed, however, to make supporting previous versions easy. It is
+ worth noting that at the time of composing this specification, we
+ would expect commercial HTTP/1.1 servers to:
+
+ o recognize the format of the Request-Line for HTTP/0.9, 1.0, and 1.1
+ requests;
+
+ o understand any valid request in the format of HTTP/0.9, 1.0, or
+ 1.1;
+
+ o respond appropriately with a message in the same major version used
+ by the client.
+
+ And we would expect HTTP/1.1 clients to:
+
+ o recognize the format of the Status-Line for HTTP/1.0 and 1.1
+ responses;
+
+ o understand any valid response in the format of HTTP/0.9, 1.0, or
+ 1.1.
+
+ For most implementations of HTTP/1.0, each connection is established
+ by the client prior to the request and closed by the server after
+ sending the response. A few implementations implement the Keep-Alive
+ version of persistent connections described in section 19.7.1.1.
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 160]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+19.7.1 Compatibility with HTTP/1.0 Persistent Connections
+
+ Some clients and servers may wish to be compatible with some previous
+ implementations of persistent connections in HTTP/1.0 clients and
+ servers. Persistent connections in HTTP/1.0 must be explicitly
+ negotiated as they are not the default behavior. HTTP/1.0
+ experimental implementations of persistent connections are faulty,
+ and the new facilities in HTTP/1.1 are designed to rectify these
+ problems. The problem was that some existing 1.0 clients may be
+ sending Keep-Alive to a proxy server that doesn't understand
+ Connection, which would then erroneously forward it to the next
+ inbound server, which would establish the Keep-Alive connection and
+ result in a hung HTTP/1.0 proxy waiting for the close on the
+ response. The result is that HTTP/1.0 clients must be prevented from
+ using Keep-Alive when talking to proxies.
+
+ However, talking to proxies is the most important use of persistent
+ connections, so that prohibition is clearly unacceptable. Therefore,
+ we need some other mechanism for indicating a persistent connection
+ is desired, which is safe to use even when talking to an old proxy
+ that ignores Connection. Persistent connections are the default for
+ HTTP/1.1 messages; we introduce a new keyword (Connection: close) for
+ declaring non-persistence.
+
+ The following describes the original HTTP/1.0 form of persistent
+ connections.
+
+ When it connects to an origin server, an HTTP client MAY send the
+ Keep-Alive connection-token in addition to the Persist connection-
+ token:
+
+ Connection: Keep-Alive
+
+ An HTTP/1.0 server would then respond with the Keep-Alive connection
+ token and the client may proceed with an HTTP/1.0 (or Keep-Alive)
+ persistent connection.
+
+ An HTTP/1.1 server may also establish persistent connections with
+ HTTP/1.0 clients upon receipt of a Keep-Alive connection token.
+ However, a persistent connection with an HTTP/1.0 client cannot make
+ use of the chunked transfer-coding, and therefore MUST use a
+ Content-Length for marking the ending boundary of each message.
+
+ A client MUST NOT send the Keep-Alive connection token to a proxy
+ server as HTTP/1.0 proxy servers do not obey the rules of HTTP/1.1
+ for parsing the Connection header field.
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 161]
+\f
+RFC 2068 HTTP/1.1 January 1997
+
+
+19.7.1.1 The Keep-Alive Header
+
+ When the Keep-Alive connection-token has been transmitted with a
+ request or a response, a Keep-Alive header field MAY also be
+ included. The Keep-Alive header field takes the following form:
+
+ Keep-Alive-header = "Keep-Alive" ":" 0# keepalive-param
+
+ keepalive-param = param-name "=" value
+
+ The Keep-Alive header itself is optional, and is used only if a
+ parameter is being sent. HTTP/1.1 does not define any parameters.
+
+ If the Keep-Alive header is sent, the corresponding connection token
+ MUST be transmitted. The Keep-Alive header MUST be ignored if
+ received without the connection token.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et. al. Standards Track [Page 162]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group D. Kristol
+Request for Comments: 2109 Bell Laboratories, Lucent Technologies
+Category: Standards Track L. Montulli
+ Netscape Communications
+ February 1997
+
+
+ HTTP State Management Mechanism
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+1. ABSTRACT
+
+ This document specifies a way to create a stateful session with HTTP
+ requests and responses. It describes two new headers, Cookie and
+ Set-Cookie, which carry state information between participating
+ origin servers and user agents. The method described here differs
+ from Netscape's Cookie proposal, but it can interoperate with
+ HTTP/1.0 user agents that use Netscape's method. (See the HISTORICAL
+ section.)
+
+2. TERMINOLOGY
+
+ The terms user agent, client, server, proxy, and origin server have
+ the same meaning as in the HTTP/1.0 specification.
+
+ Fully-qualified host name (FQHN) means either the fully-qualified
+ domain name (FQDN) of a host (i.e., a completely specified domain
+ name ending in a top-level domain such as .com or .uk), or the
+ numeric Internet Protocol (IP) address of a host. The fully
+ qualified domain name is preferred; use of numeric IP addresses is
+ strongly discouraged.
+
+ The terms request-host and request-URI refer to the values the client
+ would send to the server as, respectively, the host (but not port)
+ and abs_path portions of the absoluteURI (http_URL) of the HTTP
+ request line. Note that request-host must be a FQHN.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 1]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ Hosts names can be specified either as an IP address or a FQHN
+ string. Sometimes we compare one host name with another. Host A's
+ name domain-matches host B's if
+
+ * both host names are IP addresses and their host name strings match
+ exactly; or
+
+ * both host names are FQDN strings and their host name strings match
+ exactly; or
+
+ * A is a FQDN string and has the form NB, where N is a non-empty name
+ string, B has the form .B', and B' is a FQDN string. (So, x.y.com
+ domain-matches .y.com but not y.com.)
+
+ Note that domain-match is not a commutative operation: a.b.c.com
+ domain-matches .c.com, but not the reverse.
+
+ Because it was used in Netscape's original implementation of state
+ management, we will use the term cookie to refer to the state
+ information that passes between an origin server and user agent, and
+ that gets stored by the user agent.
+
+3. STATE AND SESSIONS
+
+ This document describes a way to create stateful sessions with HTTP
+ requests and responses. Currently, HTTP servers respond to each
+ client request without relating that request to previous or
+ subsequent requests; the technique allows clients and servers that
+ wish to exchange state information to place HTTP requests and
+ responses within a larger context, which we term a "session". This
+ context might be used to create, for example, a "shopping cart", in
+ which user selections can be aggregated before purchase, or a
+ magazine browsing system, in which a user's previous reading affects
+ which offerings are presented.
+
+ There are, of course, many different potential contexts and thus many
+ different potential types of session. The designers' paradigm for
+ sessions created by the exchange of cookies has these key attributes:
+
+ 1. Each session has a beginning and an end.
+
+ 2. Each session is relatively short-lived.
+
+ 3. Either the user agent or the origin server may terminate a
+ session.
+
+ 4. The session is implicit in the exchange of state information.
+
+
+
+
+Kristol & Montulli Standards Track [Page 2]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+4. OUTLINE
+
+ We outline here a way for an origin server to send state information
+ to the user agent, and for the user agent to return the state
+ information to the origin server. The goal is to have a minimal
+ impact on HTTP and user agents. Only origin servers that need to
+ maintain sessions would suffer any significant impact, and that
+ impact can largely be confined to Common Gateway Interface (CGI)
+ programs, unless the server provides more sophisticated state
+ management support. (See Implementation Considerations, below.)
+
+4.1 Syntax: General
+
+ The two state management headers, Set-Cookie and Cookie, have common
+ syntactic properties involving attribute-value pairs. The following
+ grammar uses the notation, and tokens DIGIT (decimal digits) and
+ token (informally, a sequence of non-special, non-white space
+ characters) from the HTTP/1.1 specification [RFC 2068] to describe
+ their syntax.
+
+ av-pairs = av-pair *(";" av-pair)
+ av-pair = attr ["=" value] ; optional value
+ attr = token
+ value = word
+ word = token | quoted-string
+
+ Attributes (names) (attr) are case-insensitive. White space is
+ permitted between tokens. Note that while the above syntax
+ description shows value as optional, most attrs require them.
+
+ NOTE: The syntax above allows whitespace between the attribute and
+ the = sign.
+
+4.2 Origin Server Role
+
+4.2.1 General
+
+ The origin server initiates a session, if it so desires. (Note that
+ "session" here does not refer to a persistent network connection but
+ to a logical session created from HTTP requests and responses. The
+ presence or absence of a persistent connection should have no effect
+ on the use of cookie-derived sessions). To initiate a session, the
+ origin server returns an extra response header to the client, Set-
+ Cookie. (The details follow later.)
+
+ A user agent returns a Cookie request header (see below) to the
+ origin server if it chooses to continue a session. The origin server
+ may ignore it or use it to determine the current state of the
+
+
+
+Kristol & Montulli Standards Track [Page 3]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ session. It may send back to the client a Set-Cookie response header
+ with the same or different information, or it may send no Set-Cookie
+ header at all. The origin server effectively ends a session by
+ sending the client a Set-Cookie header with Max-Age=0.
+
+ Servers may return a Set-Cookie response headers with any response.
+ User agents should send Cookie request headers, subject to other
+ rules detailed below, with every request.
+
+ An origin server may include multiple Set-Cookie headers in a
+ response. Note that an intervening gateway could fold multiple such
+ headers into a single header.
+
+4.2.2 Set-Cookie Syntax
+
+ The syntax for the Set-Cookie response header is
+
+ set-cookie = "Set-Cookie:" cookies
+ cookies = 1#cookie
+ cookie = NAME "=" VALUE *(";" cookie-av)
+ NAME = attr
+ VALUE = value
+ cookie-av = "Comment" "=" value
+ | "Domain" "=" value
+ | "Max-Age" "=" value
+ | "Path" "=" value
+ | "Secure"
+ | "Version" "=" 1*DIGIT
+
+ Informally, the Set-Cookie response header comprises the token Set-
+ Cookie:, followed by a comma-separated list of one or more cookies.
+ Each cookie begins with a NAME=VALUE pair, followed by zero or more
+ semi-colon-separated attribute-value pairs. The syntax for
+ attribute-value pairs was shown earlier. The specific attributes and
+ the semantics of their values follows. The NAME=VALUE attribute-
+ value pair must come first in each cookie. The others, if present,
+ can occur in any order. If an attribute appears more than once in a
+ cookie, the behavior is undefined.
+
+ NAME=VALUE
+ Required. The name of the state information ("cookie") is NAME,
+ and its value is VALUE. NAMEs that begin with $ are reserved for
+ other uses and must not be used by applications.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 4]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ The VALUE is opaque to the user agent and may be anything the
+ origin server chooses to send, possibly in a server-selected
+ printable ASCII encoding. "Opaque" implies that the content is of
+ interest and relevance only to the origin server. The content
+ may, in fact, be readable by anyone that examines the Set-Cookie
+ header.
+
+ Comment=comment
+ Optional. Because cookies can contain private information about a
+ user, the Cookie attribute allows an origin server to document its
+ intended use of a cookie. The user can inspect the information to
+ decide whether to initiate or continue a session with this cookie.
+
+ Domain=domain
+ Optional. The Domain attribute specifies the domain for which the
+ cookie is valid. An explicitly specified domain must always start
+ with a dot.
+
+ Max-Age=delta-seconds
+ Optional. The Max-Age attribute defines the lifetime of the
+ cookie, in seconds. The delta-seconds value is a decimal non-
+ negative integer. After delta-seconds seconds elapse, the client
+ should discard the cookie. A value of zero means the cookie
+ should be discarded immediately.
+
+ Path=path
+ Optional. The Path attribute specifies the subset of URLs to
+ which this cookie applies.
+
+ Secure
+ Optional. The Secure attribute (with no value) directs the user
+ agent to use only (unspecified) secure means to contact the origin
+ server whenever it sends back this cookie.
+
+ The user agent (possibly under the user's control) may determine
+ what level of security it considers appropriate for "secure"
+ cookies. The Secure attribute should be considered security
+ advice from the server to the user agent, indicating that it is in
+ the session's interest to protect the cookie contents.
+
+ Version=version
+ Required. The Version attribute, a decimal integer, identifies to
+ which version of the state management specification the cookie
+ conforms. For this specification, Version=1 applies.
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 5]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+4.2.3 Controlling Caching
+
+ An origin server must be cognizant of the effect of possible caching
+ of both the returned resource and the Set-Cookie header. Caching
+ "public" documents is desirable. For example, if the origin server
+ wants to use a public document such as a "front door" page as a
+ sentinel to indicate the beginning of a session for which a Set-
+ Cookie response header must be generated, the page should be stored
+ in caches "pre-expired" so that the origin server will see further
+ requests. "Private documents", for example those that contain
+ information strictly private to a session, should not be cached in
+ shared caches.
+
+ If the cookie is intended for use by a single user, the Set-cookie
+ header should not be cached. A Set-cookie header that is intended to
+ be shared by multiple users may be cached.
+
+ The origin server should send the following additional HTTP/1.1
+ response headers, depending on circumstances:
+
+ * To suppress caching of the Set-Cookie header: Cache-control: no-
+ cache="set-cookie".
+
+ and one of the following:
+
+ * To suppress caching of a private document in shared caches: Cache-
+ control: private.
+
+ * To allow caching of a document and require that it be validated
+ before returning it to the client: Cache-control: must-revalidate.
+
+ * To allow caching of a document, but to require that proxy caches
+ (not user agent caches) validate it before returning it to the
+ client: Cache-control: proxy-revalidate.
+
+ * To allow caching of a document and request that it be validated
+ before returning it to the client (by "pre-expiring" it):
+ Cache-control: max-age=0. Not all caches will revalidate the
+ document in every case.
+
+ HTTP/1.1 servers must send Expires: old-date (where old-date is a
+ date long in the past) on responses containing Set-Cookie response
+ headers unless they know for certain (by out of band means) that
+ there are no downsteam HTTP/1.0 proxies. HTTP/1.1 servers may send
+ other Cache-Control directives that permit caching by HTTP/1.1
+ proxies in addition to the Expires: old-date directive; the Cache-
+ Control directive will override the Expires: old-date for HTTP/1.1
+ proxies.
+
+
+
+Kristol & Montulli Standards Track [Page 6]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+4.3 User Agent Role
+
+4.3.1 Interpreting Set-Cookie
+
+ The user agent keeps separate track of state information that arrives
+ via Set-Cookie response headers from each origin server (as
+ distinguished by name or IP address and port). The user agent
+ applies these defaults for optional attributes that are missing:
+
+ VersionDefaults to "old cookie" behavior as originally specified by
+ Netscape. See the HISTORICAL section.
+
+ Domain Defaults to the request-host. (Note that there is no dot at
+ the beginning of request-host.)
+
+ Max-AgeThe default behavior is to discard the cookie when the user
+ agent exits.
+
+ Path Defaults to the path of the request URL that generated the
+ Set-Cookie response, up to, but not including, the
+ right-most /.
+
+ Secure If absent, the user agent may send the cookie over an
+ insecure channel.
+
+4.3.2 Rejecting Cookies
+
+ To prevent possible security or privacy violations, a user agent
+ rejects a cookie (shall not store its information) if any of the
+ following is true:
+
+ * The value for the Path attribute is not a prefix of the request-
+ URI.
+
+ * The value for the Domain attribute contains no embedded dots or
+ does not start with a dot.
+
+ * The value for the request-host does not domain-match the Domain
+ attribute.
+
+ * The request-host is a FQDN (not IP address) and has the form HD,
+ where D is the value of the Domain attribute, and H is a string
+ that contains one or more dots.
+
+ Examples:
+
+ * A Set-Cookie from request-host y.x.foo.com for Domain=.foo.com
+ would be rejected, because H is y.x and contains a dot.
+
+
+
+Kristol & Montulli Standards Track [Page 7]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ * A Set-Cookie from request-host x.foo.com for Domain=.foo.com would
+ be accepted.
+
+ * A Set-Cookie with Domain=.com or Domain=.com., will always be
+ rejected, because there is no embedded dot.
+
+ * A Set-Cookie with Domain=ajax.com will be rejected because the
+ value for Domain does not begin with a dot.
+
+4.3.3 Cookie Management
+
+ If a user agent receives a Set-Cookie response header whose NAME is
+ the same as a pre-existing cookie, and whose Domain and Path
+ attribute values exactly (string) match those of a pre-existing
+ cookie, the new cookie supersedes the old. However, if the Set-
+ Cookie has a value for Max-Age of zero, the (old and new) cookie is
+ discarded. Otherwise cookies accumulate until they expire (resources
+ permitting), at which time they are discarded.
+
+ Because user agents have finite space in which to store cookies, they
+ may also discard older cookies to make space for newer ones, using,
+ for example, a least-recently-used algorithm, along with constraints
+ on the maximum number of cookies that each origin server may set.
+
+ If a Set-Cookie response header includes a Comment attribute, the
+ user agent should store that information in a human-readable form
+ with the cookie and should display the comment text as part of a
+ cookie inspection user interface.
+
+ User agents should allow the user to control cookie destruction. An
+ infrequently-used cookie may function as a "preferences file" for
+ network applications, and a user may wish to keep it even if it is
+ the least-recently-used cookie. One possible implementation would be
+ an interface that allows the permanent storage of a cookie through a
+ checkbox (or, conversely, its immediate destruction).
+
+ Privacy considerations dictate that the user have considerable
+ control over cookie management. The PRIVACY section contains more
+ information.
+
+4.3.4 Sending Cookies to the Origin Server
+
+ When it sends a request to an origin server, the user agent sends a
+ Cookie request header to the origin server if it has cookies that are
+ applicable to the request, based on
+
+ * the request-host;
+
+
+
+
+Kristol & Montulli Standards Track [Page 8]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ * the request-URI;
+
+ * the cookie's age.
+
+ The syntax for the header is:
+
+ cookie = "Cookie:" cookie-version
+ 1*((";" | ",") cookie-value)
+ cookie-value = NAME "=" VALUE [";" path] [";" domain]
+ cookie-version = "$Version" "=" value
+ NAME = attr
+ VALUE = value
+ path = "$Path" "=" value
+ domain = "$Domain" "=" value
+
+ The value of the cookie-version attribute must be the value from the
+ Version attribute, if any, of the corresponding Set-Cookie response
+ header. Otherwise the value for cookie-version is 0. The value for
+ the path attribute must be the value from the Path attribute, if any,
+ of the corresponding Set-Cookie response header. Otherwise the
+ attribute should be omitted from the Cookie request header. The
+ value for the domain attribute must be the value from the Domain
+ attribute, if any, of the corresponding Set-Cookie response header.
+ Otherwise the attribute should be omitted from the Cookie request
+ header.
+
+ Note that there is no Comment attribute in the Cookie request header
+ corresponding to the one in the Set-Cookie response header. The user
+ agent does not return the comment information to the origin server.
+
+ The following rules apply to choosing applicable cookie-values from
+ among all the cookies the user agent has.
+
+ Domain Selection
+ The origin server's fully-qualified host name must domain-match
+ the Domain attribute of the cookie.
+
+ Path Selection
+ The Path attribute of the cookie must match a prefix of the
+ request-URI.
+
+ Max-Age Selection
+ Cookies that have expired should have been discarded and thus
+ are not forwarded to an origin server.
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 9]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ If multiple cookies satisfy the criteria above, they are ordered in
+ the Cookie header such that those with more specific Path attributes
+ precede those with less specific. Ordering with respect to other
+ attributes (e.g., Domain) is unspecified.
+
+ Note: For backward compatibility, the separator in the Cookie header
+ is semi-colon (;) everywhere. A server should also accept comma (,)
+ as the separator between cookie-values for future compatibility.
+
+4.3.5 Sending Cookies in Unverifiable Transactions
+
+ Users must have control over sessions in order to ensure privacy.
+ (See PRIVACY section below.) To simplify implementation and to
+ prevent an additional layer of complexity where adequate safeguards
+ exist, however, this document distinguishes between transactions that
+ are verifiable and those that are unverifiable. A transaction is
+ verifiable if the user has the option to review the request-URI prior
+ to its use in the transaction. A transaction is unverifiable if the
+ user does not have that option. Unverifiable transactions typically
+ arise when a user agent automatically requests inlined or embedded
+ entities or when it resolves redirection (3xx) responses from an
+ origin server. Typically the origin transaction, the transaction
+ that the user initiates, is verifiable, and that transaction may
+ directly or indirectly induce the user agent to make unverifiable
+ transactions.
+
+ When it makes an unverifiable transaction, a user agent must enable a
+ session only if a cookie with a domain attribute D was sent or
+ received in its origin transaction, such that the host name in the
+ Request-URI of the unverifiable transaction domain-matches D.
+
+ This restriction prevents a malicious service author from using
+ unverifiable transactions to induce a user agent to start or continue
+ a session with a server in a different domain. The starting or
+ continuation of such sessions could be contrary to the privacy
+ expectations of the user, and could also be a security problem.
+
+ User agents may offer configurable options that allow the user agent,
+ or any autonomous programs that the user agent executes, to ignore
+ the above rule, so long as these override options default to "off".
+
+ Many current user agents already provide a review option that would
+ render many links verifiable. For instance, some user agents display
+ the URL that would be referenced for a particular link when the mouse
+ pointer is placed over that link. The user can therefore determine
+ whether to visit that site before causing the browser to do so.
+ (Though not implemented on current user agents, a similar technique
+ could be used for a button used to submit a form -- the user agent
+
+
+
+Kristol & Montulli Standards Track [Page 10]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ could display the action to be taken if the user were to select that
+ button.) However, even this would not make all links verifiable; for
+ example, links to automatically loaded images would not normally be
+ subject to "mouse pointer" verification.
+
+ Many user agents also provide the option for a user to view the HTML
+ source of a document, or to save the source to an external file where
+ it can be viewed by another application. While such an option does
+ provide a crude review mechanism, some users might not consider it
+ acceptable for this purpose.
+
+4.4 How an Origin Server Interprets the Cookie Header
+
+ A user agent returns much of the information in the Set-Cookie header
+ to the origin server when the Path attribute matches that of a new
+ request. When it receives a Cookie header, the origin server should
+ treat cookies with NAMEs whose prefix is $ specially, as an attribute
+ for the adjacent cookie. The value for such a NAME is to be
+ interpreted as applying to the lexically (left-to-right) most recent
+ cookie whose name does not have the $ prefix. If there is no
+ previous cookie, the value applies to the cookie mechanism as a
+ whole. For example, consider the cookie
+
+ Cookie: $Version="1"; Customer="WILE_E_COYOTE";
+ $Path="/acme"
+
+ $Version applies to the cookie mechanism as a whole (and gives the
+ version number for the cookie mechanism). $Path is an attribute
+ whose value (/acme) defines the Path attribute that was used when the
+ Customer cookie was defined in a Set-Cookie response header.
+
+4.5 Caching Proxy Role
+
+ One reason for separating state information from both a URL and
+ document content is to facilitate the scaling that caching permits.
+ To support cookies, a caching proxy must obey these rules already in
+ the HTTP specification:
+
+ * Honor requests from the cache, if possible, based on cache validity
+ rules.
+
+ * Pass along a Cookie request header in any request that the proxy
+ must make of another server.
+
+ * Return the response to the client. Include any Set-Cookie response
+ header.
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 11]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ * Cache the received response subject to the control of the usual
+ headers, such as Expires, Cache-control: no-cache, and Cache-
+ control: private,
+
+ * Cache the Set-Cookie subject to the control of the usual header,
+ Cache-control: no-cache="set-cookie". (The Set-Cookie header
+ should usually not be cached.)
+
+ Proxies must not introduce Set-Cookie (Cookie) headers of their own
+ in proxy responses (requests).
+
+5. EXAMPLES
+
+5.1 Example 1
+
+ Most detail of request and response headers has been omitted. Assume
+ the user agent has no stored cookies.
+
+ 1. User Agent -> Server
+
+ POST /acme/login HTTP/1.1
+ [form data]
+
+ User identifies self via a form.
+
+ 2. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"
+
+ Cookie reflects user's identity.
+
+ 3. User Agent -> Server
+
+ POST /acme/pickitem HTTP/1.1
+ Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
+ [form data]
+
+ User selects an item for "shopping basket."
+
+ 4. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";
+ Path="/acme"
+
+ Shopping basket contains an item.
+
+
+
+
+Kristol & Montulli Standards Track [Page 12]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ 5. User Agent -> Server
+
+ POST /acme/shipping HTTP/1.1
+ Cookie: $Version="1";
+ Customer="WILE_E_COYOTE"; $Path="/acme";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+ [form data]
+
+ User selects shipping method from form.
+
+ 6. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie: Shipping="FedEx"; Version="1"; Path="/acme"
+
+ New cookie reflects shipping method.
+
+ 7. User Agent -> Server
+
+ POST /acme/process HTTP/1.1
+ Cookie: $Version="1";
+ Customer="WILE_E_COYOTE"; $Path="/acme";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme";
+ Shipping="FedEx"; $Path="/acme"
+ [form data]
+
+ User chooses to process order.
+
+ 8. Server -> User Agent
+
+ HTTP/1.1 200 OK
+
+ Transaction is complete.
+
+ The user agent makes a series of requests on the origin server, after
+ each of which it receives a new cookie. All the cookies have the
+ same Path attribute and (default) domain. Because the request URLs
+ all have /acme as a prefix, and that matches the Path attribute, each
+ request contains all the cookies received so far.
+
+5.2 Example 2
+
+ This example illustrates the effect of the Path attribute. All
+ detail of request and response headers has been omitted. Assume the
+ user agent has no stored cookies.
+
+ Imagine the user agent has received, in response to earlier requests,
+ the response headers
+
+
+
+Kristol & Montulli Standards Track [Page 13]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";
+ Path="/acme"
+
+ and
+
+ Set-Cookie: Part_Number="Riding_Rocket_0023"; Version="1";
+ Path="/acme/ammo"
+
+ A subsequent request by the user agent to the (same) server for URLs
+ of the form /acme/ammo/... would include the following request
+ header:
+
+ Cookie: $Version="1";
+ Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+
+ Note that the NAME=VALUE pair for the cookie with the more specific
+ Path attribute, /acme/ammo, comes before the one with the less
+ specific Path attribute, /acme. Further note that the same cookie
+ name appears more than once.
+
+ A subsequent request by the user agent to the (same) server for a URL
+ of the form /acme/parts/ would include the following request header:
+
+ Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+
+ Here, the second cookie's Path attribute /acme/ammo is not a prefix
+ of the request URL, /acme/parts/, so the cookie does not get
+ forwarded to the server.
+
+6. IMPLEMENTATION CONSIDERATIONS
+
+ Here we speculate on likely or desirable details for an origin server
+ that implements state management.
+
+6.1 Set-Cookie Content
+
+ An origin server's content should probably be divided into disjoint
+ application areas, some of which require the use of state
+ information. The application areas can be distinguished by their
+ request URLs. The Set-Cookie header can incorporate information
+ about the application areas by setting the Path attribute for each
+ one.
+
+ The session information can obviously be clear or encoded text that
+ describes state. However, if it grows too large, it can become
+ unwieldy. Therefore, an implementor might choose for the session
+ information to be a key to a server-side resource. Of course, using
+
+
+
+Kristol & Montulli Standards Track [Page 14]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ a database creates some problems that this state management
+ specification was meant to avoid, namely:
+
+ 1. keeping real state on the server side;
+
+ 2. how and when to garbage-collect the database entry, in case the
+ user agent terminates the session by, for example, exiting.
+
+6.2 Stateless Pages
+
+ Caching benefits the scalability of WWW. Therefore it is important
+ to reduce the number of documents that have state embedded in them
+ inherently. For example, if a shopping-basket-style application
+ always displays a user's current basket contents on each page, those
+ pages cannot be cached, because each user's basket's contents would
+ be different. On the other hand, if each page contains just a link
+ that allows the user to "Look at My Shopping Basket", the page can be
+ cached.
+
+6.3 Implementation Limits
+
+ Practical user agent implementations have limits on the number and
+ size of cookies that they can store. In general, user agents' cookie
+ support should have no fixed limits. They should strive to store as
+ many frequently-used cookies as possible. Furthermore, general-use
+ user agents should provide each of the following minimum capabilities
+ individually, although not necessarily simultaneously:
+
+ * at least 300 cookies
+
+ * at least 4096 bytes per cookie (as measured by the size of the
+ characters that comprise the cookie non-terminal in the syntax
+ description of the Set-Cookie header)
+
+ * at least 20 cookies per unique host or domain name
+
+ User agents created for specific purposes or for limited-capacity
+ devices should provide at least 20 cookies of 4096 bytes, to ensure
+ that the user can interact with a session-based origin server.
+
+ The information in a Set-Cookie response header must be retained in
+ its entirety. If for some reason there is inadequate space to store
+ the cookie, it must be discarded, not truncated.
+
+ Applications should use as few and as small cookies as possible, and
+ they should cope gracefully with the loss of a cookie.
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 15]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+6.3.1 Denial of Service Attacks
+
+ User agents may choose to set an upper bound on the number of cookies
+ to be stored from a given host or domain name or on the size of the
+ cookie information. Otherwise a malicious server could attempt to
+ flood a user agent with many cookies, or large cookies, on successive
+ responses, which would force out cookies the user agent had received
+ from other servers. However, the minima specified above should still
+ be supported.
+
+7. PRIVACY
+
+7.1 User Agent Control
+
+ An origin server could create a Set-Cookie header to track the path
+ of a user through the server. Users may object to this behavior as
+ an intrusive accumulation of information, even if their identity is
+ not evident. (Identity might become evident if a user subsequently
+ fills out a form that contains identifying information.) This state
+ management specification therefore requires that a user agent give
+ the user control over such a possible intrusion, although the
+ interface through which the user is given this control is left
+ unspecified. However, the control mechanisms provided shall at least
+ allow the user
+
+ * to completely disable the sending and saving of cookies.
+
+ * to determine whether a stateful session is in progress.
+
+ * to control the saving of a cookie on the basis of the cookie's
+ Domain attribute.
+
+ Such control could be provided by, for example, mechanisms
+
+ * to notify the user when the user agent is about to send a cookie
+ to the origin server, offering the option not to begin a session.
+
+ * to display a visual indication that a stateful session is in
+ progress.
+
+ * to let the user decide which cookies, if any, should be saved
+ when the user concludes a window or user agent session.
+
+ * to let the user examine the contents of a cookie at any time.
+
+ A user agent usually begins execution with no remembered state
+ information. It should be possible to configure a user agent never
+ to send Cookie headers, in which case it can never sustain state with
+
+
+
+Kristol & Montulli Standards Track [Page 16]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ an origin server. (The user agent would then behave like one that is
+ unaware of how to handle Set-Cookie response headers.)
+
+ When the user agent terminates execution, it should let the user
+ discard all state information. Alternatively, the user agent may ask
+ the user whether state information should be retained; the default
+ should be "no". If the user chooses to retain state information, it
+ would be restored the next time the user agent runs.
+
+ NOTE: User agents should probably be cautious about using files to
+ store cookies long-term. If a user runs more than one instance of
+ the user agent, the cookies could be commingled or otherwise messed
+ up.
+
+7.2 Protocol Design
+
+ The restrictions on the value of the Domain attribute, and the rules
+ concerning unverifiable transactions, are meant to reduce the ways
+ that cookies can "leak" to the "wrong" site. The intent is to
+ restrict cookies to one, or a closely related set of hosts.
+ Therefore a request-host is limited as to what values it can set for
+ Domain. We consider it acceptable for hosts host1.foo.com and
+ host2.foo.com to share cookies, but not a.com and b.com.
+
+ Similarly, a server can only set a Path for cookies that are related
+ to the request-URI.
+
+8. SECURITY CONSIDERATIONS
+
+8.1 Clear Text
+
+ The information in the Set-Cookie and Cookie headers is unprotected.
+ Two consequences are:
+
+ 1. Any sensitive information that is conveyed in them is exposed
+ to intruders.
+
+ 2. A malicious intermediary could alter the headers as they travel
+ in either direction, with unpredictable results.
+
+ These facts imply that information of a personal and/or financial
+ nature should only be sent over a secure channel. For less sensitive
+ information, or when the content of the header is a database key, an
+ origin server should be vigilant to prevent a bad Cookie value from
+ causing failures.
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 17]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+8.2 Cookie Spoofing
+
+ Proper application design can avoid spoofing attacks from related
+ domains. Consider:
+
+ 1. User agent makes request to victim.cracker.edu, gets back
+ cookie session_id="1234" and sets the default domain
+ victim.cracker.edu.
+
+ 2. User agent makes request to spoof.cracker.edu, gets back
+ cookie session-id="1111", with Domain=".cracker.edu".
+
+ 3. User agent makes request to victim.cracker.edu again, and
+ passes
+
+ Cookie: $Version="1";
+ session_id="1234";
+ session_id="1111"; $Domain=".cracker.edu"
+
+ The server at victim.cracker.edu should detect that the second
+ cookie was not one it originated by noticing that the Domain
+ attribute is not for itself and ignore it.
+
+8.3 Unexpected Cookie Sharing
+
+ A user agent should make every attempt to prevent the sharing of
+ session information between hosts that are in different domains.
+ Embedded or inlined objects may cause particularly severe privacy
+ problems if they can be used to share cookies between disparate
+ hosts. For example, a malicious server could embed cookie
+ information for host a.com in a URI for a CGI on host b.com. User
+ agent implementors are strongly encouraged to prevent this sort of
+ exchange whenever possible.
+
+9. OTHER, SIMILAR, PROPOSALS
+
+ Three other proposals have been made to accomplish similar goals.
+ This specification is an amalgam of Kristol's State-Info proposal and
+ Netscape's Cookie proposal.
+
+ Brian Behlendorf proposed a Session-ID header that would be user-
+ agent-initiated and could be used by an origin server to track
+ "clicktrails". It would not carry any origin-server-defined state,
+ however. Phillip Hallam-Baker has proposed another client-defined
+ session ID mechanism for similar purposes.
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 18]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ While both session IDs and cookies can provide a way to sustain
+ stateful sessions, their intended purpose is different, and,
+ consequently, the privacy requirements for them are different. A
+ user initiates session IDs to allow servers to track progress through
+ them, or to distinguish multiple users on a shared machine. Cookies
+ are server-initiated, so the cookie mechanism described here gives
+ users control over something that would otherwise take place without
+ the users' awareness. Furthermore, cookies convey rich, server-
+ selected information, whereas session IDs comprise user-selected,
+ simple information.
+
+10. HISTORICAL
+
+10.1 Compatibility With Netscape's Implementation
+
+ HTTP/1.0 clients and servers may use Set-Cookie and Cookie headers
+ that reflect Netscape's original cookie proposal. These notes cover
+ inter-operation between "old" and "new" cookies.
+
+10.1.1 Extended Cookie Header
+
+ This proposal adds attribute-value pairs to the Cookie request header
+ in a compatible way. An "old" client that receives a "new" cookie
+ will ignore attributes it does not understand; it returns what it
+ does understand to the origin server. A "new" client always sends
+ cookies in the new form.
+
+ An "old" server that receives a "new" cookie will see what it thinks
+ are many cookies with names that begin with a $, and it will ignore
+ them. (The "old" server expects these cookies to be separated by
+ semi-colon, not comma.) A "new" server can detect cookies that have
+ passed through an "old" client, because they lack a $Version
+ attribute.
+
+10.1.2 Expires and Max-Age
+
+ Netscape's original proposal defined an Expires header that took a
+ date value in a fixed-length variant format in place of Max-Age:
+
+ Wdy, DD-Mon-YY HH:MM:SS GMT
+
+ Note that the Expires date format contains embedded spaces, and that
+ "old" cookies did not have quotes around values. Clients that
+ implement to this specification should be aware of "old" cookies and
+ Expires.
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 19]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+10.1.3 Punctuation
+
+ In Netscape's original proposal, the values in attribute-value pairs
+ did not accept "-quoted strings. Origin servers should be cautious
+ about sending values that require quotes unless they know the
+ receiving user agent understands them (i.e., "new" cookies). A
+ ("new") user agent should only use quotes around values in Cookie
+ headers when the cookie's version(s) is (are) all compliant with this
+ specification or later.
+
+ In Netscape's original proposal, no whitespace was permitted around
+ the = that separates attribute-value pairs. Therefore such
+ whitespace should be used with caution in new implementations.
+
+10.2 Caching and HTTP/1.0
+
+ Some caches, such as those conforming to HTTP/1.0, will inevitably
+ cache the Set-Cookie header, because there was no mechanism to
+ suppress caching of headers prior to HTTP/1.1. This caching can lead
+ to security problems. Documents transmitted by an origin server
+ along with Set-Cookie headers will usually either be uncachable, or
+ will be "pre-expired". As long as caches obey instructions not to
+ cache documents (following Expires: <a date in the past> or Pragma:
+ no-cache (HTTP/1.0), or Cache-control: no-cache (HTTP/1.1))
+ uncachable documents present no problem. However, pre-expired
+ documents may be stored in caches. They require validation (a
+ conditional GET) on each new request, but some cache operators loosen
+ the rules for their caches, and sometimes serve expired documents
+ without first validating them. This combination of factors can lead
+ to cookies meant for one user later being sent to another user. The
+ Set-Cookie header is stored in the cache, and, although the document
+ is stale (expired), the cache returns the document in response to
+ later requests, including cached headers.
+
+11. ACKNOWLEDGEMENTS
+
+ This document really represents the collective efforts of the
+ following people, in addition to the authors: Roy Fielding, Marc
+ Hedlund, Ted Hardie, Koen Holtman, Shel Kaphan, Rohit Khare.
+
+
+
+
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 20]
+\f
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+12. AUTHORS' ADDRESSES
+
+ David M. Kristol
+ Bell Laboratories, Lucent Technologies
+ 600 Mountain Ave. Room 2A-227
+ Murray Hill, NJ 07974
+
+ Phone: (908) 582-2250
+ Fax: (908) 582-5809
+ EMail: dmk@bell-labs.com
+
+
+ Lou Montulli
+ Netscape Communications Corp.
+ 501 E. Middlefield Rd.
+ Mountain View, CA 94043
+
+ Phone: (415) 528-2600
+ EMail: montulli@netscape.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 21]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group J. C. Mogul
+Request for Comments: 2145 DEC
+Category: Informational R. Fielding
+ UC Irvine
+ J. Gettys
+ DEC
+ H. Frystyk
+ MIT/LCS
+ May 1997
+
+ Use and Interpretation of
+ HTTP Version Numbers
+
+Status of this Memo
+
+ This memo provides information for the Internet community. This memo
+ does not specify an Internet standard of any kind. Distribution of
+ this memo is unlimited.
+
+ Distribution of this document is unlimited. Please send comments to
+ the HTTP working group at <http-wg@cuckoo.hpl.hp.com>. Discussions
+ of the working group are archived at
+ <URL:http://www.ics.uci.edu/pub/ietf/http/>. General discussions
+ about HTTP and the applications which use HTTP should take place on
+ the <www-talk@w3.org> mailing list.
+
+Abstract
+
+ HTTP request and response messages include an HTTP protocol version
+ number. Some confusion exists concerning the proper use and
+ interpretation of HTTP version numbers, and concerning
+ interoperability of HTTP implementations of different protocol
+ versions. This document is an attempt to clarify the situation. It
+ is not a modification of the intended meaning of the existing
+ HTTP/1.0 and HTTP/1.1 documents, but it does describe the intention
+ of the authors of those documents, and can be considered definitive
+ when there is any ambiguity in those documents concerning HTTP
+ version numbers, for all versions of HTTP.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mogul, et. al. Informational [Page 1]
+\f
+RFC 2145 HTTP Version Numbers May 1997
+
+
+TABLE OF CONTENTS
+
+ 1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 2
+ 1.1 Robustness Principle . . . . . . . . . . . . . . . . . . 3
+ 2 HTTP version numbers. . . . . . . . . . . . . . . . . . . . . . 3
+ 2.1 Proxy behavior. . . . . . . . . . . . . . . . . . . . . . . . 4
+ 2.2 Compatibility between minor versions of the same major
+ version. . . . . . . . . . . . . . . . . . . . . . . . 4
+ 2.3 Which version number to send in a message. . . . . . . . 5
+ 3 Security Considerations . . . . . . . . . . . . . . . . . . . . 6
+ 4 References. . . . . . . . . . . . . . . . . . . . . . . . . . . 6
+ 5 Authors' addresses. . . . . . . . . . . . . . . . . . . . . . . 6
+
+1 Introduction
+
+ HTTP request and response messages include an HTTP protocol version
+ number. According to section 3.1 of the HTTP/1.1 specification [2],
+
+ HTTP uses a "<major>.<minor>" numbering scheme to indicate
+ versions of the protocol. The protocol versioning policy is
+ intended to allow the sender to indicate the format of a message
+ and its capacity for understanding further HTTP communication,
+ rather than the features obtained via that communication. No
+ change is made to the version number for the addition of message
+ components which do not affect communication behavior or which
+ only add to extensible field values. The <minor> number is
+ incremented when the changes made to the protocol add features
+ which do not change the general message parsing algorithm, but
+ which may add to the message semantics and imply additional
+ capabilities of the sender. The <major> number is incremented when
+ the format of a message within the protocol is changed.
+
+ The same language appears in the description of HTTP/1.0 [1].
+
+ Many readers of these documents have expressed some confusion about
+ the intended meaning of this policy. Also, some people who wrote
+ HTTP implementations before RFC1945 [1] was issued were not aware of
+ the intentions behind the introduction of version numbers in
+ HTTP/1.0. This has led to debate and inconsistency regarding the use
+ and interpretation of HTTP version numbers, and has led to
+ interoperability problems in certain cases.
+
+
+
+
+
+
+
+
+
+
+Mogul, et. al. Informational [Page 2]
+\f
+RFC 2145 HTTP Version Numbers May 1997
+
+
+ This document is an attempt to clarify the situation. It is not a
+ modification of the intended meaning of the existing HTTP/1.0 and
+ HTTP/1.1 documents, but it does describe the intention of the authors
+ of those documents. In any case where either of those two documents
+ is ambiguous regarding the use and interpretation of HTTP version
+ numbers, this document should be considered the definitive as to the
+ intentions of the designers of HTTP.
+
+ The specification described in this document is not part of the
+ specification of any individual version of HTTP, such as HTTP/1.0 or
+ HTTP/1.1. Rather, this document describes the use of HTTP version
+ numbers in any version of HTTP (except for HTTP/0.9, which did not
+ include version numbers).
+
+ No vendor or other provider of an HTTP implementation should claim
+ any compliance with any IETF HTTP specification unless the
+ implementation conditionally complies with the rules in this
+ document.
+
+1.1 Robustness Principle
+
+ RFC791 [4] defines the "robustness principle" in section 3.2:
+
+ an implementation must be conservative in its sending
+ behavior, and liberal in its receiving behavior.
+
+ This principle applies to HTTP, as well. It is the fundamental basis
+ for interpreting any part of the HTTP specification that might still
+ be ambiguous. In particular, implementations of HTTP SHOULD NOT
+ reject messages or generate errors unnecessarily.
+
+2 HTTP version numbers
+
+ We start by restating the language quoted above from section 3.1 of
+ the HTTP/1.1 specification [2]:
+
+ It is, and has always been, the explicit intent of the
+ HTTP specification that the interpretation of an HTTP message
+ header does not change between minor versions of the same major
+ version.
+
+ It is, and has always been, the explicit intent of the
+ HTTP specification that an implementation receiving a message
+ header that it does not understand MUST ignore that header. (The
+ word "ignore" has a special meaning for proxies; see section 2.1
+ below.)
+
+
+
+
+
+Mogul, et. al. Informational [Page 3]
+\f
+RFC 2145 HTTP Version Numbers May 1997
+
+
+ To make this as clear as possible: The major version sent in a
+ message MAY indicate the interpretation of other header fields. The
+ minor version sent in a message MUST NOT indicate the interpretation
+ of other header fields. This reflects the principle that the minor
+ version labels the capability of the sender, not the interpretation
+ of the message.
+
+ Note: In a future version of HTTP, we may introduce a mechanism
+ that explicitly requires a receiving implementation to reject a
+ message if it does not understand certain headers. For example,
+ this might be implemented by means of a header that lists a set of
+ other message headers that must be understood by the recipient.
+ Any implementation claiming at least conditional compliance with
+ this future version of HTTP would have to implement this
+ mechanism. However, no implementation claiming compliance with a
+ lower HTTP version (in particular, HTTP/1.1) will have to
+ implement this mechanism.
+
+ This future change may be required to support the Protocol
+ Extension Protocol (PEP) [3].
+
+ One consequence of these rules is that an HTTP/1.1 message sent to an
+ HTTP/1.0 recipient (or a recipient whose version is unknown) MUST be
+ constructed so that it remains a valid HTTP/1.0 message when all
+ headers not defined in the HTTP/1.0 specification [1] are removed.
+
+2.1 Proxy behavior
+
+ A proxy MUST forward an unknown header, unless it is protected by a
+ Connection header. A proxy implementing an HTTP version >= 1.1 MUST
+ NOT forward unknown headers that are protected by a Connection
+ header, as described in section 14.10 of the HTTP/1.1 specification
+ [2].
+
+ We remind the reader that that HTTP version numbers are hop-by-hop
+ components of HTTP messages, and are not end-to-end. That is, an
+ HTTP proxy never "forwards" an HTTP version number in either a
+ request or response.
+
+2.2 Compatibility between minor versions of the same major version
+
+ An implementation of HTTP/x.b sending a message to a recipient whose
+ version is known to be HTTP/x.a, a < b, MAY send a header that is not
+ defined in the specification for HTTP/x.a. For example, an HTTP/1.1
+ server may send a "Cache-control" header to an HTTP/1.0 client; this
+ may be useful if the immediate recipient is an HTTP/1.0 proxy, but
+ the ultimate recipient is an HTTP/1.1 client.
+
+
+
+
+Mogul, et. al. Informational [Page 4]
+\f
+RFC 2145 HTTP Version Numbers May 1997
+
+
+ An implementation of HTTP/x.b sending a message to a recipient whose
+ version is known to be HTTP/x.a, a < b, MUST NOT depend on the
+ recipient understanding a header not defined in the specification for
+ HTTP/x.a. For example, HTTP/1.0 clients cannot be expected to
+ understand chunked encodings, and so an HTTP/1.1 server must never
+ send "Transfer-Encoding: chunked" in response to an HTTP/1.0 request.
+
+2.3 Which version number to send in a message
+
+ The most strenuous debate over the use of HTTP version numbers has
+ centered on the problem of implementations that do not follow the
+ robustness principle, and which fail to produce useful results when
+ they receive a message with an HTTP minor version higher than the
+ minor version they implement. We consider these implementations
+ buggy, but we recognize that the robustness principle also implies
+ that message senders should make concessions to buggy implementations
+ when this is truly necessary for interoperation.
+
+ An HTTP client SHOULD send a request version equal to the highest
+ version for which the client is at least conditionally compliant, and
+ whose major version is no higher than the highest version supported
+ by the server, if this is known. An HTTP client MUST NOT send a
+ version for which it is not at least conditionally compliant.
+
+ An HTTP client MAY send a lower request version, if it is known that
+ the server incorrectly implements the HTTP specification, but only
+ after the client has determined that the server is actually buggy.
+
+ An HTTP server SHOULD send a response version equal to the highest
+ version for which the server is at least conditionally compliant, and
+ whose major version is less than or equal to the one received in the
+ request. An HTTP server MUST NOT send a version for which it is not
+ at least conditionally compliant. A server MAY send a 505 (HTTP
+ Version Not Supported) response if cannot send a response using the
+ major version used in the client's request.
+
+ An HTTP server MAY send a lower response version, if it is known or
+ suspected that the client incorrectly implements the HTTP
+ specification, but this should not be the default, and this SHOULD
+ NOT be done if the request version is HTTP/1.1 or greater.
+
+
+
+
+
+
+
+
+
+
+
+Mogul, et. al. Informational [Page 5]
+\f
+RFC 2145 HTTP Version Numbers May 1997
+
+
+3 Security Considerations
+
+ None, except to the extent that security mechanisms introduced in one
+ version of HTTP might depend on the proper interpretation of HTTP
+ version numbers in older implementations.
+
+4 References
+
+ 1. Berners-Lee, T., R. Fielding, and H. Frystyk. Hypertext
+ Transfer Protocol -- HTTP/1.0. RFC 1945, HTTP Working Group, May,
+ 1996.
+
+ 2. Fielding, Roy T., Jim Gettys, Jeffrey C. Mogul, Henrik Frystyk
+ Nielsen, and Tim Berners-Lee. Hypertext Transfer Protocol --
+ HTTP/1.1. RFC 2068, HTTP Working Group, January, 1997.
+
+ 3. Khare, Rohit. HTTP/1.2 Extension Protocol (PEP). HTTP Working
+ Group, Work in Progress.
+
+ 4. Postel, Jon. Internet Protocol. RFC 791, NIC, September, 1981.
+
+5 Authors' addresses
+
+ Jeffrey C. Mogul
+ Western Research Laboratory
+ Digital Equipment Corporation
+ 250 University Avenue
+ Palo Alto, California, 94305, USA
+ Email: mogul@wrl.dec.com
+
+ Roy T. Fielding
+ Department of Information and Computer Science
+ University of California
+ Irvine, CA 92717-3425, USA
+ Fax: +1 (714) 824-4056
+ Email: fielding@ics.uci.edu
+
+ Jim Gettys
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+ Fax: +1 (617) 258 8682
+ Email: jg@w3.org
+
+
+
+
+
+
+
+
+Mogul, et. al. Informational [Page 6]
+\f
+RFC 2145 HTTP Version Numbers May 1997
+
+
+ Henrik Frystyk Nielsen
+ W3 Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+ Fax: +1 (617) 258 8682
+ Email: frystyk@w3.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mogul, et. al. Informational [Page 7]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group L. Masinter
+Request for Comments: 2324 1 April 1998
+Category: Informational
+
+
+ Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)
+
+Status of this Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1998). All Rights Reserved.
+
+Abstract
+
+ This document describes HTCPCP, a protocol for controlling,
+ monitoring, and diagnosing coffee pots.
+
+1. Rationale and Scope
+
+ There is coffee all over the world. Increasingly, in a world in which
+ computing is ubiquitous, the computists want to make coffee. Coffee
+ brewing is an art, but the distributed intelligence of the web-
+ connected world transcends art. Thus, there is a strong, dark, rich
+ requirement for a protocol designed espressoly for the brewing of
+ coffee. Coffee is brewed using coffee pots. Networked coffee pots
+ require a control protocol if they are to be controlled.
+
+ Increasingly, home and consumer devices are being connected to the
+ Internet. Early networking experiments demonstrated vending devices
+ connected to the Internet for status monitoring [COKE]. One of the
+ first remotely _operated_ machine to be hooked up to the Internet,
+ the Internet Toaster, (controlled via SNMP) was debuted in 1990
+ [RFC2235].
+
+ The demand for ubiquitous appliance connectivity that is causing the
+ consumption of the IPv4 address space. Consumers want remote control
+ of devices such as coffee pots so that they may wake up to freshly
+ brewed coffee, or cause coffee to be prepared at a precise time after
+ the completion of dinner preparations.
+
+
+
+
+
+
+
+Masinter Informational [Page 1]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+ This document specifies a Hyper Text Coffee Pot Control Protocol
+ (HTCPCP), which permits the full request and responses necessary to
+ control all devices capable of making the popular caffeinated hot
+ beverages.
+
+ HTTP 1.1 ([RFC2068]) permits the transfer of web objects from origin
+ servers to clients. The web is world-wide. HTCPCP is based on HTTP.
+ This is because HTTP is everywhere. It could not be so pervasive
+ without being good. Therefore, HTTP is good. If you want good coffee,
+ HTCPCP needs to be good. To make HTCPCP good, it is good to base
+ HTCPCP on HTTP.
+
+ Future versions of this protocol may include extensions for espresso
+ machines and similar devices.
+
+2. HTCPCP Protocol
+
+ The HTCPCP protocol is built on top of HTTP, with the addition of a
+ few new methods, header fields and return codes. All HTCPCP servers
+ should be referred to with the "coffee:" URI scheme (Section 4).
+
+2.1 HTCPCP Added Methods
+
+2.1.1 The BREW method, and the use of POST
+
+ Commands to control a coffee pot are sent from client to coffee
+ server using either the BREW or POST method, and a message body with
+ Content-Type set to "application/coffee-pot-command".
+
+ A coffee pot server MUST accept both the BREW and POST method
+ equivalently. However, the use of POST for causing actions to happen
+ is deprecated.
+
+ Coffee pots heat water using electronic mechanisms, so there is no
+ fire. Thus, no firewalls are necessary, and firewall control policy
+ is irrelevant. However, POST may be a trademark for coffee, and so
+ the BREW method has been added. The BREW method may be used with
+ other HTTP-based protocols (e.g., the Hyper Text Brewery Control
+ Protocol).
+
+2.1.2 GET method
+
+ In HTTP, the GET method is used to mean "retrieve whatever
+ information (in the form of an entity) identified by the Request-
+ URI." If the Request-URI refers to a data-producing process, it is
+ the produced data which shall be returned as the entity in the
+ response and not the source text of the process, unless that text
+ happens to be the output of the process.
+
+
+
+Masinter Informational [Page 2]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+ In HTCPCP, the resources associated with a coffee pot are physical,
+ and not information resources. The "data" for most coffee URIs
+ contain no caffeine.
+
+2.1.3 PROPFIND method
+
+ If a cup of coffee is data, metadata about the brewed resource is
+ discovered using the PROPFIND method [WEBDAV].
+
+2.1.4 WHEN method
+
+ When coffee is poured, and milk is offered, it is necessary for the
+ holder of the recipient of milk to say "when" at the time when
+ sufficient milk has been introduced into the coffee. For this
+ purpose, the "WHEN" method has been added to HTCPCP. Enough? Say
+ WHEN.
+
+2.2 Coffee Pot Header fields
+
+ HTCPCP recommends several HTTP header fields and defines some new
+ ones.
+
+2.2.1 Recommended header fields
+
+2.2.1.1 The "safe" response header field.
+
+ [SAFE] defines a HTTP response header field, "Safe", which can be
+ used to indicate that repeating a HTTP request is safe. The inclusion
+ of a "Safe: Yes" header field allows a client to repeat a previous
+ request if the result of the request might be repeated.
+
+ The actual safety of devices for brewing coffee varies widely, and
+ may depend, in fact, on conditions in the client rather than just in
+ the server. Thus, this protocol includes an extension to the "Safe"
+ response header:
+
+ Safe = "Safe" ":" safe-nature
+ safe-nature = "yes" | "no" | conditionally-safe
+ conditionally-safe = "if-" safe-condition
+ safe-condition = "user-awake" | token
+
+ indication will allow user agents to handle retries of some safe
+ requests, in particular safe POST requests, in a more user-friendly
+ way.
+
+
+
+
+
+
+
+Masinter Informational [Page 3]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+2.2.2 New header fields
+
+2.2.2.1 The Accept-Additions header field
+
+ In HTTP, the "Accept" request-header field is used to specify media
+ types which are acceptable for the response. However, in HTCPCP, the
+ response may result in additional actions on the part of the
+ automated pot. For this reason, HTCPCP adds a new header field,
+ "Accept-Additions":
+
+
+ Accept-Additions = "Accept-Additions" ":"
+ #( addition-range [ accept-params ] )
+
+ addition-type = ( "*"
+ | milk-type
+ | syrup-type
+ | sweetener-type
+ | spice-type
+ | alcohol-type
+ ) *( ";" parameter )
+ milk-type = ( "Cream" | "Half-and-half" | "Whole-milk"
+ | "Part-Skim" | "Skim" | "Non-Dairy" )
+ syrup-type = ( "Vanilla" | "Almond" | "Raspberry"
+ | "Chocolate" )
+ alcohol-type = ( "Whisky" | "Rum" | "Kahlua" | "Aquavit" )
+
+2.2.3 Omitted Header Fields
+
+ No options were given for decaffeinated coffee. What's the point?
+
+2.3 HTCPCP return codes
+
+ Normal HTTP return codes are used to indicate difficulties of the
+ HTCPCP server. This section identifies special interpretations and
+ new return codes.
+
+2.3.1 406 Not Acceptable
+
+ This return code is normally interpreted as "The resource identified
+ by the request is only capable of generating response entities which
+ have content characteristics not acceptable according to the accept
+ headers sent in the request. In HTCPCP, this response code MAY be
+ returned if the operator of the coffee pot cannot comply with the
+ Accept-Addition request. Unless the request was a HEAD request, the
+ response SHOULD include an entity containing a list of available
+ coffee additions.
+
+
+
+
+Masinter Informational [Page 4]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+ In practice, most automated coffee pots cannot currently provide
+ additions.
+
+2.3.2 418 I'm a teapot
+
+ Any attempt to brew coffee with a teapot should result in the error
+ code "418 I'm a teapot". The resulting entity body MAY be short and
+ stout.
+
+3. The "coffee" URI scheme
+
+ Because coffee is international, there are international coffee URI
+ schemes. All coffee URL schemes are written with URL encoding of the
+ UTF-8 encoding of the characters that spell the word for "coffee" in
+ any of 29 languages, following the conventions for
+ internationalization in URIs [URLI18N].
+
+coffee-url = coffee-scheme ":" [ "//" host ]
+ ["/" pot-designator ] ["?" additions-list ]
+
+coffee-scheme = ( "koffie" ; Afrikaans, Dutch
+ | "q%C3%A6hv%C3%A6" ; Azerbaijani
+ | "%D9%82%D9%87%D9%88%D8%A9" ; Arabic
+ | "akeita" ; Basque
+ | "koffee" ; Bengali
+ | "kahva" ; Bosnian
+ | "kafe" ; Bulgarian, Czech
+ | "caf%C3%E8" ; Catalan, French, Galician
+ | "%E5%92%96%E5%95%A1" ; Chinese
+ | "kava" ; Croatian
+ | "k%C3%A1va ; Czech
+ | "kaffe" ; Danish, Norwegian, Swedish
+ | "coffee" ; English
+ | "kafo" ; Esperanto
+ | "kohv" ; Estonian
+ | "kahvi" ; Finnish
+ | "%4Baffee" ; German
+ | "%CE%BA%CE%B1%CF%86%CE%AD" ; Greek
+ | "%E0%A4%95%E0%A5%8C%E0%A4%AB%E0%A5%80" ; Hindi
+ | "%E3%82%B3%E3%83%BC%E3%83%92%E3%83%BC" ; Japanese
+ | "%EC%BB%A4%ED%94%BC" ; Korean
+ | "%D0%BA%D0%BE%D1%84%D0%B5" ; Russian
+ | "%E0%B8%81%E0%B8%B2%E0%B9%81%E0%B8%9F" ; Thai
+ )
+
+ pot-designator = "pot-" integer ; for machines with multiple pots
+ additions-list = #( addition )
+
+
+
+
+Masinter Informational [Page 5]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+ All alternative coffee-scheme forms are equivalent. However, the use
+ of coffee-scheme in various languages MAY be interpreted as an
+ indication of the kind of coffee produced by the coffee pot. Note
+ that while URL scheme names are case-independent, capitalization is
+ important for German and thus the initial "K" must be encoded.
+
+4. The "message/coffeepot" media type
+
+ The entity body of a POST or BREW request MUST be of Content-Type
+ "message/coffeepot". Since most of the information for controlling
+ the coffee pot is conveyed by the additional headers, the content of
+ "message/coffeepot" contains only a coffee-message-body:
+
+ coffee-message-body = "start" | "stop"
+
+5. Operational constraints
+
+ This section lays out some of the operational issues with deployment
+ of HTCPCP ubiquitously.
+
+5.1 Timing Considerations
+
+ A robust quality of service is required between the coffee pot user
+ and the coffee pot service. Coffee pots SHOULD use the Network Time
+ Protocol [NTP] to synchronize their clocks to a globally accurate
+ time standard.
+
+ Telerobotics has been an expensive technology. However, with the
+ advent of the Cambridge Coffee Pot [CAM], the use of the web (rather
+ than SNMP) for remote system monitoring and management has been
+ proven. Additional coffee pot maintenance tasks might be
+ accomplished by remote robotics.
+
+ Web data is normally static. Therefore to save data transmission and
+ time, Web browser programs store each Web page retrieved by a user on
+ the user's computer. Thus, if the user wants to return to that page,
+ it is now stored locally and does not need to be requested again from
+ the server. An image used for robot control or for monitoring a
+ changing scene is dynamic. A fresh version needs to be retrieved from
+ the server each time it is accessed.
+
+5.2 Crossing firewalls
+
+ In most organizations HTTP traffic crosses firewalls fairly easily.
+ Modern coffee pots do not use fire. However, a "firewall" is useful
+ for protection of any source from any manner of heat, and not just
+ fire. Every home computer network SHOULD be protected by a firewall
+ from sources of heat. However, remote control of coffee pots is
+
+
+
+Masinter Informational [Page 6]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+ important from outside the home. Thus, it is important that HTCPCP
+ cross firewalls easily.
+
+ By basing HTCPCP on HTTP and using port 80, it will get all of HTTP's
+ firewall-crossing virtues. Of course, the home firewalls will require
+ reconfiguration or new versions in order to accommodate HTCPCP-
+ specific methods, headers and trailers, but such upgrades will be
+ easily accommodated. Most home network system administrators drink
+ coffee, and are willing to accommodate the needs of tunnelling
+ HTCPCP.
+
+6. System management considerations
+
+ Coffee pot monitoring using HTTP protocols has been an early
+ application of the web. In the earliest instance, coffee pot
+ monitoring was an early (and appropriate) use of ATM networks [CAM].
+
+ The traditional technique [CAM] was to attach a frame-grabber to a
+ video camera, and feed the images to a web server. This was an
+ appropriate application of ATM networks. In this coffee pot
+ installation, the Trojan Room of Cambridge University laboratories
+ was used to give a web interface to monitor a common coffee pot. of
+ us involved in related research and, being poor, impoverished
+ academics, we only had one coffee filter machine between us, which
+ lived in the corridor just outside the Trojan Room. However, being
+ highly dedicated and hard-working academics, we got through a lot of
+ coffee, and when a fresh pot was brewed, it often didn't last long.
+
+ This service was created as the first application to use a new RPC
+ mechanism designed in the Cambridge Computer Laboratory - MSRPC2. It
+ runs over MSNL (Multi-Service Network Layer) - a network layer
+ protocol designed for ATM networks.
+
+ Coffee pots on the Internet may be managed using the Coffee Pot MIB
+ [CPMIB].
+
+7. Security Considerations
+
+ Anyone who gets in between me and my morning coffee should be
+ insecure.
+
+ Unmoderated access to unprotected coffee pots from Internet users
+ might lead to several kinds of "denial of coffee service" attacks.
+ The improper use of filtration devices might admit trojan grounds.
+ Filtration is not a good virus protection method.
+
+
+
+
+
+
+Masinter Informational [Page 7]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+ Putting coffee grounds into Internet plumbing may result in clogged
+ plumbing, which would entail the services of an Internet Plumber
+ [PLUMB], who would, in turn, require an Internet Plumber's Helper.
+
+ Access authentication will be discussed in a separate memo.
+
+8. Acknowledgements
+
+ Many thanks to the many contributors to this standard, including Roy
+ Fielding, Mark Day, Keith Moore, Carl Uno-Manros, Michael Slavitch,
+ and Martin Duerst. The inspiration of the Prancing Pony, the CMU
+ Coke Machine, the Cambridge Coffee Pot, the Internet Toaster, and
+ other computer controlled remote devices have led to this valuable
+ creation.
+
+9. References
+
+ [RFC2068] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and T.
+ Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2068,
+ January 1997.
+
+ [RFC2186] Wessels, D., and K. Claffy, "Internet Cache Protocol (ICP),
+ version 2," RFC 2186, September 1997
+
+ [CPMIB] Slavitch, M., "Definitions of Managed Objects for Drip-Type
+ Heated Beverage Hardware Devices using SMIv2", RFC 2325, 1 April
+ 1998.
+
+ [HTSVMP] Q. Stafford-Fraser, "Hyper Text Sandwich Van Monitoring
+ Protocol, Version 3.2". In preparation.
+
+ [RFC2295] Holtman, K., and A. Mutz, "Transparent Content Negotiation
+ in HTTP", RFC 2295, March 1998.
+
+ [SAFE] K. Holtman. "The Safe Response Header Field", September 1997.
+
+ [CAM] "The Trojan Room Coffee Machine", D. Gordon and M. Johnson,
+ University of Cambridge Computer Lab,
+ <http://www.cl.cam.ac.uk/coffee/coffee.html>
+
+ [CBIO] "The Trojan Room Coffee Pot, a (non-technical) biography", Q.
+ Stafford-Fraser, University of Cambridge Computer Lab,
+ <http://www.cl.cam.ac.uk/coffee/qsf/coffee.html>.
+
+ [RFC2235] Zakon, R., "Hobbes' Internet Timeline", FYI 32, RFC 2230,
+ November 1997. See also
+ <http://www.internode.com.au/images/toaster2.jpg>
+
+
+
+
+Masinter Informational [Page 8]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+ [NTP] Mills, D., "Network Time Protocol (Version 3) Specification,
+ Implementation and Analysis", RFC 1305, March 1992.
+
+ [URLI18N] Masinter, L., "Using UTF8 for non-ASCII Characters in
+ Extended URIs" Work in Progress.
+
+ [PLUMB] B. Metcalfe, "Internet Plumber of the Year: Jim Gettys",
+ Infoworld, February 2, 1998.
+
+ [COKE] D. Nichols, "Coke machine history", C. Everhart, "Interesting
+ uses of networking", <http://www-
+ cse.ucsd.edu/users/bsy/coke.history.txt>.
+
+10. Author's Address
+
+ Larry Masinter
+ Xerox Palo Alto Research Center
+ 3333 Coyote Hill Road
+ Palo Alto, CA 94304
+
+ EMail: masinter@parc.xerox.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Masinter Informational [Page 9]
+\f
+RFC 2324 HTCPCP/1.0 1 April 1998
+
+
+11. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1998). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Masinter Informational [Page 10]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group L. Masinter
+Request for Comments: 2388 Xerox Corporation
+Category: Standards Track August 1998
+
+
+ Returning Values from Forms: multipart/form-data
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1998). All Rights Reserved.
+
+1. Abstract
+
+ This specification defines an Internet Media Type, multipart/form-
+ data, which can be used by a wide variety of applications and
+ transported by a wide variety of protocols as a way of returning a
+ set of values as the result of a user filling out a form.
+
+2. Introduction
+
+ In many applications, it is possible for a user to be presented with
+ a form. The user will fill out the form, including information that
+ is typed, generated by user input, or included from files that the
+ user has selected. When the form is filled out, the data from the
+ form is sent from the user to the receiving application.
+
+ The definition of MultiPart/Form-Data is derived from one of those
+ applications, originally set out in [RFC1867] and subsequently
+ incorporated into [HTML40], where forms are expressed in HTML, and in
+ which the form values are sent via HTTP or electronic mail. This
+ representation is widely implemented in numerous web browsers and web
+ servers.
+
+ However, multipart/form-data can be used for forms that are presented
+ using representations other than HTML (spreadsheets, Portable
+ Document Format, etc), and for transport using other means than
+ electronic mail or HTTP. This document defines the representation of
+ form values independently of the application for which it is used.
+
+
+
+
+
+Masinter Standards Track [Page 1]
+\f
+RFC 2388 multipart/form-data August 1998
+
+
+3. Definition of multipart/form-data
+
+ The media-type multipart/form-data follows the rules of all multipart
+ MIME data streams as outlined in [RFC 2046]. In forms, there are a
+ series of fields to be supplied by the user who fills out the form.
+ Each field has a name. Within a given form, the names are unique.
+
+ "multipart/form-data" contains a series of parts. Each part is
+ expected to contain a content-disposition header [RFC 2183] where the
+ disposition type is "form-data", and where the disposition contains
+ an (additional) parameter of "name", where the value of that
+ parameter is the original field name in the form. For example, a part
+ might contain a header:
+
+ Content-Disposition: form-data; name="user"
+
+ with the value corresponding to the entry of the "user" field.
+
+ Field names originally in non-ASCII character sets may be encoded
+ within the value of the "name" parameter using the standard method
+ described in RFC 2047.
+
+ As with all multipart MIME types, each part has an optional
+ "Content-Type", which defaults to text/plain. If the contents of a
+ file are returned via filling out a form, then the file input is
+ identified as the appropriate media type, if known, or
+ "application/octet-stream". If multiple files are to be returned as
+ the result of a single form entry, they should be represented as a
+ "multipart/mixed" part embedded within the "multipart/form-data".
+
+ Each part may be encoded and the "content-transfer-encoding" header
+ supplied if the value of that part does not conform to the default
+ encoding.
+
+4. Use of multipart/form-data
+
+4.1 Boundary
+
+ As with other multipart types, a boundary is selected that does not
+ occur in any of the data. Each field of the form is sent, in the
+ order defined by the sending appliction and form, as a part of the
+ multipart stream. Each part identifies the INPUT name within the
+ original form. Each part should be labelled with an appropriate
+ content-type if the media type is known (e.g., inferred from the file
+ extension or operating system typing information) or as
+ "application/octet-stream".
+
+
+
+
+
+Masinter Standards Track [Page 2]
+\f
+RFC 2388 multipart/form-data August 1998
+
+
+4.2 Sets of files
+
+ If the value of a form field is a set of files rather than a single
+ file, that value can be transferred together using the
+ "multipart/mixed" format.
+
+4.3 Encoding
+
+ While the HTTP protocol can transport arbitrary binary data, the
+ default for mail transport is the 7BIT encoding. The value supplied
+ for a part may need to be encoded and the "content-transfer-encoding"
+ header supplied if the value does not conform to the default
+ encoding. [See section 5 of RFC 2046 for more details.]
+
+4.4 Other attributes
+
+ Forms may request file inputs from the user; the form software may
+ include the file name and other file attributes, as specified in [RFC
+ 2184].
+
+ The original local file name may be supplied as well, either as a
+ "filename" parameter either of the "content-disposition: form-data"
+ header or, in the case of multiple files, in a "content-disposition:
+ file" header of the subpart. The sending application MAY supply a
+ file name; if the file name of the sender's operating system is not
+ in US-ASCII, the file name might be approximated, or encoded using
+ the method of RFC 2231.
+
+ This is a convenience for those cases where the files supplied by the
+ form might contain references to each other, e.g., a TeX file and its
+ .sty auxiliary style description.
+
+4.5 Charset of text in form data
+
+ Each part of a multipart/form-data is supposed to have a content-
+ type. In the case where a field element is text, the charset
+ parameter for the text indicates the character encoding used.
+
+ For example, a form with a text field in which a user typed 'Joe owes
+ <eu>100' where <eu> is the Euro symbol might have form data returned
+ as:
+
+ --AaB03x
+ content-disposition: form-data; name="field1"
+ content-type: text/plain;charset=windows-1250
+ content-transfer-encoding: quoted-printable
+
+
+
+
+
+Masinter Standards Track [Page 3]
+\f
+RFC 2388 multipart/form-data August 1998
+
+
+ Joe owes =80100.
+ --AaB03x
+
+5. Operability considerations
+
+5.1 Compression, encryption
+
+ Some of the data in forms may be compressed or encrypted, using other
+ MIME mechanisms. This is a function of the application that is
+ generating the form-data.
+
+5.2 Other data encodings rather than multipart
+
+ Various people have suggested using new mime top-level type
+ "aggregate", e.g., aggregate/mixed or a content-transfer-encoding of
+ "packet" to express indeterminate-length binary data, rather than
+ relying on the multipart-style boundaries. While this would be
+ useful, the "multipart" mechanisms are well established, simple to
+ implement on both the sending client and receiving server, and as
+ efficient as other methods of dealing with multiple combinations of
+ binary data.
+
+ The multipart/form-data encoding has a high overhead and performance
+ impact if there are many fields with short values. However, in
+ practice, for the forms in use, for example, in HTML, the average
+ overhead is not significant.
+
+5.3 Remote files with third-party transfer
+
+ In some scenarios, the user operating the form software might want to
+ specify a URL for remote data rather than a local file. In this case,
+ is there a way to allow the browser to send to the client a pointer
+ to the external data rather than the entire contents? This capability
+ could be implemented, for example, by having the client send to the
+ server data of type "message/external-body" with "access-type" set
+ to, say, "uri", and the URL of the remote data in the body of the
+ message.
+
+5.4 Non-ASCII field names
+
+ Note that MIME headers are generally required to consist only of 7-
+ bit data in the US-ASCII character set. Hence field names should be
+ encoded according to the method in RFC 2047 if they contain
+ characters outside of that set.
+
+
+
+
+
+
+
+Masinter Standards Track [Page 4]
+\f
+RFC 2388 multipart/form-data August 1998
+
+
+5.5 Ordered fields and duplicated field names
+
+ The relationship of the ordering of fields within a form and the
+ ordering of returned values within "multipart/form-data" is not
+ defined by this specification, nor is the handling of the case where
+ a form has multiple fields with the same name. While HTML-based forms
+ may send back results in the order received, and intermediaries
+ should not reorder the results, there are some systems which might
+ not define a natural order for form fields.
+
+5.6 Interoperability with web applications
+
+ Many web applications use the "application/x-url-encoded" method for
+ returning data from forms. This format is quite compact, e.g.:
+
+ name=Xavier+Xantico&verdict=Yes&colour=Blue&happy=sad&Utf%F6r=Send
+
+ however, there is no opportunity to label the enclosed data with
+ content type, apply a charset, or use other encoding mechanisms.
+
+ Many form-interpreting programs (primarly web browsers) now implement
+ and generate multipart/form-data, but an existing application might
+ need to optionally support both the application/x-url-encoded format
+ as well.
+
+5.7 Correlating form data with the original form
+
+ This specification provides no specific mechanism by which
+ multipart/form-data can be associated with the form that caused it to
+ be transmitted. This separation is intentional; many different forms
+ might be used for transmitting the same data. In practice,
+ applications may supply a specific form processing resource (in HTML,
+ the ACTION attribute in a FORM tag) for each different form.
+ Alternatively, data about the form might be encoded in a "hidden
+ field" (a field which is part of the form but which has a fixed value
+ to be transmitted back to the form-data processor.)
+
+6. Security Considerations
+
+ The data format described in this document introduces no new security
+ considerations outside of those introduced by the protocols that use
+ it and of the component elements. It is important when interpreting
+ content-disposition to not overwrite files in the recipients address
+ space inadvertently.
+
+ User applications that request form information from users must be
+ careful not to cause a user to send information to the requestor or a
+ third party unwillingly or unwittingly. For example, a form might
+
+
+
+Masinter Standards Track [Page 5]
+\f
+RFC 2388 multipart/form-data August 1998
+
+
+ request 'spam' information to be sent to an unintended third party,
+ or private information to be sent to someone that the user might not
+ actually intend. While this is primarily an issue for the
+ representation and interpretation of forms themselves, rather than
+ the data representation of the result of form transmission, the
+ transportation of private information must be done in a way that does
+ not expose it to unwanted prying.
+
+ With the introduction of form-data that can reasonably send back the
+ content of files from user's file space, the possibility that a user
+ might be sent an automated script that fills out a form and then
+ sends the user's local file to another address arises. Thus,
+ additional caution is required when executing automated scripting
+ where form-data might include user's files.
+
+7. Author's Address
+
+ Larry Masinter
+ Xerox Palo Alto Research Center
+ 3333 Coyote Hill Road
+ Palo Alto, CA 94304
+
+ Fax: +1 650 812 4333
+ EMail: masinter@parc.xerox.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Masinter Standards Track [Page 6]
+\f
+RFC 2388 multipart/form-data August 1998
+
+
+Appendix A. Media type registration for multipart/form-data
+
+ Media Type name:
+ multipart
+
+ Media subtype name:
+ form-data
+
+ Required parameters:
+ none
+
+ Optional parameters:
+ none
+
+ Encoding considerations:
+ No additional considerations other than as for other multipart
+ types.
+
+ Security Considerations
+ Applications which receive forms and process them must be careful
+ not to supply data back to the requesting form processing site that
+ was not intended to be sent by the recipient. This is a
+ consideration for any application that generates a multipart/form-
+ data.
+
+ The multipart/form-data type introduces no new security
+ considerations for recipients beyond what might occur with any of
+ the enclosed parts.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Masinter Standards Track [Page 7]
+\f
+RFC 2388 multipart/form-data August 1998
+
+
+References
+
+ [RFC 2046] Freed, N., and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part Two: Media Types", RFC 2046,
+ November 1996.
+
+ [RFC 2047] Moore, K., "MIME (Multipurpose Internet Mail Extensions)
+ Part Three: Message Header Extensions for Non-ASCII Text",
+ RFC 2047, November 1996.
+
+ [RFC 2231] Freed, N., and K. Moore, "MIME Parameter Value and Encoded
+ Word Extensions: Character Sets, Languages, and
+ Continuations", RFC 2231, November 1997.
+
+ [RFC 1806] Troost, R., and S. Dorner, "Communicating Presentation
+ Information in Internet Messages: The Content-Disposition
+ Header", RFC 1806, June 1995.
+
+ [RFC 1867] Nebel, E., and L. Masinter, "Form-based File Upload in
+ HTML", RFC 1867, November 1995.
+
+ [RFC 2183] Troost, R., Dorner, S., and K. Moore, "Communicating
+ Presentation Information in Internet Messages: The
+ Content-Disposition Header Field", RFC 2183, August 1997.
+
+ [RFC 2184] Freed, N., and K. Moore, "MIME Parameter Value and Encoded
+ Word Extensions: Character Sets, Languages, and
+ Continuations", RFC 2184, August 1997.
+
+ [HTML40] D. Raggett, A. Le Hors, I. Jacobs. "HTML 4.0
+ Specification", World Wide Web Consortium Technical Report
+ "REC-html40", December, 1997. <http://www.w3.org/TR/REC-
+ html40/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Masinter Standards Track [Page 8]
+\f
+RFC 2388 multipart/form-data August 1998
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (1998). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Masinter Standards Track [Page 9]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group Y. Goland
+Request for Comments: 2518 Microsoft
+Category: Standards Track E. Whitehead
+ UC Irvine
+ A. Faizi
+ Netscape
+ S. Carter
+ Novell
+ D. Jensen
+ Novell
+ February 1999
+
+
+ HTTP Extensions for Distributed Authoring -- WEBDAV
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Abstract
+
+ This document specifies a set of methods, headers, and content-types
+ ancillary to HTTP/1.1 for the management of resource properties,
+ creation and management of resource collections, namespace
+ manipulation, and resource locking (collision avoidance).
+
+Table of Contents
+
+ ABSTRACT............................................................1
+ 1 INTRODUCTION .....................................................5
+ 2 NOTATIONAL CONVENTIONS ...........................................7
+ 3 TERMINOLOGY ......................................................7
+ 4 DATA MODEL FOR RESOURCE PROPERTIES ...............................8
+ 4.1 The Resource Property Model ...................................8
+ 4.2 Existing Metadata Proposals ...................................8
+ 4.3 Properties and HTTP Headers ...................................9
+ 4.4 Property Values ...............................................9
+ 4.5 Property Names ...............................................10
+ 4.6 Media Independent Links ......................................10
+ 5 COLLECTIONS OF WEB RESOURCES ....................................11
+
+
+
+Goland, et al. Standards Track [Page 1]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ 5.1 HTTP URL Namespace Model .....................................11
+ 5.2 Collection Resources .........................................11
+ 5.3 Creation and Retrieval of Collection Resources ...............12
+ 5.4 Source Resources and Output Resources ........................13
+ 6 LOCKING .........................................................14
+ 6.1 Exclusive Vs. Shared Locks ...................................14
+ 6.2 Required Support .............................................16
+ 6.3 Lock Tokens ..................................................16
+ 6.4 opaquelocktoken Lock Token URI Scheme ........................16
+ 6.4.1 Node Field Generation Without the IEEE 802 Address ........17
+ 6.5 Lock Capability Discovery ....................................19
+ 6.6 Active Lock Discovery ........................................19
+ 6.7 Usage Considerations .........................................19
+ 7 WRITE LOCK ......................................................20
+ 7.1 Methods Restricted by Write Locks ............................20
+ 7.2 Write Locks and Lock Tokens ..................................20
+ 7.3 Write Locks and Properties ...................................20
+ 7.4 Write Locks and Null Resources ...............................21
+ 7.5 Write Locks and Collections ..................................21
+ 7.6 Write Locks and the If Request Header ........................22
+ 7.6.1 Example - Write Lock ......................................22
+ 7.7 Write Locks and COPY/MOVE ....................................23
+ 7.8 Refreshing Write Locks .......................................23
+ 8 HTTP METHODS FOR DISTRIBUTED AUTHORING ..........................23
+ 8.1 PROPFIND .....................................................24
+ 8.1.1 Example - Retrieving Named Properties .....................25
+ 8.1.2 Example - Using allprop to Retrieve All Properties ........26
+ 8.1.3 Example - Using propname to Retrieve all Property Names ...29
+ 8.2 PROPPATCH ....................................................31
+ 8.2.1 Status Codes for use with 207 (Multi-Status) ..............31
+ 8.2.2 Example - PROPPATCH .......................................32
+ 8.3 MKCOL Method .................................................33
+ 8.3.1 Request ...................................................33
+ 8.3.2 Status Codes ..............................................33
+ 8.3.3 Example - MKCOL ...........................................34
+ 8.4 GET, HEAD for Collections ....................................34
+ 8.5 POST for Collections .........................................35
+ 8.6 DELETE .......................................................35
+ 8.6.1 DELETE for Non-Collection Resources .......................35
+ 8.6.2 DELETE for Collections ....................................36
+ 8.7 PUT ..........................................................36
+ 8.7.1 PUT for Non-Collection Resources ..........................36
+ 8.7.2 PUT for Collections .......................................37
+ 8.8 COPY Method ..................................................37
+ 8.8.1 COPY for HTTP/1.1 resources ...............................37
+ 8.8.2 COPY for Properties .......................................38
+ 8.8.3 COPY for Collections ......................................38
+ 8.8.4 COPY and the Overwrite Header .............................39
+
+
+
+Goland, et al. Standards Track [Page 2]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ 8.8.5 Status Codes ..............................................39
+ 8.8.6 Example - COPY with Overwrite .............................40
+ 8.8.7 Example - COPY with No Overwrite ..........................40
+ 8.8.8 Example - COPY of a Collection ............................41
+ 8.9 MOVE Method ..................................................42
+ 8.9.1 MOVE for Properties .......................................42
+ 8.9.2 MOVE for Collections ......................................42
+ 8.9.3 MOVE and the Overwrite Header .............................43
+ 8.9.4 Status Codes ..............................................43
+ 8.9.5 Example - MOVE of a Non-Collection ........................44
+ 8.9.6 Example - MOVE of a Collection ............................44
+ 8.10 LOCK Method ..................................................45
+ 8.10.1 Operation .................................................46
+ 8.10.2 The Effect of Locks on Properties and Collections .........46
+ 8.10.3 Locking Replicated Resources ..............................46
+ 8.10.4 Depth and Locking .........................................46
+ 8.10.5 Interaction with other Methods ............................47
+ 8.10.6 Lock Compatibility Table ..................................47
+ 8.10.7 Status Codes ..............................................48
+ 8.10.8 Example - Simple Lock Request .............................48
+ 8.10.9 Example - Refreshing a Write Lock .........................49
+ 8.10.10 Example - Multi-Resource Lock Request ....................50
+ 8.11 UNLOCK Method ................................................51
+ 8.11.1 Example - UNLOCK ..........................................52
+ 9 HTTP HEADERS FOR DISTRIBUTED AUTHORING ..........................52
+ 9.1 DAV Header ...................................................52
+ 9.2 Depth Header .................................................52
+ 9.3 Destination Header ...........................................54
+ 9.4 If Header ....................................................54
+ 9.4.1 No-tag-list Production ....................................55
+ 9.4.2 Tagged-list Production ....................................55
+ 9.4.3 not Production ............................................56
+ 9.4.4 Matching Function .........................................56
+ 9.4.5 If Header and Non-DAV Compliant Proxies ...................57
+ 9.5 Lock-Token Header ............................................57
+ 9.6 Overwrite Header .............................................57
+ 9.7 Status-URI Response Header ...................................57
+ 9.8 Timeout Request Header .......................................58
+ 10 STATUS CODE EXTENSIONS TO HTTP/1.1 ............................59
+ 10.1 102 Processing ...............................................59
+ 10.2 207 Multi-Status .............................................59
+ 10.3 422 Unprocessable Entity .....................................60
+ 10.4 423 Locked ...................................................60
+ 10.5 424 Failed Dependency ........................................60
+ 10.6 507 Insufficient Storage .....................................60
+ 11 MULTI-STATUS RESPONSE .........................................60
+ 12 XML ELEMENT DEFINITIONS .......................................61
+ 12.1 activelock XML Element .......................................61
+
+
+
+Goland, et al. Standards Track [Page 3]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ 12.1.1 depth XML Element .........................................61
+ 12.1.2 locktoken XML Element .....................................61
+ 12.1.3 timeout XML Element .......................................61
+ 12.2 collection XML Element .......................................62
+ 12.3 href XML Element .............................................62
+ 12.4 link XML Element .............................................62
+ 12.4.1 dst XML Element ...........................................62
+ 12.4.2 src XML Element ...........................................62
+ 12.5 lockentry XML Element ........................................63
+ 12.6 lockinfo XML Element .........................................63
+ 12.7 lockscope XML Element ........................................63
+ 12.7.1 exclusive XML Element .....................................63
+ 12.7.2 shared XML Element ........................................63
+ 12.8 locktype XML Element .........................................64
+ 12.8.1 write XML Element .........................................64
+ 12.9 multistatus XML Element ......................................64
+ 12.9.1 response XML Element ......................................64
+ 12.9.2 responsedescription XML Element ...........................65
+ 12.10 owner XML Element ...........................................65
+ 12.11 prop XML element ............................................66
+ 12.12 propertybehavior XML element ................................66
+ 12.12.1 keepalive XML element ....................................66
+ 12.12.2 omit XML element .........................................67
+ 12.13 propertyupdate XML element ..................................67
+ 12.13.1 remove XML element .......................................67
+ 12.13.2 set XML element ..........................................67
+ 12.14 propfind XML Element ........................................68
+ 12.14.1 allprop XML Element ......................................68
+ 12.14.2 propname XML Element .....................................68
+ 13 DAV PROPERTIES ................................................68
+ 13.1 creationdate Property ........................................69
+ 13.2 displayname Property .........................................69
+ 13.3 getcontentlanguage Property ..................................69
+ 13.4 getcontentlength Property ....................................69
+ 13.5 getcontenttype Property ......................................70
+ 13.6 getetag Property .............................................70
+ 13.7 getlastmodified Property .....................................70
+ 13.8 lockdiscovery Property .......................................71
+ 13.8.1 Example - Retrieving the lockdiscovery Property ...........71
+ 13.9 resourcetype Property ........................................72
+ 13.10 source Property .............................................72
+ 13.10.1 Example - A source Property ..............................72
+ 13.11 supportedlock Property ......................................73
+ 13.11.1 Example - Retrieving the supportedlock Property ..........73
+ 14 INSTRUCTIONS FOR PROCESSING XML IN DAV ........................74
+ 15 DAV COMPLIANCE CLASSES ........................................75
+ 15.1 Class 1 ......................................................75
+ 15.2 Class 2 ......................................................75
+
+
+
+Goland, et al. Standards Track [Page 4]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ 16 INTERNATIONALIZATION CONSIDERATIONS ...........................76
+ 17 SECURITY CONSIDERATIONS .......................................77
+ 17.1 Authentication of Clients ....................................77
+ 17.2 Denial of Service ............................................78
+ 17.3 Security through Obscurity ...................................78
+ 17.4 Privacy Issues Connected to Locks ............................78
+ 17.5 Privacy Issues Connected to Properties .......................79
+ 17.6 Reduction of Security due to Source Link .....................79
+ 17.7 Implications of XML External Entities ........................79
+ 17.8 Risks Connected with Lock Tokens .............................80
+ 18 IANA CONSIDERATIONS ...........................................80
+ 19 INTELLECTUAL PROPERTY .........................................81
+ 20 ACKNOWLEDGEMENTS ..............................................82
+ 21 REFERENCES ....................................................82
+ 21.1 Normative References .........................................82
+ 21.2 Informational References .....................................83
+ 22 AUTHORS' ADDRESSES ............................................84
+ 23 APPENDICES ....................................................86
+ 23.1 Appendix 1 - WebDAV Document Type Definition .................86
+ 23.2 Appendix 2 - ISO 8601 Date and Time Profile ..................88
+ 23.3 Appendix 3 - Notes on Processing XML Elements ................89
+ 23.3.1 Notes on Empty XML Elements ...............................89
+ 23.3.2 Notes on Illegal XML Processing ...........................89
+ 23.4 Appendix 4 -- XML Namespaces for WebDAV ......................92
+ 23.4.1 Introduction ..............................................92
+ 23.4.2 Meaning of Qualified Names ................................92
+ 24 FULL COPYRIGHT STATEMENT ......................................94
+
+
+
+1 Introduction
+
+ This document describes an extension to the HTTP/1.1 protocol that
+ allows clients to perform remote web content authoring operations.
+ This extension provides a coherent set of methods, headers, request
+ entity body formats, and response entity body formats that provide
+ operations for:
+
+ Properties: The ability to create, remove, and query information
+ about Web pages, such as their authors, creation dates, etc. Also,
+ the ability to link pages of any media type to related pages.
+
+ Collections: The ability to create sets of documents and to retrieve
+ a hierarchical membership listing (like a directory listing in a file
+ system).
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 5]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Locking: The ability to keep more than one person from working on a
+ document at the same time. This prevents the "lost update problem,"
+ in which modifications are lost as first one author then another
+ writes changes without merging the other author's changes.
+
+ Namespace Operations: The ability to instruct the server to copy and
+ move Web resources.
+
+ Requirements and rationale for these operations are described in a
+ companion document, "Requirements for a Distributed Authoring and
+ Versioning Protocol for the World Wide Web" [RFC2291].
+
+ The sections below provide a detailed introduction to resource
+ properties (section 4), collections of resources (section 5), and
+ locking operations (section 6). These sections introduce the
+ abstractions manipulated by the WebDAV-specific HTTP methods
+ described in section 8, "HTTP Methods for Distributed Authoring".
+
+ In HTTP/1.1, method parameter information was exclusively encoded in
+ HTTP headers. Unlike HTTP/1.1, WebDAV encodes method parameter
+ information either in an Extensible Markup Language (XML) [REC-XML]
+ request entity body, or in an HTTP header. The use of XML to encode
+ method parameters was motivated by the ability to add extra XML
+ elements to existing structures, providing extensibility; and by
+ XML's ability to encode information in ISO 10646 character sets,
+ providing internationalization support. As a rule of thumb,
+ parameters are encoded in XML entity bodies when they have unbounded
+ length, or when they may be shown to a human user and hence require
+ encoding in an ISO 10646 character set. Otherwise, parameters are
+ encoded within HTTP headers. Section 9 describes the new HTTP
+ headers used with WebDAV methods.
+
+ In addition to encoding method parameters, XML is used in WebDAV to
+ encode the responses from methods, providing the extensibility and
+ internationalization advantages of XML for method output, as well as
+ input.
+
+ XML elements used in this specification are defined in section 12.
+
+ The XML namespace extension (Appendix 4) is also used in this
+ specification in order to allow for new XML elements to be added
+ without fear of colliding with other element names.
+
+ While the status codes provided by HTTP/1.1 are sufficient to
+ describe most error conditions encountered by WebDAV methods, there
+ are some errors that do not fall neatly into the existing categories.
+ New status codes developed for the WebDAV methods are defined in
+ section 10. Since some WebDAV methods may operate over many
+
+
+
+Goland, et al. Standards Track [Page 6]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ resources, the Multi-Status response has been introduced to return
+ status information for multiple resources. The Multi-Status response
+ is described in section 11.
+
+ WebDAV employs the property mechanism to store information about the
+ current state of the resource. For example, when a lock is taken out
+ on a resource, a lock information property describes the current
+ state of the lock. Section 13 defines the properties used within the
+ WebDAV specification.
+
+ Finishing off the specification are sections on what it means to be
+ compliant with this specification (section 15), on
+ internationalization support (section 16), and on security (section
+ 17).
+
+2 Notational Conventions
+
+ Since this document describes a set of extensions to the HTTP/1.1
+ protocol, the augmented BNF used herein to describe protocol elements
+ is exactly the same as described in section 2.1 of [RFC2068]. Since
+ this augmented BNF uses the basic production rules provided in
+ section 2.2 of [RFC2068], these rules apply to this document as well.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+3 Terminology
+
+ URI/URL - A Uniform Resource Identifier and Uniform Resource Locator,
+ respectively. These terms (and the distinction between them) are
+ defined in [RFC2396].
+
+ Collection - A resource that contains a set of URIs, termed member
+ URIs, which identify member resources and meets the requirements in
+ section 5 of this specification.
+
+ Member URI - A URI which is a member of the set of URIs contained by
+ a collection.
+
+ Internal Member URI - A Member URI that is immediately relative to
+ the URI of the collection (the definition of immediately relative is
+ given in section 5.2).
+
+ Property - A name/value pair that contains descriptive information
+ about a resource.
+
+
+
+
+
+Goland, et al. Standards Track [Page 7]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Live Property - A property whose semantics and syntax are enforced by
+ the server. For example, the live "getcontentlength" property has
+ its value, the length of the entity returned by a GET request,
+ automatically calculated by the server.
+
+ Dead Property - A property whose semantics and syntax are not
+ enforced by the server. The server only records the value of a dead
+ property; the client is responsible for maintaining the consistency
+ of the syntax and semantics of a dead property.
+
+ Null Resource - A resource which responds with a 404 (Not Found) to
+ any HTTP/1.1 or DAV method except for PUT, MKCOL, OPTIONS and LOCK.
+ A NULL resource MUST NOT appear as a member of its parent collection.
+
+4 Data Model for Resource Properties
+
+4.1 The Resource Property Model
+
+ Properties are pieces of data that describe the state of a resource.
+ Properties are data about data.
+
+ Properties are used in distributed authoring environments to provide
+ for efficient discovery and management of resources. For example, a
+ 'subject' property might allow for the indexing of all resources by
+ their subject, and an 'author' property might allow for the discovery
+ of what authors have written which documents.
+
+ The DAV property model consists of name/value pairs. The name of a
+ property identifies the property's syntax and semantics, and provides
+ an address by which to refer to its syntax and semantics.
+
+ There are two categories of properties: "live" and "dead". A live
+ property has its syntax and semantics enforced by the server. Live
+ properties include cases where a) the value of a property is read-
+ only, maintained by the server, and b) the value of the property is
+ maintained by the client, but the server performs syntax checking on
+ submitted values. All instances of a given live property MUST comply
+ with the definition associated with that property name. A dead
+ property has its syntax and semantics enforced by the client; the
+ server merely records the value of the property verbatim.
+
+4.2 Existing Metadata Proposals
+
+ Properties have long played an essential role in the maintenance of
+ large document repositories, and many current proposals contain some
+ notion of a property, or discuss web metadata more generally. These
+ include PICS [REC-PICS], PICS-NG, XML, Web Collections, and several
+ proposals on representing relationships within HTML. Work on PICS-NG
+
+
+
+Goland, et al. Standards Track [Page 8]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ and Web Collections has been subsumed by the Resource Description
+ Framework (RDF) metadata activity of the World Wide Web Consortium.
+ RDF consists of a network-based data model and an XML representation
+ of that model.
+
+ Some proposals come from a digital library perspective. These
+ include the Dublin Core [RFC2413] metadata set and the Warwick
+ Framework [WF], a container architecture for different metadata
+ schemas. The literature includes many examples of metadata,
+ including MARC [USMARC], a bibliographic metadata format, and a
+ technical report bibliographic format employed by the Dienst system
+ [RFC1807]. Additionally, the proceedings from the first IEEE Metadata
+ conference describe many community-specific metadata sets.
+
+ Participants of the 1996 Metadata II Workshop in Warwick, UK [WF],
+ noted that "new metadata sets will develop as the networked
+ infrastructure matures" and "different communities will propose,
+ design, and be responsible for different types of metadata." These
+ observations can be corroborated by noting that many community-
+ specific sets of metadata already exist, and there is significant
+ motivation for the development of new forms of metadata as many
+ communities increasingly make their data available in digital form,
+ requiring a metadata format to assist data location and cataloging.
+
+4.3 Properties and HTTP Headers
+
+ Properties already exist, in a limited sense, in HTTP message
+ headers. However, in distributed authoring environments a relatively
+ large number of properties are needed to describe the state of a
+ resource, and setting/returning them all through HTTP headers is
+ inefficient. Thus a mechanism is needed which allows a principal to
+ identify a set of properties in which the principal is interested and
+ to set or retrieve just those properties.
+
+4.4 Property Values
+
+ The value of a property when expressed in XML MUST be well formed.
+
+ XML has been chosen because it is a flexible, self-describing,
+ structured data format that supports rich schema definitions, and
+ because of its support for multiple character sets. XML's self-
+ describing nature allows any property's value to be extended by
+ adding new elements. Older clients will not break when they
+ encounter extensions because they will still have the data specified
+ in the original schema and will ignore elements they do not
+ understand. XML's support for multiple character sets allows any
+ human-readable property to be encoded and read in a character set
+ familiar to the user. XML's support for multiple human languages,
+
+
+
+Goland, et al. Standards Track [Page 9]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ using the "xml:lang" attribute, handles cases where the same
+ character set is employed by multiple human languages.
+
+4.5 Property Names
+
+ A property name is a universally unique identifier that is associated
+ with a schema that provides information about the syntax and
+ semantics of the property.
+
+ Because a property's name is universally unique, clients can depend
+ upon consistent behavior for a particular property across multiple
+ resources, on the same and across different servers, so long as that
+ property is "live" on the resources in question, and the
+ implementation of the live property is faithful to its definition.
+
+ The XML namespace mechanism, which is based on URIs [RFC2396], is
+ used to name properties because it prevents namespace collisions and
+ provides for varying degrees of administrative control.
+
+ The property namespace is flat; that is, no hierarchy of properties
+ is explicitly recognized. Thus, if a property A and a property A/B
+ exist on a resource, there is no recognition of any relationship
+ between the two properties. It is expected that a separate
+ specification will eventually be produced which will address issues
+ relating to hierarchical properties.
+
+ Finally, it is not possible to define the same property twice on a
+ single resource, as this would cause a collision in the resource's
+ property namespace.
+
+4.6 Media Independent Links
+
+ Although HTML resources support links to other resources, the Web
+ needs more general support for links between resources of any media
+ type (media types are also known as MIME types, or content types).
+ WebDAV provides such links. A WebDAV link is a special type of
+ property value, formally defined in section 12.4, that allows typed
+ connections to be established between resources of any media type.
+ The property value consists of source and destination Uniform
+ Resource Identifiers (URIs); the property name identifies the link
+ type.
+
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 10]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+5 Collections of Web Resources
+
+ This section provides a description of a new type of Web resource,
+ the collection, and discusses its interactions with the HTTP URL
+ namespace. The purpose of a collection resource is to model
+ collection-like objects (e.g., file system directories) within a
+ server's namespace.
+
+ All DAV compliant resources MUST support the HTTP URL namespace model
+ specified herein.
+
+5.1 HTTP URL Namespace Model
+
+ The HTTP URL namespace is a hierarchical namespace where the
+ hierarchy is delimited with the "/" character.
+
+ An HTTP URL namespace is said to be consistent if it meets the
+ following conditions: for every URL in the HTTP hierarchy there
+ exists a collection that contains that URL as an internal member.
+ The root, or top-level collection of the namespace under
+ consideration is exempt from the previous rule.
+
+ Neither HTTP/1.1 nor WebDAV require that the entire HTTP URL
+ namespace be consistent. However, certain WebDAV methods are
+ prohibited from producing results that cause namespace
+ inconsistencies.
+
+ Although implicit in [RFC2068] and [RFC2396], any resource, including
+ collection resources, MAY be identified by more than one URI. For
+ example, a resource could be identified by multiple HTTP URLs.
+
+5.2 Collection Resources
+
+ A collection is a resource whose state consists of at least a list of
+ internal member URIs and a set of properties, but which may have
+ additional state such as entity bodies returned by GET. An internal
+ member URI MUST be immediately relative to a base URI of the
+ collection. That is, the internal member URI is equal to a
+ containing collection's URI plus an additional segment for non-
+ collection resources, or additional segment plus trailing slash "/"
+ for collection resources, where segment is defined in section 3.3 of
+ [RFC2396].
+
+ Any given internal member URI MUST only belong to the collection
+ once, i.e., it is illegal to have multiple instances of the same URI
+ in a collection. Properties defined on collections behave exactly as
+ do properties on non-collection resources.
+
+
+
+
+Goland, et al. Standards Track [Page 11]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ For all WebDAV compliant resources A and B, identified by URIs U and
+ V, for which U is immediately relative to V, B MUST be a collection
+ that has U as an internal member URI. So, if the resource with URL
+ http://foo.com/bar/blah is WebDAV compliant and if the resource with
+ URL http://foo.com/bar/ is WebDAV compliant then the resource with
+ URL http://foo.com/bar/ must be a collection and must contain URL
+ http://foo.com/bar/blah as an internal member.
+
+ Collection resources MAY list the URLs of non-WebDAV compliant
+ children in the HTTP URL namespace hierarchy as internal members but
+ are not required to do so. For example, if the resource with URL
+ http://foo.com/bar/blah is not WebDAV compliant and the URL
+ http://foo.com/bar/ identifies a collection then URL
+ http://foo.com/bar/blah may or may not be an internal member of the
+ collection with URL http://foo.com/bar/.
+
+ If a WebDAV compliant resource has no WebDAV compliant children in
+ the HTTP URL namespace hierarchy then the WebDAV compliant resource
+ is not required to be a collection.
+
+ There is a standing convention that when a collection is referred to
+ by its name without a trailing slash, the trailing slash is
+ automatically appended. Due to this, a resource may accept a URI
+ without a trailing "/" to point to a collection. In this case it
+ SHOULD return a content-location header in the response pointing to
+ the URI ending with the "/". For example, if a client invokes a
+ method on http://foo.bar/blah (no trailing slash), the resource
+ http://foo.bar/blah/ (trailing slash) may respond as if the operation
+ were invoked on it, and should return a content-location header with
+ http://foo.bar/blah/ in it. In general clients SHOULD use the "/"
+ form of collection names.
+
+ A resource MAY be a collection but not be WebDAV compliant. That is,
+ the resource may comply with all the rules set out in this
+ specification regarding how a collection is to behave without
+ necessarily supporting all methods that a WebDAV compliant resource
+ is required to support. In such a case the resource may return the
+ DAV:resourcetype property with the value DAV:collection but MUST NOT
+ return a DAV header containing the value "1" on an OPTIONS response.
+
+5.3 Creation and Retrieval of Collection Resources
+
+ This document specifies the MKCOL method to create new collection
+ resources, rather than using the existing HTTP/1.1 PUT or POST
+ method, for the following reasons:
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 12]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ In HTTP/1.1, the PUT method is defined to store the request body at
+ the location specified by the Request-URI. While a description
+ format for a collection can readily be constructed for use with PUT,
+ the implications of sending such a description to the server are
+ undesirable. For example, if a description of a collection that
+ omitted some existing resources were PUT to a server, this might be
+ interpreted as a command to remove those members. This would extend
+ PUT to perform DELETE functionality, which is undesirable since it
+ changes the semantics of PUT, and makes it difficult to control
+ DELETE functionality with an access control scheme based on methods.
+
+ While the POST method is sufficiently open-ended that a "create a
+ collection" POST command could be constructed, this is undesirable
+ because it would be difficult to separate access control for
+ collection creation from other uses of POST.
+
+ The exact definition of the behavior of GET and PUT on collections is
+ defined later in this document.
+
+5.4 Source Resources and Output Resources
+
+ For many resources, the entity returned by a GET method exactly
+ matches the persistent state of the resource, for example, a GIF file
+ stored on a disk. For this simple case, the URI at which a resource
+ is accessed is identical to the URI at which the source (the
+ persistent state) of the resource is accessed. This is also the case
+ for HTML source files that are not processed by the server prior to
+ transmission.
+
+ However, the server can sometimes process HTML resources before they
+ are transmitted as a return entity body. For example, a server-
+ side-include directive within an HTML file might instruct a server to
+ replace the directive with another value, such as the current date.
+ In this case, what is returned by GET (HTML plus date) differs from
+ the persistent state of the resource (HTML plus directive).
+ Typically there is no way to access the HTML resource containing the
+ unprocessed directive.
+
+ Sometimes the entity returned by GET is the output of a data-
+ producing process that is described by one or more source resources
+ (that may not even have a location in the URI namespace). A single
+ data-producing process may dynamically generate the state of a
+ potentially large number of output resources. An example of this is
+ a CGI script that describes a "finger" gateway process that maps part
+ of the namespace of a server into finger requests, such as
+ http://www.foo.bar.org/finger_gateway/user@host.
+
+
+
+
+
+Goland, et al. Standards Track [Page 13]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ In the absence of distributed authoring capabilities, it is
+ acceptable to have no mapping of source resource(s) to the URI
+ namespace. In fact, preventing access to the source resource(s) has
+ desirable security benefits. However, if remote editing of the
+ source resource(s) is desired, the source resource(s) should be given
+ a location in the URI namespace. This source location should not be
+ one of the locations at which the generated output is retrievable,
+ since in general it is impossible for the server to differentiate
+ requests for source resources from requests for process output
+ resources. There is often a many-to-many relationship between source
+ resources and output resources.
+
+ On WebDAV compliant servers the URI of the source resource(s) may be
+ stored in a link on the output resource with type DAV:source (see
+ section 13.10 for a description of the source link property).
+ Storing the source URIs in links on the output resources places the
+ burden of discovering the source on the authoring client. Note that
+ the value of a source link is not guaranteed to point to the correct
+ source. Source links may break or incorrect values may be entered.
+ Also note that not all servers will allow the client to set the
+ source link value. For example a server which generates source links
+ on the fly for its CGI files will most likely not allow a client to
+ set the source link value.
+
+6 Locking
+
+ The ability to lock a resource provides a mechanism for serializing
+ access to that resource. Using a lock, an authoring client can
+ provide a reasonable guarantee that another principal will not modify
+ a resource while it is being edited. In this way, a client can
+ prevent the "lost update" problem.
+
+ This specification allows locks to vary over two client-specified
+ parameters, the number of principals involved (exclusive vs. shared)
+ and the type of access to be granted. This document defines locking
+ for only one access type, write. However, the syntax is extensible,
+ and permits the eventual specification of locking for other access
+ types.
+
+6.1 Exclusive Vs. Shared Locks
+
+ The most basic form of lock is an exclusive lock. This is a lock
+ where the access right in question is only granted to a single
+ principal. The need for this arbitration results from a desire to
+ avoid having to merge results.
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 14]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ However, there are times when the goal of a lock is not to exclude
+ others from exercising an access right but rather to provide a
+ mechanism for principals to indicate that they intend to exercise
+ their access rights. Shared locks are provided for this case. A
+ shared lock allows multiple principals to receive a lock. Hence any
+ principal with appropriate access can get the lock.
+
+ With shared locks there are two trust sets that affect a resource.
+ The first trust set is created by access permissions. Principals who
+ are trusted, for example, may have permission to write to the
+ resource. Among those who have access permission to write to the
+ resource, the set of principals who have taken out a shared lock also
+ must trust each other, creating a (typically) smaller trust set
+ within the access permission write set.
+
+ Starting with every possible principal on the Internet, in most
+ situations the vast majority of these principals will not have write
+ access to a given resource. Of the small number who do have write
+ access, some principals may decide to guarantee their edits are free
+ from overwrite conflicts by using exclusive write locks. Others may
+ decide they trust their collaborators will not overwrite their work
+ (the potential set of collaborators being the set of principals who
+ have write permission) and use a shared lock, which informs their
+ collaborators that a principal may be working on the resource.
+
+ The WebDAV extensions to HTTP do not need to provide all of the
+ communications paths necessary for principals to coordinate their
+ activities. When using shared locks, principals may use any out of
+ band communication channel to coordinate their work (e.g., face-to-
+ face interaction, written notes, post-it notes on the screen,
+ telephone conversation, Email, etc.) The intent of a shared lock is
+ to let collaborators know who else may be working on a resource.
+
+ Shared locks are included because experience from web distributed
+ authoring systems has indicated that exclusive locks are often too
+ rigid. An exclusive lock is used to enforce a particular editing
+ process: take out an exclusive lock, read the resource, perform
+ edits, write the resource, release the lock. This editing process
+ has the problem that locks are not always properly released, for
+ example when a program crashes, or when a lock owner leaves without
+ unlocking a resource. While both timeouts and administrative action
+ can be used to remove an offending lock, neither mechanism may be
+ available when needed; the timeout may be long or the administrator
+ may not be available.
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 15]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+6.2 Required Support
+
+ A WebDAV compliant server is not required to support locking in any
+ form. If the server does support locking it may choose to support
+ any combination of exclusive and shared locks for any access types.
+
+ The reason for this flexibility is that locking policy strikes to the
+ very heart of the resource management and versioning systems employed
+ by various storage repositories. These repositories require control
+ over what sort of locking will be made available. For example, some
+ repositories only support shared write locks while others only
+ provide support for exclusive write locks while yet others use no
+ locking at all. As each system is sufficiently different to merit
+ exclusion of certain locking features, this specification leaves
+ locking as the sole axis of negotiation within WebDAV.
+
+6.3 Lock Tokens
+
+ A lock token is a type of state token, represented as a URI, which
+ identifies a particular lock. A lock token is returned by every
+ successful LOCK operation in the lockdiscovery property in the
+ response body, and can also be found through lock discovery on a
+ resource.
+
+ Lock token URIs MUST be unique across all resources for all time.
+ This uniqueness constraint allows lock tokens to be submitted across
+ resources and servers without fear of confusion.
+
+ This specification provides a lock token URI scheme called
+ opaquelocktoken that meets the uniqueness requirements. However
+ resources are free to return any URI scheme so long as it meets the
+ uniqueness requirements.
+
+ Having a lock token provides no special access rights. Anyone can
+ find out anyone else's lock token by performing lock discovery.
+ Locks MUST be enforced based upon whatever authentication mechanism
+ is used by the server, not based on the secrecy of the token values.
+
+6.4 opaquelocktoken Lock Token URI Scheme
+
+ The opaquelocktoken URI scheme is designed to be unique across all
+ resources for all time. Due to this uniqueness quality, a client may
+ submit an opaque lock token in an If header on a resource other than
+ the one that returned it.
+
+ All resources MUST recognize the opaquelocktoken scheme and, at
+ minimum, recognize that the lock token does not refer to an
+ outstanding lock on the resource.
+
+
+
+Goland, et al. Standards Track [Page 16]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ In order to guarantee uniqueness across all resources for all time
+ the opaquelocktoken requires the use of the Universal Unique
+ Identifier (UUID) mechanism, as described in [ISO-11578].
+
+ Opaquelocktoken generators, however, have a choice of how they create
+ these tokens. They can either generate a new UUID for every lock
+ token they create or they can create a single UUID and then add
+ extension characters. If the second method is selected then the
+ program generating the extensions MUST guarantee that the same
+ extension will never be used twice with the associated UUID.
+
+ OpaqueLockToken-URI = "opaquelocktoken:" UUID [Extension] ; The UUID
+ production is the string representation of a UUID, as defined in
+ [ISO-11578]. Note that white space (LWS) is not allowed between
+ elements of this production.
+
+ Extension = path ; path is defined in section 3.2.1 of RFC 2068
+ [RFC2068]
+
+6.4.1 Node Field Generation Without the IEEE 802 Address
+
+ UUIDs, as defined in [ISO-11578], contain a "node" field that
+ contains one of the IEEE 802 addresses for the server machine. As
+ noted in section 17.8, there are several security risks associated
+ with exposing a machine's IEEE 802 address. This section provides an
+ alternate mechanism for generating the "node" field of a UUID which
+ does not employ an IEEE 802 address. WebDAV servers MAY use this
+ algorithm for creating the node field when generating UUIDs. The
+ text in this section is originally from an Internet-Draft by Paul
+ Leach and Rich Salz, who are noted here to properly attribute their
+ work.
+
+ The ideal solution is to obtain a 47 bit cryptographic quality random
+ number, and use it as the low 47 bits of the node ID, with the most
+ significant bit of the first octet of the node ID set to 1. This bit
+ is the unicast/multicast bit, which will never be set in IEEE 802
+ addresses obtained from network cards; hence, there can never be a
+ conflict between UUIDs generated by machines with and without network
+ cards.
+
+ If a system does not have a primitive to generate cryptographic
+ quality random numbers, then in most systems there are usually a
+ fairly large number of sources of randomness available from which one
+ can be generated. Such sources are system specific, but often
+ include:
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 17]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ - the percent of memory in use
+ - the size of main memory in bytes
+ - the amount of free main memory in bytes
+ - the size of the paging or swap file in bytes
+ - free bytes of paging or swap file
+ - the total size of user virtual address space in bytes
+ - the total available user address space bytes
+ - the size of boot disk drive in bytes
+ - the free disk space on boot drive in bytes
+ - the current time
+ - the amount of time since the system booted
+ - the individual sizes of files in various system directories
+ - the creation, last read, and modification times of files in
+ various system directories
+ - the utilization factors of various system resources (heap, etc.)
+ - current mouse cursor position
+ - current caret position
+ - current number of running processes, threads
+ - handles or IDs of the desktop window and the active window
+ - the value of stack pointer of the caller
+ - the process and thread ID of caller
+ - various processor architecture specific performance counters
+ (instructions executed, cache misses, TLB misses)
+
+ (Note that it is precisely the above kinds of sources of randomness
+ that are used to seed cryptographic quality random number generators
+ on systems without special hardware for their construction.)
+
+ In addition, items such as the computer's name and the name of the
+ operating system, while not strictly speaking random, will help
+ differentiate the results from those obtained by other systems.
+
+ The exact algorithm to generate a node ID using these data is system
+ specific, because both the data available and the functions to obtain
+ them are often very system specific. However, assuming that one can
+ concatenate all the values from the randomness sources into a buffer,
+ and that a cryptographic hash function such as MD5 is available, then
+ any 6 bytes of the MD5 hash of the buffer, with the multicast bit
+ (the high bit of the first byte) set will be an appropriately random
+ node ID.
+
+ Other hash functions, such as SHA-1, can also be used. The only
+ requirement is that the result be suitably random _ in the sense that
+ the outputs from a set uniformly distributed inputs are themselves
+ uniformly distributed, and that a single bit change in the input can
+ be expected to cause half of the output bits to change.
+
+
+
+
+
+Goland, et al. Standards Track [Page 18]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+6.5 Lock Capability Discovery
+
+ Since server lock support is optional, a client trying to lock a
+ resource on a server can either try the lock and hope for the best,
+ or perform some form of discovery to determine what lock capabilities
+ the server supports. This is known as lock capability discovery.
+ Lock capability discovery differs from discovery of supported access
+ control types, since there may be access control types without
+ corresponding lock types. A client can determine what lock types the
+ server supports by retrieving the supportedlock property.
+
+ Any DAV compliant resource that supports the LOCK method MUST support
+ the supportedlock property.
+
+6.6 Active Lock Discovery
+
+ If another principal locks a resource that a principal wishes to
+ access, it is useful for the second principal to be able to find out
+ who the first principal is. For this purpose the lockdiscovery
+ property is provided. This property lists all outstanding locks,
+ describes their type, and where available, provides their lock token.
+
+ Any DAV compliant resource that supports the LOCK method MUST support
+ the lockdiscovery property.
+
+6.7 Usage Considerations
+
+ Although the locking mechanisms specified here provide some help in
+ preventing lost updates, they cannot guarantee that updates will
+ never be lost. Consider the following scenario:
+
+ Two clients A and B are interested in editing the resource '
+ index.html'. Client A is an HTTP client rather than a WebDAV client,
+ and so does not know how to perform locking.
+ Client A doesn't lock the document, but does a GET and begins
+ editing.
+ Client B does LOCK, performs a GET and begins editing.
+ Client B finishes editing, performs a PUT, then an UNLOCK.
+ Client A performs a PUT, overwriting and losing all of B's changes.
+
+ There are several reasons why the WebDAV protocol itself cannot
+ prevent this situation. First, it cannot force all clients to use
+ locking because it must be compatible with HTTP clients that do not
+ comprehend locking. Second, it cannot require servers to support
+ locking because of the variety of repository implementations, some of
+ which rely on reservations and merging rather than on locking.
+ Finally, being stateless, it cannot enforce a sequence of operations
+ like LOCK / GET / PUT / UNLOCK.
+
+
+
+Goland, et al. Standards Track [Page 19]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ WebDAV servers that support locking can reduce the likelihood that
+ clients will accidentally overwrite each other's changes by requiring
+ clients to lock resources before modifying them. Such servers would
+ effectively prevent HTTP 1.0 and HTTP 1.1 clients from modifying
+ resources.
+
+ WebDAV clients can be good citizens by using a lock / retrieve /
+ write /unlock sequence of operations (at least by default) whenever
+ they interact with a WebDAV server that supports locking.
+
+ HTTP 1.1 clients can be good citizens, avoiding overwriting other
+ clients' changes, by using entity tags in If-Match headers with any
+ requests that would modify resources.
+
+ Information managers may attempt to prevent overwrites by
+ implementing client-side procedures requiring locking before
+ modifying WebDAV resources.
+
+7 Write Lock
+
+ This section describes the semantics specific to the write lock type.
+ The write lock is a specific instance of a lock type, and is the only
+ lock type described in this specification.
+
+7.1 Methods Restricted by Write Locks
+
+ A write lock MUST prevent a principal without the lock from
+ successfully executing a PUT, POST, PROPPATCH, LOCK, UNLOCK, MOVE,
+ DELETE, or MKCOL on the locked resource. All other current methods,
+ GET in particular, function independently of the lock.
+
+ Note, however, that as new methods are created it will be necessary
+ to specify how they interact with a write lock.
+
+7.2 Write Locks and Lock Tokens
+
+ A successful request for an exclusive or shared write lock MUST
+ result in the generation of a unique lock token associated with the
+ requesting principal. Thus if five principals have a shared write
+ lock on the same resource there will be five lock tokens, one for
+ each principal.
+
+7.3 Write Locks and Properties
+
+ While those without a write lock may not alter a property on a
+ resource it is still possible for the values of live properties to
+ change, even while locked, due to the requirements of their schemas.
+
+
+
+
+Goland, et al. Standards Track [Page 20]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Only dead properties and live properties defined to respect locks are
+ guaranteed not to change while write locked.
+
+7.4 Write Locks and Null Resources
+
+ It is possible to assert a write lock on a null resource in order to
+ lock the name.
+
+ A write locked null resource, referred to as a lock-null resource,
+ MUST respond with a 404 (Not Found) or 405 (Method Not Allowed) to
+ any HTTP/1.1 or DAV methods except for PUT, MKCOL, OPTIONS, PROPFIND,
+ LOCK, and UNLOCK. A lock-null resource MUST appear as a member of
+ its parent collection. Additionally the lock-null resource MUST have
+ defined on it all mandatory DAV properties. Most of these
+ properties, such as all the get* properties, will have no value as a
+ lock-null resource does not support the GET method. Lock-Null
+ resources MUST have defined values for lockdiscovery and
+ supportedlock properties.
+
+ Until a method such as PUT or MKCOL is successfully executed on the
+ lock-null resource the resource MUST stay in the lock-null state.
+ However, once a PUT or MKCOL is successfully executed on a lock-null
+ resource the resource ceases to be in the lock-null state.
+
+ If the resource is unlocked, for any reason, without a PUT, MKCOL, or
+ similar method having been successfully executed upon it then the
+ resource MUST return to the null state.
+
+7.5 Write Locks and Collections
+
+ A write lock on a collection, whether created by a "Depth: 0" or
+ "Depth: infinity" lock request, prevents the addition or removal of
+ member URIs of the collection by non-lock owners. As a consequence,
+ when a principal issues a PUT or POST request to create a new
+ resource under a URI which needs to be an internal member of a write
+ locked collection to maintain HTTP namespace consistency, or issues a
+ DELETE to remove a resource which has a URI which is an existing
+ internal member URI of a write locked collection, this request MUST
+ fail if the principal does not have a write lock on the collection.
+
+ However, if a write lock request is issued to a collection containing
+ member URIs identifying resources that are currently locked in a
+ manner which conflicts with the write lock, the request MUST fail
+ with a 423 (Locked) status code.
+
+ If a lock owner causes the URI of a resource to be added as an
+ internal member URI of a locked collection then the new resource MUST
+ be automatically added to the lock. This is the only mechanism that
+
+
+
+Goland, et al. Standards Track [Page 21]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ allows a resource to be added to a write lock. Thus, for example, if
+ the collection /a/b/ is write locked and the resource /c is moved to
+ /a/b/c then resource /a/b/c will be added to the write lock.
+
+7.6 Write Locks and the If Request Header
+
+ If a user agent is not required to have knowledge about a lock when
+ requesting an operation on a locked resource, the following scenario
+ might occur. Program A, run by User A, takes out a write lock on a
+ resource. Program B, also run by User A, has no knowledge of the
+ lock taken out by Program A, yet performs a PUT to the locked
+ resource. In this scenario, the PUT succeeds because locks are
+ associated with a principal, not a program, and thus program B,
+ because it is acting with principal A's credential, is allowed to
+ perform the PUT. However, had program B known about the lock, it
+ would not have overwritten the resource, preferring instead to
+ present a dialog box describing the conflict to the user. Due to
+ this scenario, a mechanism is needed to prevent different programs
+ from accidentally ignoring locks taken out by other programs with the
+ same authorization.
+
+ In order to prevent these collisions a lock token MUST be submitted
+ by an authorized principal in the If header for all locked resources
+ that a method may interact with or the method MUST fail. For
+ example, if a resource is to be moved and both the source and
+ destination are locked then two lock tokens must be submitted, one
+ for the source and the other for the destination.
+
+7.6.1 Example - Write Lock
+
+ >>Request
+
+ COPY /~fielding/index.html HTTP/1.1
+ Host: www.ics.uci.edu
+ Destination: http://www.ics.uci.edu/users/f/fielding/index.html
+ If: <http://www.ics.uci.edu/users/f/fielding/index.html>
+ (<opaquelocktoken:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>)
+
+ >>Response
+
+ HTTP/1.1 204 No Content
+
+ In this example, even though both the source and destination are
+ locked, only one lock token must be submitted, for the lock on the
+ destination. This is because the source resource is not modified by
+ a COPY, and hence unaffected by the write lock. In this example, user
+ agent authentication has previously occurred via a mechanism outside
+ the scope of the HTTP protocol, in the underlying transport layer.
+
+
+
+Goland, et al. Standards Track [Page 22]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+7.7 Write Locks and COPY/MOVE
+
+ A COPY method invocation MUST NOT duplicate any write locks active on
+ the source. However, as previously noted, if the COPY copies the
+ resource into a collection that is locked with "Depth: infinity",
+ then the resource will be added to the lock.
+
+ A successful MOVE request on a write locked resource MUST NOT move
+ the write lock with the resource. However, the resource is subject to
+ being added to an existing lock at the destination, as specified in
+ section 7.5. For example, if the MOVE makes the resource a child of a
+ collection that is locked with "Depth: infinity", then the resource
+ will be added to that collection's lock. Additionally, if a resource
+ locked with "Depth: infinity" is moved to a destination that is
+ within the scope of the same lock (e.g., within the namespace tree
+ covered by the lock), the moved resource will again be a added to the
+ lock. In both these examples, as specified in section 7.6, an If
+ header must be submitted containing a lock token for both the source
+ and destination.
+
+7.8 Refreshing Write Locks
+
+ A client MUST NOT submit the same write lock request twice. Note
+ that a client is always aware it is resubmitting the same lock
+ request because it must include the lock token in the If header in
+ order to make the request for a resource that is already locked.
+
+ However, a client may submit a LOCK method with an If header but
+ without a body. This form of LOCK MUST only be used to "refresh" a
+ lock. Meaning, at minimum, that any timers associated with the lock
+ MUST be re-set.
+
+ A server may return a Timeout header with a lock refresh that is
+ different than the Timeout header returned when the lock was
+ originally requested. Additionally clients may submit Timeout
+ headers of arbitrary value with their lock refresh requests.
+ Servers, as always, may ignore Timeout headers submitted by the
+ client.
+
+ If an error is received in response to a refresh LOCK request the
+ client SHOULD assume that the lock was not refreshed.
+
+8 HTTP Methods for Distributed Authoring
+
+ The following new HTTP methods use XML as a request and response
+ format. All DAV compliant clients and resources MUST use XML parsers
+ that are compliant with [REC-XML]. All XML used in either requests
+ or responses MUST be, at minimum, well formed. If a server receives
+
+
+
+Goland, et al. Standards Track [Page 23]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ ill-formed XML in a request it MUST reject the entire request with a
+ 400 (Bad Request). If a client receives ill-formed XML in a response
+ then it MUST NOT assume anything about the outcome of the executed
+ method and SHOULD treat the server as malfunctioning.
+
+8.1 PROPFIND
+
+ The PROPFIND method retrieves properties defined on the resource
+ identified by the Request-URI, if the resource does not have any
+ internal members, or on the resource identified by the Request-URI
+ and potentially its member resources, if the resource is a collection
+ that has internal member URIs. All DAV compliant resources MUST
+ support the PROPFIND method and the propfind XML element (section
+ 12.14) along with all XML elements defined for use with that element.
+
+ A client may submit a Depth header with a value of "0", "1", or
+ "infinity" with a PROPFIND on a collection resource with internal
+ member URIs. DAV compliant servers MUST support the "0", "1" and
+ "infinity" behaviors. By default, the PROPFIND method without a Depth
+ header MUST act as if a "Depth: infinity" header was included.
+
+ A client may submit a propfind XML element in the body of the request
+ method describing what information is being requested. It is
+ possible to request particular property values, all property values,
+ or a list of the names of the resource's properties. A client may
+ choose not to submit a request body. An empty PROPFIND request body
+ MUST be treated as a request for the names and values of all
+ properties.
+
+ All servers MUST support returning a response of content type
+ text/xml or application/xml that contains a multistatus XML element
+ that describes the results of the attempts to retrieve the various
+ properties.
+
+ If there is an error retrieving a property then a proper error result
+ MUST be included in the response. A request to retrieve the value of
+ a property which does not exist is an error and MUST be noted, if the
+ response uses a multistatus XML element, with a response XML element
+ which contains a 404 (Not Found) status value.
+
+ Consequently, the multistatus XML element for a collection resource
+ with member URIs MUST include a response XML element for each member
+ URI of the collection, to whatever depth was requested. Each response
+ XML element MUST contain an href XML element that gives the URI of
+ the resource on which the properties in the prop XML element are
+ defined. Results for a PROPFIND on a collection resource with
+ internal member URIs are returned as a flat list whose order of
+ entries is not significant.
+
+
+
+Goland, et al. Standards Track [Page 24]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ In the case of allprop and propname, if a principal does not have the
+ right to know whether a particular property exists then the property
+ should be silently excluded from the response.
+
+ The results of this method SHOULD NOT be cached.
+
+8.1.1 Example - Retrieving Named Properties
+
+ >>Request
+
+ PROPFIND /file HTTP/1.1
+ Host: www.foo.bar
+ Content-type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:">
+ <D:prop xmlns:R="http://www.foo.bar/boxschema/">
+ <R:bigbox/>
+ <R:author/>
+ <R:DingALing/>
+ <R:Random/>
+ </D:prop>
+ </D:propfind>
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D="DAV:">
+ <D:response>
+ <D:href>http://www.foo.bar/file</D:href>
+ <D:propstat>
+ <D:prop xmlns:R="http://www.foo.bar/boxschema/">
+ <R:bigbox>
+ <R:BoxType>Box type A</R:BoxType>
+ </R:bigbox>
+ <R:author>
+ <R:Name>J.J. Johnson</R:Name>
+ </R:author>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ <D:propstat>
+ <D:prop><R:DingALing/><R:Random/></D:prop>
+
+
+
+Goland, et al. Standards Track [Page 25]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <D:status>HTTP/1.1 403 Forbidden</D:status>
+ <D:responsedescription> The user does not have access to
+ the DingALing property.
+ </D:responsedescription>
+ </D:propstat>
+ </D:response>
+ <D:responsedescription> There has been an access violation error.
+ </D:responsedescription>
+ </D:multistatus>
+
+ In this example, PROPFIND is executed on a non-collection resource
+ http://www.foo.bar/file. The propfind XML element specifies the name
+ of four properties whose values are being requested. In this case
+ only two properties were returned, since the principal issuing the
+ request did not have sufficient access rights to see the third and
+ fourth properties.
+
+8.1.2 Example - Using allprop to Retrieve All Properties
+
+ >>Request
+
+ PROPFIND /container/ HTTP/1.1
+ Host: www.foo.bar
+ Depth: 1
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:">
+ <D:allprop/>
+ </D:propfind>
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D="DAV:">
+ <D:response>
+ <D:href>http://www.foo.bar/container/</D:href>
+ <D:propstat>
+ <D:prop xmlns:R="http://www.foo.bar/boxschema/">
+ <R:bigbox>
+ <R:BoxType>Box type A</R:BoxType>
+ </R:bigbox>
+ <R:author>
+
+
+
+Goland, et al. Standards Track [Page 26]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <R:Name>Hadrian</R:Name>
+ </R:author>
+ <D:creationdate>
+ 1997-12-01T17:42:21-08:00
+ </D:creationdate>
+ <D:displayname>
+ Example collection
+ </D:displayname>
+ <D:resourcetype><D:collection/></D:resourcetype>
+ <D:supportedlock>
+ <D:lockentry>
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ </D:lockentry>
+ <D:lockentry>
+ <D:lockscope><D:shared/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ </D:lockentry>
+ </D:supportedlock>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ <D:response>
+ <D:href>http://www.foo.bar/container/front.html</D:href>
+ <D:propstat>
+ <D:prop xmlns:R="http://www.foo.bar/boxschema/">
+ <R:bigbox>
+ <R:BoxType>Box type B</R:BoxType>
+ </R:bigbox>
+ <D:creationdate>
+ 1997-12-01T18:27:21-08:00
+ </D:creationdate>
+ <D:displayname>
+ Example HTML resource
+ </D:displayname>
+ <D:getcontentlength>
+ 4525
+ </D:getcontentlength>
+ <D:getcontenttype>
+ text/html
+ </D:getcontenttype>
+ <D:getetag>
+ zzyzx
+ </D:getetag>
+ <D:getlastmodified>
+ Monday, 12-Jan-98 09:25:56 GMT
+ </D:getlastmodified>
+
+
+
+Goland, et al. Standards Track [Page 27]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <D:resourcetype/>
+ <D:supportedlock>
+ <D:lockentry>
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ </D:lockentry>
+ <D:lockentry>
+ <D:lockscope><D:shared/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ </D:lockentry>
+ </D:supportedlock>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ </D:multistatus>
+
+ In this example, PROPFIND was invoked on the resource
+ http://www.foo.bar/container/ with a Depth header of 1, meaning the
+ request applies to the resource and its children, and a propfind XML
+ element containing the allprop XML element, meaning the request
+ should return the name and value of all properties defined on each
+ resource.
+
+ The resource http://www.foo.bar/container/ has six properties defined
+ on it:
+
+ http://www.foo.bar/boxschema/bigbox,
+ http://www.foo.bar/boxschema/author, DAV:creationdate,
+ DAV:displayname, DAV:resourcetype, and DAV:supportedlock.
+
+ The last four properties are WebDAV-specific, defined in section 13.
+ Since GET is not supported on this resource, the get* properties
+ (e.g., getcontentlength) are not defined on this resource. The DAV-
+ specific properties assert that "container" was created on December
+ 1, 1997, at 5:42:21PM, in a time zone 8 hours west of GMT
+ (creationdate), has a name of "Example collection" (displayname), a
+ collection resource type (resourcetype), and supports exclusive write
+ and shared write locks (supportedlock).
+
+ The resource http://www.foo.bar/container/front.html has nine
+ properties defined on it:
+
+ http://www.foo.bar/boxschema/bigbox (another instance of the "bigbox"
+ property type), DAV:creationdate, DAV:displayname,
+ DAV:getcontentlength, DAV:getcontenttype, DAV:getetag,
+ DAV:getlastmodified, DAV:resourcetype, and DAV:supportedlock.
+
+
+
+
+Goland, et al. Standards Track [Page 28]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ The DAV-specific properties assert that "front.html" was created on
+ December 1, 1997, at 6:27:21PM, in a time zone 8 hours west of GMT
+ (creationdate), has a name of "Example HTML resource" (displayname),
+ a content length of 4525 bytes (getcontentlength), a MIME type of
+ "text/html" (getcontenttype), an entity tag of "zzyzx" (getetag), was
+ last modified on Monday, January 12, 1998, at 09:25:56 GMT
+ (getlastmodified), has an empty resource type, meaning that it is not
+ a collection (resourcetype), and supports both exclusive write and
+ shared write locks (supportedlock).
+
+8.1.3 Example - Using propname to Retrieve all Property Names
+
+ >>Request
+
+ PROPFIND /container/ HTTP/1.1
+ Host: www.foo.bar
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <propfind xmlns="DAV:">
+ <propname/>
+ </propfind>
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <multistatus xmlns="DAV:">
+ <response>
+ <href>http://www.foo.bar/container/</href>
+ <propstat>
+ <prop xmlns:R="http://www.foo.bar/boxschema/">
+ <R:bigbox/>
+ <R:author/>
+ <creationdate/>
+ <displayname/>
+ <resourcetype/>
+ <supportedlock/>
+ </prop>
+ <status>HTTP/1.1 200 OK</status>
+ </propstat>
+ </response>
+ <response>
+ <href>http://www.foo.bar/container/front.html</href>
+
+
+
+Goland, et al. Standards Track [Page 29]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <propstat>
+ <prop xmlns:R="http://www.foo.bar/boxschema/">
+ <R:bigbox/>
+ <creationdate/>
+ <displayname/>
+ <getcontentlength/>
+ <getcontenttype/>
+ <getetag/>
+ <getlastmodified/>
+ <resourcetype/>
+ <supportedlock/>
+ </prop>
+ <status>HTTP/1.1 200 OK</status>
+ </propstat>
+ </response>
+ </multistatus>
+
+
+ In this example, PROPFIND is invoked on the collection resource
+ http://www.foo.bar/container/, with a propfind XML element containing
+ the propname XML element, meaning the name of all properties should
+ be returned. Since no Depth header is present, it assumes its
+ default value of "infinity", meaning the name of the properties on
+ the collection and all its progeny should be returned.
+
+ Consistent with the previous example, resource
+ http://www.foo.bar/container/ has six properties defined on it,
+ http://www.foo.bar/boxschema/bigbox,
+ http://www.foo.bar/boxschema/author, DAV:creationdate,
+ DAV:displayname, DAV:resourcetype, and DAV:supportedlock.
+
+ The resource http://www.foo.bar/container/index.html, a member of the
+ "container" collection, has nine properties defined on it,
+ http://www.foo.bar/boxschema/bigbox, DAV:creationdate,
+ DAV:displayname, DAV:getcontentlength, DAV:getcontenttype,
+ DAV:getetag, DAV:getlastmodified, DAV:resourcetype, and
+ DAV:supportedlock.
+
+ This example also demonstrates the use of XML namespace scoping, and
+ the default namespace. Since the "xmlns" attribute does not contain
+ an explicit "shorthand name" (prefix) letter, the namespace applies
+ by default to all enclosed elements. Hence, all elements which do
+ not explicitly state the namespace to which they belong are members
+ of the "DAV:" namespace schema.
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 30]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.2 PROPPATCH
+
+ The PROPPATCH method processes instructions specified in the request
+ body to set and/or remove properties defined on the resource
+ identified by the Request-URI.
+
+ All DAV compliant resources MUST support the PROPPATCH method and
+ MUST process instructions that are specified using the
+ propertyupdate, set, and remove XML elements of the DAV schema.
+ Execution of the directives in this method is, of course, subject to
+ access control constraints. DAV compliant resources SHOULD support
+ the setting of arbitrary dead properties.
+
+ The request message body of a PROPPATCH method MUST contain the
+ propertyupdate XML element. Instruction processing MUST occur in the
+ order instructions are received (i.e., from top to bottom).
+ Instructions MUST either all be executed or none executed. Thus if
+ any error occurs during processing all executed instructions MUST be
+ undone and a proper error result returned. Instruction processing
+ details can be found in the definition of the set and remove
+ instructions in section 12.13.
+
+8.2.1 Status Codes for use with 207 (Multi-Status)
+
+ The following are examples of response codes one would expect to be
+ used in a 207 (Multi-Status) response for this method. Note,
+ however, that unless explicitly prohibited any 2/3/4/5xx series
+ response code may be used in a 207 (Multi-Status) response.
+
+ 200 (OK) - The command succeeded. As there can be a mixture of sets
+ and removes in a body, a 201 (Created) seems inappropriate.
+
+ 403 (Forbidden) - The client, for reasons the server chooses not to
+ specify, cannot alter one of the properties.
+
+ 409 (Conflict) - The client has provided a value whose semantics are
+ not appropriate for the property. This includes trying to set read-
+ only properties.
+
+ 423 (Locked) - The specified resource is locked and the client either
+ is not a lock owner or the lock type requires a lock token to be
+ submitted and the client did not submit it.
+
+ 507 (Insufficient Storage) - The server did not have sufficient space
+ to record the property.
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 31]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.2.2 Example - PROPPATCH
+
+ >>Request
+
+ PROPPATCH /bar.html HTTP/1.1
+ Host: www.foo.com
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propertyupdate xmlns:D="DAV:"
+ xmlns:Z="http://www.w3.com/standards/z39.50/">
+ <D:set>
+ <D:prop>
+ <Z:authors>
+ <Z:Author>Jim Whitehead</Z:Author>
+ <Z:Author>Roy Fielding</Z:Author>
+ </Z:authors>
+ </D:prop>
+ </D:set>
+ <D:remove>
+ <D:prop><Z:Copyright-Owner/></D:prop>
+ </D:remove>
+ </D:propertyupdate>
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D="DAV:"
+ xmlns:Z="http://www.w3.com/standards/z39.50">
+ <D:response>
+ <D:href>http://www.foo.com/bar.html</D:href>
+ <D:propstat>
+ <D:prop><Z:Authors/></D:prop>
+ <D:status>HTTP/1.1 424 Failed Dependency</D:status>
+ </D:propstat>
+ <D:propstat>
+ <D:prop><Z:Copyright-Owner/></D:prop>
+ <D:status>HTTP/1.1 409 Conflict</D:status>
+ </D:propstat>
+ <D:responsedescription> Copyright Owner can not be deleted or
+ altered.</D:responsedescription>
+ </D:response>
+ </D:multistatus>
+
+
+
+Goland, et al. Standards Track [Page 32]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ In this example, the client requests the server to set the value of
+ the http://www.w3.com/standards/z39.50/Authors property, and to
+ remove the property http://www.w3.com/standards/z39.50/Copyright-
+ Owner. Since the Copyright-Owner property could not be removed, no
+ property modifications occur. The 424 (Failed Dependency) status
+ code for the Authors property indicates this action would have
+ succeeded if it were not for the conflict with removing the
+ Copyright-Owner property.
+
+8.3 MKCOL Method
+
+ The MKCOL method is used to create a new collection. All DAV
+ compliant resources MUST support the MKCOL method.
+
+8.3.1 Request
+
+ MKCOL creates a new collection resource at the location specified by
+ the Request-URI. If the resource identified by the Request-URI is
+ non-null then the MKCOL MUST fail. During MKCOL processing, a server
+ MUST make the Request-URI a member of its parent collection, unless
+ the Request-URI is "/". If no such ancestor exists, the method MUST
+ fail. When the MKCOL operation creates a new collection resource,
+ all ancestors MUST already exist, or the method MUST fail with a 409
+ (Conflict) status code. For example, if a request to create
+ collection /a/b/c/d/ is made, and neither /a/b/ nor /a/b/c/ exists,
+ the request must fail.
+
+ When MKCOL is invoked without a request body, the newly created
+ collection SHOULD have no members.
+
+ A MKCOL request message may contain a message body. The behavior of
+ a MKCOL request when the body is present is limited to creating
+ collections, members of a collection, bodies of members and
+ properties on the collections or members. If the server receives a
+ MKCOL request entity type it does not support or understand it MUST
+ respond with a 415 (Unsupported Media Type) status code. The exact
+ behavior of MKCOL for various request media types is undefined in
+ this document, and will be specified in separate documents.
+
+8.3.2 Status Codes
+
+ Responses from a MKCOL request MUST NOT be cached as MKCOL has non-
+ idempotent semantics.
+
+ 201 (Created) - The collection or structured resource was created in
+ its entirety.
+
+
+
+
+
+Goland, et al. Standards Track [Page 33]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ 403 (Forbidden) - This indicates at least one of two conditions: 1)
+ the server does not allow the creation of collections at the given
+ location in its namespace, or 2) the parent collection of the
+ Request-URI exists but cannot accept members.
+
+ 405 (Method Not Allowed) - MKCOL can only be executed on a
+ deleted/non-existent resource.
+
+ 409 (Conflict) - A collection cannot be made at the Request-URI until
+ one or more intermediate collections have been created.
+
+ 415 (Unsupported Media Type)- The server does not support the request
+ type of the body.
+
+ 507 (Insufficient Storage) - The resource does not have sufficient
+ space to record the state of the resource after the execution of this
+ method.
+
+8.3.3 Example - MKCOL
+
+ This example creates a collection called /webdisc/xfiles/ on the
+ server www.server.org.
+
+ >>Request
+
+ MKCOL /webdisc/xfiles/ HTTP/1.1
+ Host: www.server.org
+
+ >>Response
+
+ HTTP/1.1 201 Created
+
+8.4 GET, HEAD for Collections
+
+ The semantics of GET are unchanged when applied to a collection,
+ since GET is defined as, "retrieve whatever information (in the form
+ of an entity) is identified by the Request-URI" [RFC2068]. GET when
+ applied to a collection may return the contents of an "index.html"
+ resource, a human-readable view of the contents of the collection, or
+ something else altogether. Hence it is possible that the result of a
+ GET on a collection will bear no correlation to the membership of the
+ collection.
+
+ Similarly, since the definition of HEAD is a GET without a response
+ message body, the semantics of HEAD are unmodified when applied to
+ collection resources.
+
+
+
+
+
+Goland, et al. Standards Track [Page 34]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.5 POST for Collections
+
+ Since by definition the actual function performed by POST is
+ determined by the server and often depends on the particular
+ resource, the behavior of POST when applied to collections cannot be
+ meaningfully modified because it is largely undefined. Thus the
+ semantics of POST are unmodified when applied to a collection.
+
+8.6 DELETE
+
+ 8.6.1 DELETE for Non-Collection Resources
+
+ If the DELETE method is issued to a non-collection resource whose
+ URIs are an internal member of one or more collections, then during
+ DELETE processing a server MUST remove any URI for the resource
+ identified by the Request-URI from collections which contain it as a
+ member.
+
+8.6.2 DELETE for Collections
+
+ The DELETE method on a collection MUST act as if a "Depth: infinity"
+ header was used on it. A client MUST NOT submit a Depth header with
+ a DELETE on a collection with any value but infinity.
+
+ DELETE instructs that the collection specified in the Request-URI and
+ all resources identified by its internal member URIs are to be
+ deleted.
+
+ If any resource identified by a member URI cannot be deleted then all
+ of the member's ancestors MUST NOT be deleted, so as to maintain
+ namespace consistency.
+
+ Any headers included with DELETE MUST be applied in processing every
+ resource to be deleted.
+
+ When the DELETE method has completed processing it MUST result in a
+ consistent namespace.
+
+ If an error occurs with a resource other than the resource identified
+ in the Request-URI then the response MUST be a 207 (Multi-Status).
+ 424 (Failed Dependency) errors SHOULD NOT be in the 207 (Multi-
+ Status). They can be safely left out because the client will know
+ that the ancestors of a resource could not be deleted when the client
+ receives an error for the ancestor's progeny. Additionally 204 (No
+ Content) errors SHOULD NOT be returned in the 207 (Multi-Status).
+ The reason for this prohibition is that 204 (No Content) is the
+ default success code.
+
+
+
+
+Goland, et al. Standards Track [Page 35]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.6.2.1 Example - DELETE
+
+ >>Request
+
+ DELETE /container/ HTTP/1.1
+ Host: www.foo.bar
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <d:multistatus xmlns:d="DAV:">
+ <d:response>
+ <d:href>http://www.foo.bar/container/resource3</d:href>
+ <d:status>HTTP/1.1 423 Locked</d:status>
+ </d:response>
+ </d:multistatus>
+
+ In this example the attempt to delete
+ http://www.foo.bar/container/resource3 failed because it is locked,
+ and no lock token was submitted with the request. Consequently, the
+ attempt to delete http://www.foo.bar/container/ also failed. Thus the
+ client knows that the attempt to delete http://www.foo.bar/container/
+ must have also failed since the parent can not be deleted unless its
+ child has also been deleted. Even though a Depth header has not been
+ included, a depth of infinity is assumed because the method is on a
+ collection.
+
+8.7 PUT
+
+8.7.1 PUT for Non-Collection Resources
+
+ A PUT performed on an existing resource replaces the GET response
+ entity of the resource. Properties defined on the resource may be
+ recomputed during PUT processing but are not otherwise affected. For
+ example, if a server recognizes the content type of the request body,
+ it may be able to automatically extract information that could be
+ profitably exposed as properties.
+
+ A PUT that would result in the creation of a resource without an
+ appropriately scoped parent collection MUST fail with a 409
+ (Conflict).
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 36]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.7.2 PUT for Collections
+
+ As defined in the HTTP/1.1 specification [RFC2068], the "PUT method
+ requests that the enclosed entity be stored under the supplied
+ Request-URI." Since submission of an entity representing a
+ collection would implicitly encode creation and deletion of
+ resources, this specification intentionally does not define a
+ transmission format for creating a collection using PUT. Instead,
+ the MKCOL method is defined to create collections.
+
+ When the PUT operation creates a new non-collection resource all
+ ancestors MUST already exist. If all ancestors do not exist, the
+ method MUST fail with a 409 (Conflict) status code. For example, if
+ resource /a/b/c/d.html is to be created and /a/b/c/ does not exist,
+ then the request must fail.
+
+8.8 COPY Method
+
+ The COPY method creates a duplicate of the source resource,
+ identified by the Request-URI, in the destination resource,
+ identified by the URI in the Destination header. The Destination
+ header MUST be present. The exact behavior of the COPY method
+ depends on the type of the source resource.
+
+ All WebDAV compliant resources MUST support the COPY method.
+ However, support for the COPY method does not guarantee the ability
+ to copy a resource. For example, separate programs may control
+ resources on the same server. As a result, it may not be possible to
+ copy a resource to a location that appears to be on the same server.
+
+8.8.1 COPY for HTTP/1.1 resources
+
+ When the source resource is not a collection the result of the COPY
+ method is the creation of a new resource at the destination whose
+ state and behavior match that of the source resource as closely as
+ possible. After a successful COPY invocation, all properties on the
+ source resource MUST be duplicated on the destination resource,
+ subject to modifying headers and XML elements, following the
+ definition for copying properties. Since the environment at the
+ destination may be different than at the source due to factors
+ outside the scope of control of the server, such as the absence of
+ resources required for correct operation, it may not be possible to
+ completely duplicate the behavior of the resource at the destination.
+ Subsequent alterations to the destination resource will not modify
+ the source resource. Subsequent alterations to the source resource
+ will not modify the destination resource.
+
+
+
+
+
+Goland, et al. Standards Track [Page 37]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.8.2. COPY for Properties
+
+ The following section defines how properties on a resource are
+ handled during a COPY operation.
+
+ Live properties SHOULD be duplicated as identically behaving live
+ properties at the destination resource. If a property cannot be
+ copied live, then its value MUST be duplicated, octet-for-octet, in
+ an identically named, dead property on the destination resource
+ subject to the effects of the propertybehavior XML element.
+
+ The propertybehavior XML element can specify that properties are
+ copied on best effort, that all live properties must be successfully
+ copied or the method must fail, or that a specified list of live
+ properties must be successfully copied or the method must fail. The
+ propertybehavior XML element is defined in section 12.12.
+
+8.8.3 COPY for Collections
+
+ The COPY method on a collection without a Depth header MUST act as if
+ a Depth header with value "infinity" was included. A client may
+ submit a Depth header on a COPY on a collection with a value of "0"
+ or "infinity". DAV compliant servers MUST support the "0" and
+ "infinity" Depth header behaviors.
+
+ A COPY of depth infinity instructs that the collection resource
+ identified by the Request-URI is to be copied to the location
+ identified by the URI in the Destination header, and all its internal
+ member resources are to be copied to a location relative to it,
+ recursively through all levels of the collection hierarchy.
+
+ A COPY of "Depth: 0" only instructs that the collection and its
+ properties but not resources identified by its internal member URIs,
+ are to be copied.
+
+ Any headers included with a COPY MUST be applied in processing every
+ resource to be copied with the exception of the Destination header.
+
+ The Destination header only specifies the destination URI for the
+ Request-URI. When applied to members of the collection identified by
+ the Request-URI the value of Destination is to be modified to reflect
+ the current location in the hierarchy. So, if the Request- URI is
+ /a/ with Host header value http://fun.com/ and the Destination is
+ http://fun.com/b/ then when http://fun.com/a/c/d is processed it must
+ use a Destination of http://fun.com/b/c/d.
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 38]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ When the COPY method has completed processing it MUST have created a
+ consistent namespace at the destination (see section 5.1 for the
+ definition of namespace consistency). However, if an error occurs
+ while copying an internal collection, the server MUST NOT copy any
+ resources identified by members of this collection (i.e., the server
+ must skip this subtree), as this would create an inconsistent
+ namespace. After detecting an error, the COPY operation SHOULD try to
+ finish as much of the original copy operation as possible (i.e., the
+ server should still attempt to copy other subtrees and their members,
+ that are not descendents of an error-causing collection). So, for
+ example, if an infinite depth copy operation is performed on
+ collection /a/, which contains collections /a/b/ and /a/c/, and an
+ error occurs copying /a/b/, an attempt should still be made to copy
+ /a/c/. Similarly, after encountering an error copying a non-
+ collection resource as part of an infinite depth copy, the server
+ SHOULD try to finish as much of the original copy operation as
+ possible.
+
+ If an error in executing the COPY method occurs with a resource other
+ than the resource identified in the Request-URI then the response
+ MUST be a 207 (Multi-Status).
+
+ The 424 (Failed Dependency) status code SHOULD NOT be returned in the
+ 207 (Multi-Status) response from a COPY method. These responses can
+ be safely omitted because the client will know that the progeny of a
+ resource could not be copied when the client receives an error for
+ the parent. Additionally 201 (Created)/204 (No Content) status codes
+ SHOULD NOT be returned as values in 207 (Multi-Status) responses from
+ COPY methods. They, too, can be safely omitted because they are the
+ default success codes.
+
+8.8.4 COPY and the Overwrite Header
+
+ If a resource exists at the destination and the Overwrite header is
+ "T" then prior to performing the copy the server MUST perform a
+ DELETE with "Depth: infinity" on the destination resource. If the
+ Overwrite header is set to "F" then the operation will fail.
+
+8.8.5 Status Codes
+
+ 201 (Created) - The source resource was successfully copied. The
+ copy operation resulted in the creation of a new resource.
+
+ 204 (No Content) - The source resource was successfully copied to a
+ pre-existing destination resource.
+
+ 403 (Forbidden) _ The source and destination URIs are the same.
+
+
+
+
+Goland, et al. Standards Track [Page 39]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ 409 (Conflict) _ A resource cannot be created at the destination
+ until one or more intermediate collections have been created.
+
+ 412 (Precondition Failed) - The server was unable to maintain the
+ liveness of the properties listed in the propertybehavior XML element
+ or the Overwrite header is "F" and the state of the destination
+ resource is non-null.
+
+ 423 (Locked) - The destination resource was locked.
+
+ 502 (Bad Gateway) - This may occur when the destination is on another
+ server and the destination server refuses to accept the resource.
+
+ 507 (Insufficient Storage) - The destination resource does not have
+ sufficient space to record the state of the resource after the
+ execution of this method.
+
+8.8.6 Example - COPY with Overwrite
+
+ This example shows resource
+ http://www.ics.uci.edu/~fielding/index.html being copied to the
+ location http://www.ics.uci.edu/users/f/fielding/index.html. The 204
+ (No Content) status code indicates the existing resource at the
+ destination was overwritten.
+
+ >>Request
+
+ COPY /~fielding/index.html HTTP/1.1
+ Host: www.ics.uci.edu
+ Destination: http://www.ics.uci.edu/users/f/fielding/index.html
+
+ >>Response
+
+ HTTP/1.1 204 No Content
+
+8.8.7 Example - COPY with No Overwrite
+
+ The following example shows the same copy operation being performed,
+ but with the Overwrite header set to "F." A response of 412
+ (Precondition Failed) is returned because the destination resource
+ has a non-null state.
+
+ >>Request
+
+ COPY /~fielding/index.html HTTP/1.1
+ Host: www.ics.uci.edu
+ Destination: http://www.ics.uci.edu/users/f/fielding/index.html
+ Overwrite: F
+
+
+
+Goland, et al. Standards Track [Page 40]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ >>Response
+
+ HTTP/1.1 412 Precondition Failed
+
+8.8.8 Example - COPY of a Collection
+
+ >>Request
+
+ COPY /container/ HTTP/1.1
+ Host: www.foo.bar
+ Destination: http://www.foo.bar/othercontainer/
+ Depth: infinity
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <d:propertybehavior xmlns:d="DAV:">
+ <d:keepalive>*</d:keepalive>
+ </d:propertybehavior>
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <d:multistatus xmlns:d="DAV:">
+ <d:response>
+ <d:href>http://www.foo.bar/othercontainer/R2/</d:href>
+ <d:status>HTTP/1.1 412 Precondition Failed</d:status>
+ </d:response>
+ </d:multistatus>
+
+ The Depth header is unnecessary as the default behavior of COPY on a
+ collection is to act as if a "Depth: infinity" header had been
+ submitted. In this example most of the resources, along with the
+ collection, were copied successfully. However the collection R2
+ failed, most likely due to a problem with maintaining the liveness of
+ properties (this is specified by the propertybehavior XML element).
+ Because there was an error copying R2, none of R2's members were
+ copied. However no errors were listed for those members due to the
+ error minimization rules given in section 8.8.3.
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 41]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.9 MOVE Method
+
+ The MOVE operation on a non-collection resource is the logical
+ equivalent of a copy (COPY), followed by consistency maintenance
+ processing, followed by a delete of the source, where all three
+ actions are performed atomically. The consistency maintenance step
+ allows the server to perform updates caused by the move, such as
+ updating all URIs other than the Request-URI which identify the
+ source resource, to point to the new destination resource.
+ Consequently, the Destination header MUST be present on all MOVE
+ methods and MUST follow all COPY requirements for the COPY part of
+ the MOVE method. All DAV compliant resources MUST support the MOVE
+ method. However, support for the MOVE method does not guarantee the
+ ability to move a resource to a particular destination.
+
+ For example, separate programs may actually control different sets of
+ resources on the same server. Therefore, it may not be possible to
+ move a resource within a namespace that appears to belong to the same
+ server.
+
+ If a resource exists at the destination, the destination resource
+ will be DELETEd as a side-effect of the MOVE operation, subject to
+ the restrictions of the Overwrite header.
+
+8.9.1 MOVE for Properties
+
+ The behavior of properties on a MOVE, including the effects of the
+ propertybehavior XML element, MUST be the same as specified in
+ section 8.8.2.
+
+8.9.2 MOVE for Collections
+
+ A MOVE with "Depth: infinity" instructs that the collection
+ identified by the Request-URI be moved to the URI specified in the
+ Destination header, and all resources identified by its internal
+ member URIs are to be moved to locations relative to it, recursively
+ through all levels of the collection hierarchy.
+
+ The MOVE method on a collection MUST act as if a "Depth: infinity"
+ header was used on it. A client MUST NOT submit a Depth header on a
+ MOVE on a collection with any value but "infinity".
+
+ Any headers included with MOVE MUST be applied in processing every
+ resource to be moved with the exception of the Destination header.
+
+ The behavior of the Destination header is the same as given for COPY
+ on collections.
+
+
+
+
+Goland, et al. Standards Track [Page 42]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ When the MOVE method has completed processing it MUST have created a
+ consistent namespace at both the source and destination (see section
+ 5.1 for the definition of namespace consistency). However, if an
+ error occurs while moving an internal collection, the server MUST NOT
+ move any resources identified by members of the failed collection
+ (i.e., the server must skip the error-causing subtree), as this would
+ create an inconsistent namespace. In this case, after detecting the
+ error, the move operation SHOULD try to finish as much of the
+ original move as possible (i.e., the server should still attempt to
+ move other subtrees and the resources identified by their members,
+ that are not descendents of an error-causing collection). So, for
+ example, if an infinite depth move is performed on collection /a/,
+ which contains collections /a/b/ and /a/c/, and an error occurs
+ moving /a/b/, an attempt should still be made to try moving /a/c/.
+ Similarly, after encountering an error moving a non-collection
+ resource as part of an infinite depth move, the server SHOULD try to
+ finish as much of the original move operation as possible.
+
+ If an error occurs with a resource other than the resource identified
+ in the Request-URI then the response MUST be a 207 (Multi-Status).
+
+ The 424 (Failed Dependency) status code SHOULD NOT be returned in the
+ 207 (Multi-Status) response from a MOVE method. These errors can be
+ safely omitted because the client will know that the progeny of a
+ resource could not be moved when the client receives an error for the
+ parent. Additionally 201 (Created)/204 (No Content) responses SHOULD
+ NOT be returned as values in 207 (Multi-Status) responses from a
+ MOVE. These responses can be safely omitted because they are the
+ default success codes.
+
+8.9.3 MOVE and the Overwrite Header
+
+ If a resource exists at the destination and the Overwrite header is
+ "T" then prior to performing the move the server MUST perform a
+ DELETE with "Depth: infinity" on the destination resource. If the
+ Overwrite header is set to "F" then the operation will fail.
+
+8.9.4 Status Codes
+
+ 201 (Created) - The source resource was successfully moved, and a new
+ resource was created at the destination.
+
+ 204 (No Content) - The source resource was successfully moved to a
+ pre-existing destination resource.
+
+ 403 (Forbidden) _ The source and destination URIs are the same.
+
+
+
+
+
+Goland, et al. Standards Track [Page 43]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ 409 (Conflict) _ A resource cannot be created at the destination
+ until one or more intermediate collections have been created.
+
+ 412 (Precondition Failed) - The server was unable to maintain the
+ liveness of the properties listed in the propertybehavior XML element
+ or the Overwrite header is "F" and the state of the destination
+ resource is non-null.
+
+ 423 (Locked) - The source or the destination resource was locked.
+
+ 502 (Bad Gateway) - This may occur when the destination is on another
+ server and the destination server refuses to accept the resource.
+
+8.9.5 Example - MOVE of a Non-Collection
+
+ This example shows resource
+ http://www.ics.uci.edu/~fielding/index.html being moved to the
+ location http://www.ics.uci.edu/users/f/fielding/index.html. The
+ contents of the destination resource would have been overwritten if
+ the destination resource had been non-null. In this case, since
+ there was nothing at the destination resource, the response code is
+ 201 (Created).
+
+ >>Request
+
+ MOVE /~fielding/index.html HTTP/1.1
+ Host: www.ics.uci.edu
+ Destination: http://www.ics.uci.edu/users/f/fielding/index.html
+
+ >>Response
+
+ HTTP/1.1 201 Created
+ Location: http://www.ics.uci.edu/users/f/fielding/index.html
+
+
+8.9.6 Example - MOVE of a Collection
+
+ >>Request
+
+ MOVE /container/ HTTP/1.1
+ Host: www.foo.bar
+ Destination: http://www.foo.bar/othercontainer/
+ Overwrite: F
+ If: (<opaquelocktoken:fe184f2e-6eec-41d0-c765-01adc56e6bb4>)
+ (<opaquelocktoken:e454f3f3-acdc-452a-56c7-00a5c91e4b77>)
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+
+
+
+Goland, et al. Standards Track [Page 44]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <d:propertybehavior xmlns:d='DAV:'>
+ <d:keepalive>*</d:keepalive>
+ </d:propertybehavior>
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <d:multistatus xmlns:d='DAV:'>
+ <d:response>
+ <d:href>http://www.foo.bar/othercontainer/C2/</d:href>
+ <d:status>HTTP/1.1 423 Locked</d:status>
+ </d:response>
+ </d:multistatus>
+
+ In this example the client has submitted a number of lock tokens with
+ the request. A lock token will need to be submitted for every
+ resource, both source and destination, anywhere in the scope of the
+ method, that is locked. In this case the proper lock token was not
+ submitted for the destination http://www.foo.bar/othercontainer/C2/.
+ This means that the resource /container/C2/ could not be moved.
+ Because there was an error copying /container/C2/, none of
+ /container/C2's members were copied. However no errors were listed
+ for those members due to the error minimization rules given in
+ section 8.8.3. User agent authentication has previously occurred via
+ a mechanism outside the scope of the HTTP protocol, in an underlying
+ transport layer.
+
+8.10 LOCK Method
+
+ The following sections describe the LOCK method, which is used to
+ take out a lock of any access type. These sections on the LOCK
+ method describe only those semantics that are specific to the LOCK
+ method and are independent of the access type of the lock being
+ requested.
+
+ Any resource which supports the LOCK method MUST, at minimum, support
+ the XML request and response formats defined herein.
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 45]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.10.1 Operation
+
+ A LOCK method invocation creates the lock specified by the lockinfo
+ XML element on the Request-URI. Lock method requests SHOULD have a
+ XML request body which contains an owner XML element for this lock
+ request, unless this is a refresh request. The LOCK request may have
+ a Timeout header.
+
+ Clients MUST assume that locks may arbitrarily disappear at any time,
+ regardless of the value given in the Timeout header. The Timeout
+ header only indicates the behavior of the server if "extraordinary"
+ circumstances do not occur. For example, an administrator may remove
+ a lock at any time or the system may crash in such a way that it
+ loses the record of the lock's existence. The response MUST contain
+ the value of the lockdiscovery property in a prop XML element.
+
+ In order to indicate the lock token associated with a newly created
+ lock, a Lock-Token response header MUST be included in the response
+ for every successful LOCK request for a new lock. Note that the
+ Lock-Token header would not be returned in the response for a
+ successful refresh LOCK request because a new lock was not created.
+
+8.10.2 The Effect of Locks on Properties and Collections
+
+ The scope of a lock is the entire state of the resource, including
+ its body and associated properties. As a result, a lock on a
+ resource MUST also lock the resource's properties.
+
+ For collections, a lock also affects the ability to add or remove
+ members. The nature of the effect depends upon the type of access
+ control involved.
+
+8.10.3 Locking Replicated Resources
+
+ A resource may be made available through more than one URI. However
+ locks apply to resources, not URIs. Therefore a LOCK request on a
+ resource MUST NOT succeed if can not be honored by all the URIs
+ through which the resource is addressable.
+
+8.10.4 Depth and Locking
+
+ The Depth header may be used with the LOCK method. Values other than
+ 0 or infinity MUST NOT be used with the Depth header on a LOCK
+ method. All resources that support the LOCK method MUST support the
+ Depth header.
+
+ A Depth header of value 0 means to just lock the resource specified
+ by the Request-URI.
+
+
+
+Goland, et al. Standards Track [Page 46]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ If the Depth header is set to infinity then the resource specified in
+ the Request-URI along with all its internal members, all the way down
+ the hierarchy, are to be locked. A successful result MUST return a
+ single lock token which represents all the resources that have been
+ locked. If an UNLOCK is successfully executed on this token, all
+ associated resources are unlocked. If the lock cannot be granted to
+ all resources, a 409 (Conflict) status code MUST be returned with a
+ response entity body containing a multistatus XML element describing
+ which resource(s) prevented the lock from being granted. Hence,
+ partial success is not an option. Either the entire hierarchy is
+ locked or no resources are locked.
+
+ If no Depth header is submitted on a LOCK request then the request
+ MUST act as if a "Depth:infinity" had been submitted.
+
+8.10.5 Interaction with other Methods
+
+ The interaction of a LOCK with various methods is dependent upon the
+ lock type. However, independent of lock type, a successful DELETE of
+ a resource MUST cause all of its locks to be removed.
+
+8.10.6 Lock Compatibility Table
+
+ The table below describes the behavior that occurs when a lock
+ request is made on a resource.
+
+ Current lock state/ | Shared Lock | Exclusive
+ Lock request | | Lock
+ =====================+=================+==============
+ None | True | True
+ ---------------------+-----------------+--------------
+ Shared Lock | True | False
+ ---------------------+-----------------+--------------
+ Exclusive Lock | False | False*
+ ------------------------------------------------------
+
+ Legend: True = lock may be granted. False = lock MUST NOT be
+ granted. *=It is illegal for a principal to request the same lock
+ twice.
+
+ The current lock state of a resource is given in the leftmost column,
+ and lock requests are listed in the first row. The intersection of a
+ row and column gives the result of a lock request. For example, if a
+ shared lock is held on a resource, and an exclusive lock is
+ requested, the table entry is "false", indicating the lock must not
+ be granted.
+
+
+
+
+
+Goland, et al. Standards Track [Page 47]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.10.7 Status Codes
+
+ 200 (OK) - The lock request succeeded and the value of the
+ lockdiscovery property is included in the body.
+
+ 412 (Precondition Failed) - The included lock token was not
+ enforceable on this resource or the server could not satisfy the
+ request in the lockinfo XML element.
+
+ 423 (Locked) - The resource is locked, so the method has been
+ rejected.
+
+8.10.8 Example - Simple Lock Request
+
+ >>Request
+
+ LOCK /workspace/webdav/proposal.doc HTTP/1.1
+ Host: webdav.sb.aol.com
+ Timeout: Infinite, Second-4100000000
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+ Authorization: Digest username="ejw",
+ realm="ejw@webdav.sb.aol.com", nonce="...",
+ uri="/workspace/webdav/proposal.doc",
+ response="...", opaque="..."
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:lockinfo xmlns:D='DAV:'>
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ <D:owner>
+ <D:href>http://www.ics.uci.edu/~ejw/contact.html</D:href>
+ </D:owner>
+ </D:lockinfo>
+
+ >>Response
+
+ HTTP/1.1 200 OK
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:prop xmlns:D="DAV:">
+ <D:lockdiscovery>
+ <D:activelock>
+ <D:locktype><D:write/></D:locktype>
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:depth>Infinity</D:depth>
+
+
+
+Goland, et al. Standards Track [Page 48]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <D:owner>
+ <D:href>
+ http://www.ics.uci.edu/~ejw/contact.html
+ </D:href>
+ </D:owner>
+ <D:timeout>Second-604800</D:timeout>
+ <D:locktoken>
+ <D:href>
+ opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6be4
+ </D:href>
+ </D:locktoken>
+ </D:activelock>
+ </D:lockdiscovery>
+ </D:prop>
+
+ This example shows the successful creation of an exclusive write lock
+ on resource http://webdav.sb.aol.com/workspace/webdav/proposal.doc.
+ The resource http://www.ics.uci.edu/~ejw/contact.html contains
+ contact information for the owner of the lock. The server has an
+ activity-based timeout policy in place on this resource, which causes
+ the lock to automatically be removed after 1 week (604800 seconds).
+ Note that the nonce, response, and opaque fields have not been
+ calculated in the Authorization request header.
+
+8.10.9 Example - Refreshing a Write Lock
+
+ >>Request
+
+ LOCK /workspace/webdav/proposal.doc HTTP/1.1
+ Host: webdav.sb.aol.com
+ Timeout: Infinite, Second-4100000000
+ If: (<opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6be4>)
+ Authorization: Digest username="ejw",
+ realm="ejw@webdav.sb.aol.com", nonce="...",
+ uri="/workspace/webdav/proposal.doc",
+ response="...", opaque="..."
+
+ >>Response
+
+ HTTP/1.1 200 OK
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:prop xmlns:D="DAV:">
+ <D:lockdiscovery>
+ <D:activelock>
+ <D:locktype><D:write/></D:locktype>
+
+
+
+Goland, et al. Standards Track [Page 49]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:depth>Infinity</D:depth>
+ <D:owner>
+ <D:href>
+ http://www.ics.uci.edu/~ejw/contact.html
+ </D:href>
+ </D:owner>
+ <D:timeout>Second-604800</D:timeout>
+ <D:locktoken>
+ <D:href>
+ opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6be4
+ </D:href>
+ </D:locktoken>
+ </D:activelock>
+ </D:lockdiscovery>
+ </D:prop>
+
+ This request would refresh the lock, resetting any time outs. Notice
+ that the client asked for an infinite time out but the server choose
+ to ignore the request. In this example, the nonce, response, and
+ opaque fields have not been calculated in the Authorization request
+ header.
+
+8.10.10 Example - Multi-Resource Lock Request
+
+ >>Request
+
+ LOCK /webdav/ HTTP/1.1
+ Host: webdav.sb.aol.com
+ Timeout: Infinite, Second-4100000000
+ Depth: infinity
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+ Authorization: Digest username="ejw",
+ realm="ejw@webdav.sb.aol.com", nonce="...",
+ uri="/workspace/webdav/proposal.doc",
+ response="...", opaque="..."
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:lockinfo xmlns:D="DAV:">
+ <D:locktype><D:write/></D:locktype>
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:owner>
+ <D:href>http://www.ics.uci.edu/~ejw/contact.html</D:href>
+ </D:owner>
+ </D:lockinfo>
+
+ >>Response
+
+
+
+Goland, et al. Standards Track [Page 50]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D="DAV:">
+ <D:response>
+ <D:href>http://webdav.sb.aol.com/webdav/secret</D:href>
+ <D:status>HTTP/1.1 403 Forbidden</D:status>
+ </D:response>
+ <D:response>
+ <D:href>http://webdav.sb.aol.com/webdav/</D:href>
+ <D:propstat>
+ <D:prop><D:lockdiscovery/></D:prop>
+ <D:status>HTTP/1.1 424 Failed Dependency</D:status>
+ </D:propstat>
+ </D:response>
+ </D:multistatus>
+
+ This example shows a request for an exclusive write lock on a
+ collection and all its children. In this request, the client has
+ specified that it desires an infinite length lock, if available,
+ otherwise a timeout of 4.1 billion seconds, if available. The request
+ entity body contains the contact information for the principal taking
+ out the lock, in this case a web page URL.
+
+ The error is a 403 (Forbidden) response on the resource
+ http://webdav.sb.aol.com/webdav/secret. Because this resource could
+ not be locked, none of the resources were locked. Note also that the
+ lockdiscovery property for the Request-URI has been included as
+ required. In this example the lockdiscovery property is empty which
+ means that there are no outstanding locks on the resource.
+
+ In this example, the nonce, response, and opaque fields have not been
+ calculated in the Authorization request header.
+
+8.11 UNLOCK Method
+
+ The UNLOCK method removes the lock identified by the lock token in
+ the Lock-Token request header from the Request-URI, and all other
+ resources included in the lock. If all resources which have been
+ locked under the submitted lock token can not be unlocked then the
+ UNLOCK request MUST fail.
+
+ Any DAV compliant resource which supports the LOCK method MUST
+ support the UNLOCK method.
+
+
+
+
+
+Goland, et al. Standards Track [Page 51]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+8.11.1 Example - UNLOCK
+
+ >>Request
+
+ UNLOCK /workspace/webdav/info.doc HTTP/1.1
+ Host: webdav.sb.aol.com
+ Lock-Token: <opaquelocktoken:a515cfa4-5da4-22e1-f5b5-00a0451e6bf7>
+ Authorization: Digest username="ejw",
+ realm="ejw@webdav.sb.aol.com", nonce="...",
+ uri="/workspace/webdav/proposal.doc",
+ response="...", opaque="..."
+
+ >>Response
+
+ HTTP/1.1 204 No Content
+
+ In this example, the lock identified by the lock token
+ "opaquelocktoken:a515cfa4-5da4-22e1-f5b5-00a0451e6bf7" is
+ successfully removed from the resource
+ http://webdav.sb.aol.com/workspace/webdav/info.doc. If this lock
+ included more than just one resource, the lock is removed from all
+ resources included in the lock. The 204 (No Content) status code is
+ used instead of 200 (OK) because there is no response entity body.
+
+ In this example, the nonce, response, and opaque fields have not been
+ calculated in the Authorization request header.
+
+9 HTTP Headers for Distributed Authoring
+
+9.1 DAV Header
+
+ DAV = "DAV" ":" "1" ["," "2"] ["," 1#extend]
+
+ This header indicates that the resource supports the DAV schema and
+ protocol as specified. All DAV compliant resources MUST return the
+ DAV header on all OPTIONS responses.
+
+ The value is a list of all compliance classes that the resource
+ supports. Note that above a comma has already been added to the 2.
+ This is because a resource can not be level 2 compliant unless it is
+ also level 1 compliant. Please refer to section 15 for more details.
+ In general, however, support for one compliance class does not entail
+ support for any other.
+
+9.2 Depth Header
+
+ Depth = "Depth" ":" ("0" | "1" | "infinity")
+
+
+
+
+Goland, et al. Standards Track [Page 52]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ The Depth header is used with methods executed on resources which
+ could potentially have internal members to indicate whether the
+ method is to be applied only to the resource ("Depth: 0"), to the
+ resource and its immediate children, ("Depth: 1"), or the resource
+ and all its progeny ("Depth: infinity").
+
+ The Depth header is only supported if a method's definition
+ explicitly provides for such support.
+
+ The following rules are the default behavior for any method that
+ supports the Depth header. A method may override these defaults by
+ defining different behavior in its definition.
+
+ Methods which support the Depth header may choose not to support all
+ of the header's values and may define, on a case by case basis, the
+ behavior of the method if a Depth header is not present. For example,
+ the MOVE method only supports "Depth: infinity" and if a Depth header
+ is not present will act as if a "Depth: infinity" header had been
+ applied.
+
+ Clients MUST NOT rely upon methods executing on members of their
+ hierarchies in any particular order or on the execution being atomic
+ unless the particular method explicitly provides such guarantees.
+
+ Upon execution, a method with a Depth header will perform as much of
+ its assigned task as possible and then return a response specifying
+ what it was able to accomplish and what it failed to do.
+
+ So, for example, an attempt to COPY a hierarchy may result in some of
+ the members being copied and some not.
+
+ Any headers on a method that has a defined interaction with the Depth
+ header MUST be applied to all resources in the scope of the method
+ except where alternative behavior is explicitly defined. For example,
+ an If-Match header will have its value applied against every resource
+ in the method's scope and will cause the method to fail if the header
+ fails to match.
+
+ If a resource, source or destination, within the scope of the method
+ with a Depth header is locked in such a way as to prevent the
+ successful execution of the method, then the lock token for that
+ resource MUST be submitted with the request in the If request header.
+
+ The Depth header only specifies the behavior of the method with
+ regards to internal children. If a resource does not have internal
+ children then the Depth header MUST be ignored.
+
+
+
+
+
+Goland, et al. Standards Track [Page 53]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Please note, however, that it is always an error to submit a value
+ for the Depth header that is not allowed by the method's definition.
+ Thus submitting a "Depth: 1" on a COPY, even if the resource does not
+ have internal members, will result in a 400 (Bad Request). The method
+ should fail not because the resource doesn't have internal members,
+ but because of the illegal value in the header.
+
+9.3 Destination Header
+
+ Destination = "Destination" ":" absoluteURI
+
+ The Destination header specifies the URI which identifies a
+ destination resource for methods such as COPY and MOVE, which take
+ two URIs as parameters. Note that the absoluteURI production is
+ defined in [RFC2396].
+
+9.4 If Header
+
+ If = "If" ":" ( 1*No-tag-list | 1*Tagged-list)
+ No-tag-list = List
+ Tagged-list = Resource 1*List
+ Resource = Coded-URL
+ List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")"
+ State-token = Coded-URL
+ Coded-URL = "<" absoluteURI ">"
+
+ The If header is intended to have similar functionality to the If-
+ Match header defined in section 14.25 of [RFC2068]. However the If
+ header is intended for use with any URI which represents state
+ information, referred to as a state token, about a resource as well
+ as ETags. A typical example of a state token is a lock token, and
+ lock tokens are the only state tokens defined in this specification.
+
+ All DAV compliant resources MUST honor the If header.
+
+ The If header's purpose is to describe a series of state lists. If
+ the state of the resource to which the header is applied does not
+ match any of the specified state lists then the request MUST fail
+ with a 412 (Precondition Failed). If one of the described state
+ lists matches the state of the resource then the request may succeed.
+
+ Note that the absoluteURI production is defined in [RFC2396].
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 54]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+9.4.1 No-tag-list Production
+
+ The No-tag-list production describes a series of state tokens and
+ ETags. If multiple No-tag-list productions are used then one only
+ needs to match the state of the resource for the method to be allowed
+ to continue.
+
+ If a method, due to the presence of a Depth or Destination header, is
+ applied to multiple resources then the No-tag-list production MUST be
+ applied to each resource the method is applied to.
+
+9.4.1.1 Example - No-tag-list If Header
+
+ If: (<locktoken:a-write-lock-token> ["I am an ETag"]) (["I am another
+ ETag"])
+
+ The previous header would require that any resources within the scope
+ of the method must either be locked with the specified lock token and
+ in the state identified by the "I am an ETag" ETag or in the state
+ identified by the second ETag "I am another ETag". To put the matter
+ more plainly one can think of the previous If header as being in the
+ form (or (and <locktoken:a-write-lock-token> ["I am an ETag"]) (and
+ ["I am another ETag"])).
+
+9.4.2 Tagged-list Production
+
+ The tagged-list production scopes a list production. That is, it
+ specifies that the lists following the resource specification only
+ apply to the specified resource. The scope of the resource
+ production begins with the list production immediately following the
+ resource production and ends with the next resource production, if
+ any.
+
+ When the If header is applied to a particular resource, the Tagged-
+ list productions MUST be searched to determine if any of the listed
+ resources match the operand resource(s) for the current method. If
+ none of the resource productions match the current resource then the
+ header MUST be ignored. If one of the resource productions does
+ match the name of the resource under consideration then the list
+ productions following the resource production MUST be applied to the
+ resource in the manner specified in the previous section.
+
+ The same URI MUST NOT appear more than once in a resource production
+ in an If header.
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 55]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+9.4.2.1 Example - Tagged List If header
+
+ COPY /resource1 HTTP/1.1
+ Host: www.foo.bar
+ Destination: http://www.foo.bar/resource2
+ If: <http://www.foo.bar/resource1> (<locktoken:a-write-lock-token>
+ [W/"A weak ETag"]) (["strong ETag"])
+ <http://www.bar.bar/random>(["another strong ETag"])
+
+ In this example http://www.foo.bar/resource1 is being copied to
+ http://www.foo.bar/resource2. When the method is first applied to
+ http://www.foo.bar/resource1, resource1 must be in the state
+ specified by "(<locktoken:a-write-lock-token> [W/"A weak ETag"])
+ (["strong ETag"])", that is, it either must be locked with a lock
+ token of "locktoken:a-write-lock-token" and have a weak entity tag
+ W/"A weak ETag" or it must have a strong entity tag "strong ETag".
+
+ That is the only success condition since the resource
+ http://www.bar.bar/random never has the method applied to it (the
+ only other resource listed in the If header) and
+ http://www.foo.bar/resource2 is not listed in the If header.
+
+9.4.3 not Production
+
+ Every state token or ETag is either current, and hence describes the
+ state of a resource, or is not current, and does not describe the
+ state of a resource. The boolean operation of matching a state token
+ or ETag to the current state of a resource thus resolves to a true or
+ false value. The not production is used to reverse that value. The
+ scope of the not production is the state-token or entity-tag
+ immediately following it.
+
+ If: (Not <locktoken:write1> <locktoken:write2>)
+
+ When submitted with a request, this If header requires that all
+ operand resources must not be locked with locktoken:write1 and must
+ be locked with locktoken:write2.
+
+9.4.4 Matching Function
+
+ When performing If header processing, the definition of a matching
+ state token or entity tag is as follows.
+
+ Matching entity tag: Where the entity tag matches an entity tag
+ associated with that resource.
+
+ Matching state token: Where there is an exact match between the state
+ token in the If header and any state token on the resource.
+
+
+
+Goland, et al. Standards Track [Page 56]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+9.4.5 If Header and Non-DAV Compliant Proxies
+
+ Non-DAV compliant proxies will not honor the If header, since they
+ will not understand the If header, and HTTP requires non-understood
+ headers to be ignored. When communicating with HTTP/1.1 proxies, the
+ "Cache-Control: no-cache" request header MUST be used so as to
+ prevent the proxy from improperly trying to service the request from
+ its cache. When dealing with HTTP/1.0 proxies the "Pragma: no-cache"
+ request header MUST be used for the same reason.
+
+9.5 Lock-Token Header
+
+ Lock-Token = "Lock-Token" ":" Coded-URL
+
+ The Lock-Token request header is used with the UNLOCK method to
+ identify the lock to be removed. The lock token in the Lock-Token
+ request header MUST identify a lock that contains the resource
+ identified by Request-URI as a member.
+
+ The Lock-Token response header is used with the LOCK method to
+ indicate the lock token created as a result of a successful LOCK
+ request to create a new lock.
+
+9.6 Overwrite Header
+
+ Overwrite = "Overwrite" ":" ("T" | "F")
+
+ The Overwrite header specifies whether the server should overwrite
+ the state of a non-null destination resource during a COPY or MOVE.
+ A value of "F" states that the server must not perform the COPY or
+ MOVE operation if the state of the destination resource is non-null.
+ If the overwrite header is not included in a COPY or MOVE request
+ then the resource MUST treat the request as if it has an overwrite
+ header of value "T". While the Overwrite header appears to duplicate
+ the functionality of the If-Match: * header of HTTP/1.1, If-Match
+ applies only to the Request-URI, and not to the Destination of a COPY
+ or MOVE.
+
+ If a COPY or MOVE is not performed due to the value of the Overwrite
+ header, the method MUST fail with a 412 (Precondition Failed) status
+ code.
+
+ All DAV compliant resources MUST support the Overwrite header.
+
+9.7 Status-URI Response Header
+
+ The Status-URI response header may be used with the 102 (Processing)
+ status code to inform the client as to the status of a method.
+
+
+
+Goland, et al. Standards Track [Page 57]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Status-URI = "Status-URI" ":" *(Status-Code Coded-URL) ; Status-Code
+ is defined in 6.1.1 of [RFC2068]
+
+ The URIs listed in the header are source resources which have been
+ affected by the outstanding method. The status code indicates the
+ resolution of the method on the identified resource. So, for
+ example, if a MOVE method on a collection is outstanding and a 102
+ (Processing) response with a Status-URI response header is returned,
+ the included URIs will indicate resources that have had move
+ attempted on them and what the result was.
+
+9.8 Timeout Request Header
+
+ TimeOut = "Timeout" ":" 1#TimeType
+ TimeType = ("Second-" DAVTimeOutVal | "Infinite" | Other)
+ DAVTimeOutVal = 1*digit
+ Other = "Extend" field-value ; See section 4.2 of [RFC2068]
+
+ Clients may include Timeout headers in their LOCK requests. However,
+ the server is not required to honor or even consider these requests.
+ Clients MUST NOT submit a Timeout request header with any method
+ other than a LOCK method.
+
+ A Timeout request header MUST contain at least one TimeType and may
+ contain multiple TimeType entries. The purpose of listing multiple
+ TimeType entries is to indicate multiple different values and value
+ types that are acceptable to the client. The client lists the
+ TimeType entries in order of preference.
+
+ Timeout response values MUST use a Second value, Infinite, or a
+ TimeType the client has indicated familiarity with. The server may
+ assume a client is familiar with any TimeType submitted in a Timeout
+ header.
+
+ The "Second" TimeType specifies the number of seconds that will
+ elapse between granting of the lock at the server, and the automatic
+ removal of the lock. The timeout value for TimeType "Second" MUST
+ NOT be greater than 2^32-1.
+
+ The timeout counter SHOULD be restarted any time an owner of the lock
+ sends a method to any member of the lock, including unsupported
+ methods, or methods which are unsuccessful. However the lock MUST be
+ refreshed if a refresh LOCK method is successfully received.
+
+ If the timeout expires then the lock may be lost. Specifically, if
+ the server wishes to harvest the lock upon time-out, the server
+ SHOULD act as if an UNLOCK method was executed by the server on the
+ resource using the lock token of the timed-out lock, performed with
+
+
+
+Goland, et al. Standards Track [Page 58]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ its override authority. Thus logs should be updated with the
+ disposition of the lock, notifications should be sent, etc., just as
+ they would be for an UNLOCK request.
+
+ Servers are advised to pay close attention to the values submitted by
+ clients, as they will be indicative of the type of activity the
+ client intends to perform. For example, an applet running in a
+ browser may need to lock a resource, but because of the instability
+ of the environment within which the applet is running, the applet may
+ be turned off without warning. As a result, the applet is likely to
+ ask for a relatively small timeout value so that if the applet dies,
+ the lock can be quickly harvested. However, a document management
+ system is likely to ask for an extremely long timeout because its
+ user may be planning on going off-line.
+
+ A client MUST NOT assume that just because the time-out has expired
+ the lock has been lost.
+
+10 Status Code Extensions to HTTP/1.1
+
+ The following status codes are added to those defined in HTTP/1.1
+ [RFC2068].
+
+10.1 102 Processing
+
+ The 102 (Processing) status code is an interim response used to
+ inform the client that the server has accepted the complete request,
+ but has not yet completed it. This status code SHOULD only be sent
+ when the server has a reasonable expectation that the request will
+ take significant time to complete. As guidance, if a method is taking
+ longer than 20 seconds (a reasonable, but arbitrary value) to process
+ the server SHOULD return a 102 (Processing) response. The server MUST
+ send a final response after the request has been completed.
+
+ Methods can potentially take a long period of time to process,
+ especially methods that support the Depth header. In such cases the
+ client may time-out the connection while waiting for a response. To
+ prevent this the server may return a 102 (Processing) status code to
+ indicate to the client that the server is still processing the
+ method.
+
+10.2 207 Multi-Status
+
+ The 207 (Multi-Status) status code provides status for multiple
+ independent operations (see section 11 for more information).
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 59]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+10.3 422 Unprocessable Entity
+
+ The 422 (Unprocessable Entity) status code means the server
+ understands the content type of the request entity (hence a
+ 415(Unsupported Media Type) status code is inappropriate), and the
+ syntax of the request entity is correct (thus a 400 (Bad Request)
+ status code is inappropriate) but was unable to process the contained
+ instructions. For example, this error condition may occur if an XML
+ request body contains well-formed (i.e., syntactically correct), but
+ semantically erroneous XML instructions.
+
+10.4 423 Locked
+
+ The 423 (Locked) status code means the source or destination resource
+ of a method is locked.
+
+10.5 424 Failed Dependency
+
+ The 424 (Failed Dependency) status code means that the method could
+ not be performed on the resource because the requested action
+ depended on another action and that action failed. For example, if a
+ command in a PROPPATCH method fails then, at minimum, the rest of the
+ commands will also fail with 424 (Failed Dependency).
+
+10.6 507 Insufficient Storage
+
+ The 507 (Insufficient Storage) status code means the method could not
+ be performed on the resource because the server is unable to store
+ the representation needed to successfully complete the request. This
+ condition is considered to be temporary. If the request which
+ received this status code was the result of a user action, the
+ request MUST NOT be repeated until it is requested by a separate user
+ action.
+
+11 Multi-Status Response
+
+ The default 207 (Multi-Status) response body is a text/xml or
+ application/xml HTTP entity that contains a single XML element called
+ multistatus, which contains a set of XML elements called response
+ which contain 200, 300, 400, and 500 series status codes generated
+ during the method invocation. 100 series status codes SHOULD NOT be
+ recorded in a response XML element.
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 60]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+12 XML Element Definitions
+
+ In the section below, the final line of each section gives the
+ element type declaration using the format defined in [REC-XML]. The
+ "Value" field, where present, specifies further restrictions on the
+ allowable contents of the XML element using BNF (i.e., to further
+ restrict the values of a PCDATA element).
+
+12.1 activelock XML Element
+
+ Name: activelock
+ Namespace: DAV:
+ Purpose: Describes a lock on a resource.
+
+ <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?,
+ locktoken?) >
+
+12.1.1 depth XML Element
+
+ Name: depth
+ Namespace: DAV:
+ Purpose: The value of the Depth header.
+ Value: "0" | "1" | "infinity"
+
+ <!ELEMENT depth (#PCDATA) >
+
+12.1.2 locktoken XML Element
+
+ Name: locktoken
+ Namespace: DAV:
+ Purpose: The lock token associated with a lock.
+ Description: The href contains one or more opaque lock token URIs
+ which all refer to the same lock (i.e., the OpaqueLockToken-URI
+ production in section 6.4).
+
+ <!ELEMENT locktoken (href+) >
+
+12.1.3 timeout XML Element
+
+ Name: timeout
+ Namespace: DAV:
+ Purpose: The timeout associated with a lock
+ Value: TimeType ;Defined in section 9.8
+
+ <!ELEMENT timeout (#PCDATA) >
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 61]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+12.2 collection XML Element
+
+ Name: collection
+ Namespace: DAV:
+ Purpose: Identifies the associated resource as a collection. The
+ resourcetype property of a collection resource MUST have this value.
+
+ <!ELEMENT collection EMPTY >
+
+12.3 href XML Element
+
+ Name: href
+ Namespace: DAV:
+ Purpose: Identifies the content of the element as a URI.
+ Value: URI ; See section 3.2.1 of [RFC2068]
+
+ <!ELEMENT href (#PCDATA)>
+
+12.4 link XML Element
+
+ Name: link
+ Namespace: DAV:
+ Purpose: Identifies the property as a link and contains the source
+ and destination of that link.
+ Description: The link XML element is used to provide the sources and
+ destinations of a link. The name of the property containing the link
+ XML element provides the type of the link. Link is a multi-valued
+ element, so multiple links may be used together to indicate multiple
+ links with the same type. The values in the href XML elements inside
+ the src and dst XML elements of the link XML element MUST NOT be
+ rejected if they point to resources which do not exist.
+
+ <!ELEMENT link (src+, dst+) >
+
+12.4.1 dst XML Element
+
+ Name: dst
+ Namespace: DAV:
+ Purpose: Indicates the destination of a link
+ Value: URI
+
+ <!ELEMENT dst (#PCDATA) >
+
+12.4.2 src XML Element
+
+ Name: src
+ Namespace: DAV:
+ Purpose: Indicates the source of a link.
+
+
+
+Goland, et al. Standards Track [Page 62]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Value: URI
+
+ <!ELEMENT src (#PCDATA) >
+
+12.5 lockentry XML Element
+
+ Name: lockentry
+ Namespace: DAV:
+ Purpose: Defines the types of locks that can be used with the
+ resource.
+
+ <!ELEMENT lockentry (lockscope, locktype) >
+
+12.6 lockinfo XML Element
+
+ Name: lockinfo
+ Namespace: DAV:
+ Purpose: The lockinfo XML element is used with a LOCK method to
+ specify the type of lock the client wishes to have created.
+
+ <!ELEMENT lockinfo (lockscope, locktype, owner?) >
+
+12.7 lockscope XML Element
+
+ Name: lockscope
+ Namespace: DAV:
+ Purpose: Specifies whether a lock is an exclusive lock, or a
+ shared lock.
+
+ <!ELEMENT lockscope (exclusive | shared) >
+
+12.7.1 exclusive XML Element
+
+ Name: exclusive
+ Namespace: DAV:
+ Purpose: Specifies an exclusive lock
+
+ <!ELEMENT exclusive EMPTY >
+
+12.7.2 shared XML Element
+
+ Name: shared
+ Namespace: DAV:
+ Purpose: Specifies a shared lock
+
+ <!ELEMENT shared EMPTY >
+
+
+
+
+
+Goland, et al. Standards Track [Page 63]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+12.8 locktype XML Element
+
+ Name: locktype
+ Namespace: DAV:
+ Purpose: Specifies the access type of a lock. At present, this
+ specification only defines one lock type, the write lock.
+
+ <!ELEMENT locktype (write) >
+
+12.8.1 write XML Element
+
+ Name: write
+ Namespace: DAV:
+ Purpose: Specifies a write lock.
+
+ <!ELEMENT write EMPTY >
+
+12.9 multistatus XML Element
+
+ Name: multistatus
+ Namespace: DAV:
+ Purpose: Contains multiple response messages.
+ Description: The responsedescription at the top level is used to
+ provide a general message describing the overarching nature of the
+ response. If this value is available an application may use it
+ instead of presenting the individual response descriptions contained
+ within the responses.
+
+ <!ELEMENT multistatus (response+, responsedescription?) >
+
+12.9.1 response XML Element
+
+ Name: response
+ Namespace: DAV:
+ Purpose: Holds a single response describing the effect of a
+ method on resource and/or its properties.
+ Description: A particular href MUST NOT appear more than once as the
+ child of a response XML element under a multistatus XML element.
+ This requirement is necessary in order to keep processing costs for a
+ response to linear time. Essentially, this prevents having to search
+ in order to group together all the responses by href. There are,
+ however, no requirements regarding ordering based on href values.
+
+ <!ELEMENT response (href, ((href*, status)|(propstat+)),
+ responsedescription?) >
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 64]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+12.9.1.1 propstat XML Element
+
+ Name: propstat
+ Namespace: DAV:
+ Purpose: Groups together a prop and status element that is
+ associated with a particular href element.
+ Description: The propstat XML element MUST contain one prop XML
+ element and one status XML element. The contents of the prop XML
+ element MUST only list the names of properties to which the result in
+ the status element applies.
+
+ <!ELEMENT propstat (prop, status, responsedescription?) >
+
+12.9.1.2 status XML Element
+
+ Name: status
+ Namespace: DAV:
+ Purpose: Holds a single HTTP status-line
+ Value: status-line ;status-line defined in [RFC2068]
+
+ <!ELEMENT status (#PCDATA) >
+
+12.9.2 responsedescription XML Element
+
+ Name: responsedescription
+ Namespace: DAV:
+ Purpose: Contains a message that can be displayed to the user
+ explaining the nature of the response.
+ Description: This XML element provides information suitable to be
+ presented to a user.
+
+ <!ELEMENT responsedescription (#PCDATA) >
+
+12.10 owner XML Element
+
+ Name: owner
+ Namespace: DAV:
+ Purpose: Provides information about the principal taking out a
+ lock.
+ Description: The owner XML element provides information sufficient
+ for either directly contacting a principal (such as a telephone
+ number or Email URI), or for discovering the principal (such as the
+ URL of a homepage) who owns a lock.
+
+ <!ELEMENT owner ANY>
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 65]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+12.11 prop XML element
+
+ Name: prop
+ Namespace: DAV:
+ Purpose: Contains properties related to a resource.
+ Description: The prop XML element is a generic container for
+ properties defined on resources. All elements inside a prop XML
+ element MUST define properties related to the resource. No other
+ elements may be used inside of a prop element.
+
+ <!ELEMENT prop ANY>
+
+12.12 propertybehavior XML element
+
+ Name: propertybehavior Namespace: DAV: Purpose: Specifies
+ how properties are handled during a COPY or MOVE.
+ Description: The propertybehavior XML element specifies how
+ properties are handled during a COPY or MOVE. If this XML element is
+ not included in the request body then the server is expected to act
+ as defined by the default property handling behavior of the
+ associated method. All WebDAV compliant resources MUST support the
+ propertybehavior XML element.
+
+ <!ELEMENT propertybehavior (omit | keepalive) >
+
+12.12.1 keepalive XML element
+
+ Name: keepalive
+ Namespace: DAV:
+ Purpose: Specifies requirements for the copying/moving of live
+ properties.
+ Description: If a list of URIs is included as the value of keepalive
+ then the named properties MUST be "live" after they are copied
+ (moved) to the destination resource of a COPY (or MOVE). If the
+ value "*" is given for the keepalive XML element, this designates
+ that all live properties on the source resource MUST be live on the
+ destination. If the requirements specified by the keepalive element
+ can not be honored then the method MUST fail with a 412 (Precondition
+ Failed). All DAV compliant resources MUST support the keepalive XML
+ element for use with the COPY and MOVE methods.
+ Value: "*" ; #PCDATA value can only be "*"
+
+ <!ELEMENT keepalive (#PCDATA | href+) >
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 66]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+12.12.2 omit XML element
+
+ Name: omit
+ Namespace: DAV:
+ Purpose: The omit XML element instructs the server that it should
+ use best effort to copy properties but a failure to copy a property
+ MUST NOT cause the method to fail. Description: The default behavior
+ for a COPY or MOVE is to copy/move all properties or fail the method.
+ In certain circumstances, such as when a server copies a resource
+ over another protocol such as FTP, it may not be possible to
+ copy/move the properties associated with the resource. Thus any
+ attempt to copy/move over FTP would always have to fail because
+ properties could not be moved over, even as dead properties. All DAV
+ compliant resources MUST support the omit XML element on COPY/MOVE
+ methods.
+
+ <!ELEMENT omit EMPTY >
+
+12.13 propertyupdate XML element
+
+ Name: propertyupdate
+ Namespace: DAV:
+ Purpose: Contains a request to alter the properties on a
+ resource.
+ Description: This XML element is a container for the information
+ required to modify the properties on the resource. This XML element
+ is multi-valued.
+
+ <!ELEMENT propertyupdate (remove | set)+ >
+
+12.13.1 remove XML element
+
+ Name: remove
+ Namespace: DAV:
+ Purpose: Lists the DAV properties to be removed from a resource.
+ Description: Remove instructs that the properties specified in prop
+ should be removed. Specifying the removal of a property that does
+ not exist is not an error. All the XML elements in a prop XML
+ element inside of a remove XML element MUST be empty, as only the
+ names of properties to be removed are required.
+
+ <!ELEMENT remove (prop) >
+
+12.13.2 set XML element
+
+ Name: set
+ Namespace: DAV:
+ Purpose: Lists the DAV property values to be set for a resource.
+
+
+
+Goland, et al. Standards Track [Page 67]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Description: The set XML element MUST contain only a prop XML
+ element. The elements contained by the prop XML element inside the
+ set XML element MUST specify the name and value of properties that
+ are set on the resource identified by Request-URI. If a property
+ already exists then its value is replaced. Language tagging
+ information in the property's value (in the "xml:lang" attribute, if
+ present) MUST be persistently stored along with the property, and
+ MUST be subsequently retrievable using PROPFIND.
+
+ <!ELEMENT set (prop) >
+
+12.14 propfind XML Element
+
+ Name: propfind
+ Namespace: DAV:
+ Purpose: Specifies the properties to be returned from a PROPFIND
+ method. Two special elements are specified for use with propfind,
+ allprop and propname. If prop is used inside propfind it MUST only
+ contain property names, not values.
+
+ <!ELEMENT propfind (allprop | propname | prop) >
+
+12.14.1 allprop XML Element
+
+ Name: allprop Namespace: DAV: Purpose: The allprop XML
+ element specifies that all property names and values on the resource
+ are to be returned.
+
+ <!ELEMENT allprop EMPTY >
+
+12.14.2 propname XML Element
+
+ Name: propname Namespace: DAV: Purpose: The propname XML
+ element specifies that only a list of property names on the resource
+ is to be returned.
+
+ <!ELEMENT propname EMPTY >
+
+13 DAV Properties
+
+ For DAV properties, the name of the property is also the same as the
+ name of the XML element that contains its value. In the section
+ below, the final line of each section gives the element type
+ declaration using the format defined in [REC-XML]. The "Value" field,
+ where present, specifies further restrictions on the allowable
+ contents of the XML element using BNF (i.e., to further restrict the
+ values of a PCDATA element).
+
+
+
+
+Goland, et al. Standards Track [Page 68]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+13.1 creationdate Property
+
+ Name: creationdate
+ Namespace: DAV:
+ Purpose: Records the time and date the resource was created.
+ Value: date-time ; See Appendix 2
+ Description: The creationdate property should be defined on all DAV
+ compliant resources. If present, it contains a timestamp of the
+ moment when the resource was created (i.e., the moment it had non-
+ null state).
+
+ <!ELEMENT creationdate (#PCDATA) >
+
+13.2 displayname Property
+
+ Name: displayname
+ Namespace: DAV:
+ Purpose: Provides a name for the resource that is suitable for
+ presentation to a user.
+ Description: The displayname property should be defined on all DAV
+ compliant resources. If present, the property contains a description
+ of the resource that is suitable for presentation to a user.
+
+ <!ELEMENT displayname (#PCDATA) >
+
+13.3 getcontentlanguage Property
+
+ Name: getcontentlanguage
+ Namespace: DAV:
+ Purpose: Contains the Content-Language header returned by a GET
+ without accept headers
+ Description: The getcontentlanguage property MUST be defined on any
+ DAV compliant resource that returns the Content-Language header on a
+ GET.
+ Value: language-tag ;language-tag is defined in section 14.13
+ of [RFC2068]
+
+ <!ELEMENT getcontentlanguage (#PCDATA) >
+
+13.4 getcontentlength Property
+
+ Name: getcontentlength
+ Namespace: DAV:
+ Purpose: Contains the Content-Length header returned by a GET
+ without accept headers.
+ Description: The getcontentlength property MUST be defined on any
+ DAV compliant resource that returns the Content-Length header in
+ response to a GET.
+
+
+
+Goland, et al. Standards Track [Page 69]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Value: content-length ; see section 14.14 of [RFC2068]
+
+ <!ELEMENT getcontentlength (#PCDATA) >
+
+13.5 getcontenttype Property
+
+ Name: getcontenttype
+ Namespace: DAV:
+ Purpose: Contains the Content-Type header returned by a GET
+ without accept headers.
+ Description: This getcontenttype property MUST be defined on any DAV
+ compliant resource that returns the Content-Type header in response
+ to a GET.
+ Value: media-type ; defined in section 3.7 of [RFC2068]
+
+ <!ELEMENT getcontenttype (#PCDATA) >
+
+13.6 getetag Property
+
+ Name: getetag
+ Namespace: DAV:
+ Purpose: Contains the ETag header returned by a GET without
+ accept headers.
+ Description: The getetag property MUST be defined on any DAV
+ compliant resource that returns the Etag header.
+ Value: entity-tag ; defined in section 3.11 of [RFC2068]
+
+ <!ELEMENT getetag (#PCDATA) >
+
+13.7 getlastmodified Property
+
+ Name: getlastmodified
+ Namespace: DAV:
+ Purpose: Contains the Last-Modified header returned by a GET
+ method without accept headers.
+ Description: Note that the last-modified date on a resource may
+ reflect changes in any part of the state of the resource, not
+ necessarily just a change to the response to the GET method. For
+ example, a change in a property may cause the last-modified date to
+ change. The getlastmodified property MUST be defined on any DAV
+ compliant resource that returns the Last-Modified header in response
+ to a GET.
+ Value: HTTP-date ; defined in section 3.3.1 of [RFC2068]
+
+ <!ELEMENT getlastmodified (#PCDATA) >
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 70]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+13.8 lockdiscovery Property
+
+ Name: lockdiscovery
+ Namespace: DAV:
+ Purpose: Describes the active locks on a resource
+ Description: The lockdiscovery property returns a listing of who has
+ a lock, what type of lock he has, the timeout type and the time
+ remaining on the timeout, and the associated lock token. The server
+ is free to withhold any or all of this information if the requesting
+ principal does not have sufficient access rights to see the requested
+ data.
+
+ <!ELEMENT lockdiscovery (activelock)* >
+
+13.8.1 Example - Retrieving the lockdiscovery Property
+
+ >>Request
+
+ PROPFIND /container/ HTTP/1.1
+ Host: www.foo.bar
+ Content-Length: xxxx
+ Content-Type: text/xml; charset="utf-8"
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D='DAV:'>
+ <D:prop><D:lockdiscovery/></D:prop>
+ </D:propfind>
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D='DAV:'>
+ <D:response>
+ <D:href>http://www.foo.bar/container/</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:lockdiscovery>
+ <D:activelock>
+ <D:locktype><D:write/></D:locktype>
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:depth>0</D:depth>
+ <D:owner>Jane Smith</D:owner>
+ <D:timeout>Infinite</D:timeout>
+ <D:locktoken>
+
+
+
+Goland, et al. Standards Track [Page 71]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <D:href>
+ opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76
+ </D:href>
+ </D:locktoken>
+ </D:activelock>
+ </D:lockdiscovery>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ </D:multistatus>
+
+ This resource has a single exclusive write lock on it, with an
+ infinite timeout.
+
+13.9 resourcetype Property
+
+ Name: resourcetype
+ Namespace: DAV:
+ Purpose: Specifies the nature of the resource.
+ Description: The resourcetype property MUST be defined on all DAV
+ compliant resources. The default value is empty.
+
+ <!ELEMENT resourcetype ANY >
+
+13.10 source Property
+
+ Name: source
+ Namespace: DAV:
+ Purpose: The destination of the source link identifies the
+ resource that contains the unprocessed source of the link's source.
+ Description: The source of the link (src) is typically the URI of the
+ output resource on which the link is defined, and there is typically
+ only one destination (dst) of the link, which is the URI where the
+ unprocessed source of the resource may be accessed. When more than
+ one link destination exists, this specification asserts no policy on
+ ordering.
+
+ <!ELEMENT source (link)* >
+
+13.10.1 Example - A source Property
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:prop xmlns:D="DAV:" xmlns:F="http://www.foocorp.com/Project/">
+ <D:source>
+ <D:link>
+ <F:projfiles>Source</F:projfiles>
+ <D:src>http://foo.bar/program</D:src>
+
+
+
+Goland, et al. Standards Track [Page 72]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <D:dst>http://foo.bar/src/main.c</D:dst>
+ </D:link>
+ <D:link>
+ <F:projfiles>Library</F:projfiles>
+ <D:src>http://foo.bar/program</D:src>
+ <D:dst>http://foo.bar/src/main.lib</D:dst>
+ </D:link>
+ <D:link>
+ <F:projfiles>Makefile</F:projfiles>
+ <D:src>http://foo.bar/program</D:src>
+ <D:dst>http://foo.bar/src/makefile</D:dst>
+ </D:link>
+ </D:source>
+ </D:prop>
+
+ In this example the resource http://foo.bar/program has a source
+ property that contains three links. Each link contains three
+ elements, two of which, src and dst, are part of the DAV schema
+ defined in this document, and one which is defined by the schema
+ http://www.foocorp.com/project/ (Source, Library, and Makefile). A
+ client which only implements the elements in the DAV spec will not
+ understand the foocorp elements and will ignore them, thus seeing the
+ expected source and destination links. An enhanced client may know
+ about the foocorp elements and be able to present the user with
+ additional information about the links. This example demonstrates
+ the power of XML markup, allowing element values to be enhanced
+ without breaking older clients.
+
+13.11 supportedlock Property
+
+ Name: supportedlock
+ Namespace: DAV:
+ Purpose: To provide a listing of the lock capabilities supported
+ by the resource.
+ Description: The supportedlock property of a resource returns a
+ listing of the combinations of scope and access types which may be
+ specified in a lock request on the resource. Note that the actual
+ contents are themselves controlled by access controls so a server is
+ not required to provide information the client is not authorized to
+ see.
+
+ <!ELEMENT supportedlock (lockentry)* >
+
+13.11.1 Example - Retrieving the supportedlock Property
+
+ >>Request
+
+ PROPFIND /container/ HTTP/1.1
+
+
+
+Goland, et al. Standards Track [Page 73]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Host: www.foo.bar
+ Content-Length: xxxx
+ Content-Type: text/xml; charset="utf-8"
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:">
+ <D:prop><D:supportedlock/></D:prop>
+ </D:propfind>
+
+ >>Response
+
+ HTTP/1.1 207 Multi-Status
+ Content-Type: text/xml; charset="utf-8"
+ Content-Length: xxxx
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:multistatus xmlns:D="DAV:">
+ <D:response>
+ <D:href>http://www.foo.bar/container/</D:href>
+ <D:propstat>
+ <D:prop>
+ <D:supportedlock>
+ <D:lockentry>
+ <D:lockscope><D:exclusive/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ </D:lockentry>
+ <D:lockentry>
+ <D:lockscope><D:shared/></D:lockscope>
+ <D:locktype><D:write/></D:locktype>
+ </D:lockentry>
+ </D:supportedlock>
+ </D:prop>
+ <D:status>HTTP/1.1 200 OK</D:status>
+ </D:propstat>
+ </D:response>
+ </D:multistatus>
+
+14 Instructions for Processing XML in DAV
+
+ All DAV compliant resources MUST ignore any unknown XML element and
+ all its children encountered while processing a DAV method that uses
+ XML as its command language.
+
+ This restriction also applies to the processing, by clients, of DAV
+ property values where unknown XML elements SHOULD be ignored unless
+ the property's schema declares otherwise.
+
+
+
+
+
+Goland, et al. Standards Track [Page 74]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ This restriction does not apply to setting dead DAV properties on the
+ server where the server MUST record unknown XML elements.
+
+ Additionally, this restriction does not apply to the use of XML where
+ XML happens to be the content type of the entity body, for example,
+ when used as the body of a PUT.
+
+ Since XML can be transported as text/xml or application/xml, a DAV
+ server MUST accept DAV method requests with XML parameters
+ transported as either text/xml or application/xml, and DAV client
+ MUST accept XML responses using either text/xml or application/xml.
+
+15 DAV Compliance Classes
+
+ A DAV compliant resource can choose from two classes of compliance.
+ A client can discover the compliance classes of a resource by
+ executing OPTIONS on the resource, and examining the "DAV" header
+ which is returned.
+
+ Since this document describes extensions to the HTTP/1.1 protocol,
+ minimally all DAV compliant resources, clients, and proxies MUST be
+ compliant with [RFC2068].
+
+ Compliance classes are not necessarily sequential. A resource that is
+ class 2 compliant must also be class 1 compliant; but if additional
+ compliance classes are defined later, a resource that is class 1, 2,
+ and 4 compliant might not be class 3 compliant. Also note that
+ identifiers other than numbers may be used as compliance class
+ identifiers.
+
+15.1 Class 1
+
+ A class 1 compliant resource MUST meet all "MUST" requirements in all
+ sections of this document.
+
+ Class 1 compliant resources MUST return, at minimum, the value "1" in
+ the DAV header on all responses to the OPTIONS method.
+
+15.2 Class 2
+
+ A class 2 compliant resource MUST meet all class 1 requirements and
+ support the LOCK method, the supportedlock property, the
+ lockdiscovery property, the Time-Out response header and the Lock-
+ Token request header. A class "2" compliant resource SHOULD also
+ support the Time-Out request header and the owner XML element.
+
+ Class 2 compliant resources MUST return, at minimum, the values "1"
+ and "2" in the DAV header on all responses to the OPTIONS method.
+
+
+
+Goland, et al. Standards Track [Page 75]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+16 Internationalization Considerations
+
+ In the realm of internationalization, this specification complies
+ with the IETF Character Set Policy [RFC2277]. In this specification,
+ human-readable fields can be found either in the value of a property,
+ or in an error message returned in a response entity body. In both
+ cases, the human-readable content is encoded using XML, which has
+ explicit provisions for character set tagging and encoding, and
+ requires that XML processors read XML elements encoded, at minimum,
+ using the UTF-8 [UTF-8] encoding of the ISO 10646 multilingual plane.
+ XML examples in this specification demonstrate use of the charset
+ parameter of the Content-Type header, as defined in [RFC2376], as
+ well as the XML "encoding" attribute, which together provide charset
+ identification information for MIME and XML processors.
+
+ XML also provides a language tagging capability for specifying the
+ language of the contents of a particular XML element. XML uses
+ either IANA registered language tags (see [RFC1766]) or ISO 639
+ language tags [ISO-639] in the "xml:lang" attribute of an XML element
+ to identify the language of its content and attributes.
+
+ WebDAV applications MUST support the character set tagging, character
+ set encoding, and the language tagging functionality of the XML
+ specification. Implementors of WebDAV applications are strongly
+ encouraged to read "XML Media Types" [RFC2376] for instruction on
+ which MIME media type to use for XML transport, and on use of the
+ charset parameter of the Content-Type header.
+
+ Names used within this specification fall into three categories:
+ names of protocol elements such as methods and headers, names of XML
+ elements, and names of properties. Naming of protocol elements
+ follows the precedent of HTTP, using English names encoded in USASCII
+ for methods and headers. Since these protocol elements are not
+ visible to users, and are in fact simply long token identifiers, they
+ do not need to support encoding in multiple character sets.
+ Similarly, though the names of XML elements used in this
+ specification are English names encoded in UTF-8, these names are not
+ visible to the user, and hence do not need to support multiple
+ character set encodings.
+
+ The name of a property defined on a resource is a URI. Although some
+ applications (e.g., a generic property viewer) will display property
+ URIs directly to their users, it is expected that the typical
+ application will use a fixed set of properties, and will provide a
+ mapping from the property name URI to a human-readable field when
+ displaying the property name to a user. It is only in the case where
+
+
+
+
+
+Goland, et al. Standards Track [Page 76]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ the set of properties is not known ahead of time that an application
+ need display a property name URI to a user. We recommend that
+ applications provide human-readable property names wherever feasible.
+
+ For error reporting, we follow the convention of HTTP/1.1 status
+ codes, including with each status code a short, English description
+ of the code (e.g., 423 (Locked)). While the possibility exists that
+ a poorly crafted user agent would display this message to a user,
+ internationalized applications will ignore this message, and display
+ an appropriate message in the user's language and character set.
+
+ Since interoperation of clients and servers does not require locale
+ information, this specification does not specify any mechanism for
+ transmission of this information.
+
+17 Security Considerations
+
+ This section is provided to detail issues concerning security
+ implications of which WebDAV applications need to be aware.
+
+ All of the security considerations of HTTP/1.1 (discussed in
+ [RFC2068]) and XML (discussed in [RFC2376]) also apply to WebDAV. In
+ addition, the security risks inherent in remote authoring require
+ stronger authentication technology, introduce several new privacy
+ concerns, and may increase the hazards from poor server design.
+ These issues are detailed below.
+
+17.1 Authentication of Clients
+
+ Due to their emphasis on authoring, WebDAV servers need to use
+ authentication technology to protect not just access to a network
+ resource, but the integrity of the resource as well. Furthermore,
+ the introduction of locking functionality requires support for
+ authentication.
+
+ A password sent in the clear over an insecure channel is an
+ inadequate means for protecting the accessibility and integrity of a
+ resource as the password may be intercepted. Since Basic
+ authentication for HTTP/1.1 performs essentially clear text
+ transmission of a password, Basic authentication MUST NOT be used to
+ authenticate a WebDAV client to a server unless the connection is
+ secure. Furthermore, a WebDAV server MUST NOT send Basic
+ authentication credentials in a WWW-Authenticate header unless the
+ connection is secure. Examples of secure connections include a
+ Transport Layer Security (TLS) connection employing a strong cipher
+ suite with mutual authentication of client and server, or a
+ connection over a network which is physically secure, for example, an
+ isolated network in a building with restricted access.
+
+
+
+Goland, et al. Standards Track [Page 77]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ WebDAV applications MUST support the Digest authentication scheme
+ [RFC2069]. Since Digest authentication verifies that both parties to
+ a communication know a shared secret, a password, without having to
+ send that secret in the clear, Digest authentication avoids the
+ security problems inherent in Basic authentication while providing a
+ level of authentication which is useful in a wide range of scenarios.
+
+17.2 Denial of Service
+
+ Denial of service attacks are of special concern to WebDAV servers.
+ WebDAV plus HTTP enables denial of service attacks on every part of a
+ system's resources.
+
+ The underlying storage can be attacked by PUTting extremely large
+ files.
+
+ Asking for recursive operations on large collections can attack
+ processing time.
+
+ Making multiple pipelined requests on multiple connections can attack
+ network connections.
+
+ WebDAV servers need to be aware of the possibility of a denial of
+ service attack at all levels.
+
+17.3 Security through Obscurity
+
+ WebDAV provides, through the PROPFIND method, a mechanism for listing
+ the member resources of a collection. This greatly diminishes the
+ effectiveness of security or privacy techniques that rely only on the
+ difficulty of discovering the names of network resources. Users of
+ WebDAV servers are encouraged to use access control techniques to
+ prevent unwanted access to resources, rather than depending on the
+ relative obscurity of their resource names.
+
+17.4 Privacy Issues Connected to Locks
+
+ When submitting a lock request a user agent may also submit an owner
+ XML field giving contact information for the person taking out the
+ lock (for those cases where a person, rather than a robot, is taking
+ out the lock). This contact information is stored in a lockdiscovery
+ property on the resource, and can be used by other collaborators to
+ begin negotiation over access to the resource. However, in many
+ cases this contact information can be very private, and should not be
+ widely disseminated. Servers SHOULD limit read access to the
+ lockdiscovery property as appropriate. Furthermore, user agents
+
+
+
+
+
+Goland, et al. Standards Track [Page 78]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ SHOULD provide control over whether contact information is sent at
+ all, and if contact information is sent, control over exactly what
+ information is sent.
+
+17.5 Privacy Issues Connected to Properties
+
+ Since property values are typically used to hold information such as
+ the author of a document, there is the possibility that privacy
+ concerns could arise stemming from widespread access to a resource's
+ property data. To reduce the risk of inadvertent release of private
+ information via properties, servers are encouraged to develop access
+ control mechanisms that separate read access to the resource body and
+ read access to the resource's properties. This allows a user to
+ control the dissemination of their property data without overly
+ restricting access to the resource's contents.
+
+17.6 Reduction of Security due to Source Link
+
+ HTTP/1.1 warns against providing read access to script code because
+ it may contain sensitive information. Yet WebDAV, via its source
+ link facility, can potentially provide a URI for script resources so
+ they may be authored. For HTTP/1.1, a server could reasonably
+ prevent access to source resources due to the predominance of read-
+ only access. WebDAV, with its emphasis on authoring, encourages read
+ and write access to source resources, and provides the source link
+ facility to identify the source. This reduces the security benefits
+ of eliminating access to source resources. Users and administrators
+ of WebDAV servers should be very cautious when allowing remote
+ authoring of scripts, limiting read and write access to the source
+ resources to authorized principals.
+
+17.7 Implications of XML External Entities
+
+ XML supports a facility known as "external entities", defined in
+ section 4.2.2 of [REC-XML], which instruct an XML processor to
+ retrieve and perform an inline include of XML located at a particular
+ URI. An external XML entity can be used to append or modify the
+ document type declaration (DTD) associated with an XML document. An
+ external XML entity can also be used to include XML within the
+ content of an XML document. For non-validating XML, such as the XML
+ used in this specification, including an external XML entity is not
+ required by [REC-XML]. However, [REC-XML] does state that an XML
+ processor may, at its discretion, include the external XML entity.
+
+ External XML entities have no inherent trustworthiness and are
+ subject to all the attacks that are endemic to any HTTP GET request.
+ Furthermore, it is possible for an external XML entity to modify the
+ DTD, and hence affect the final form of an XML document, in the worst
+
+
+
+Goland, et al. Standards Track [Page 79]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ case significantly modifying its semantics, or exposing the XML
+ processor to the security risks discussed in [RFC2376]. Therefore,
+ implementers must be aware that external XML entities should be
+ treated as untrustworthy.
+
+ There is also the scalability risk that would accompany a widely
+ deployed application which made use of external XML entities. In
+ this situation, it is possible that there would be significant
+ numbers of requests for one external XML entity, potentially
+ overloading any server which fields requests for the resource
+ containing the external XML entity.
+
+17.8 Risks Connected with Lock Tokens
+
+ This specification, in section 6.4, requires the use of Universal
+ Unique Identifiers (UUIDs) for lock tokens, in order to guarantee
+ their uniqueness across space and time. UUIDs, as defined in [ISO-
+ 11578], contain a "node" field which "consists of the IEEE address,
+ usually the host address. For systems with multiple IEEE 802 nodes,
+ any available node address can be used." Since a WebDAV server will
+ issue many locks over its lifetime, the implication is that it will
+ also be publicly exposing its IEEE 802 address.
+
+ There are several risks associated with exposure of IEEE 802
+ addresses. Using the IEEE 802 address:
+
+ * It is possible to track the movement of hardware from subnet to
+ subnet.
+
+ * It may be possible to identify the manufacturer of the hardware
+ running a WebDAV server.
+
+ * It may be possible to determine the number of each type of computer
+ running WebDAV.
+
+ Section 6.4.1 of this specification details an alternate mechanism
+ for generating the "node" field of a UUID without using an IEEE 802
+ address, which alleviates the risks associated with exposure of IEEE
+ 802 addresses by using an alternate source of uniqueness.
+
+18 IANA Considerations
+
+ This document defines two namespaces, the namespace of property
+ names, and the namespace of WebDAV-specific XML elements used within
+ property values.
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 80]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ URIs are used for both names, for several reasons. Assignment of a
+ URI does not require a request to a central naming authority, and
+ hence allow WebDAV property names and XML elements to be quickly
+ defined by any WebDAV user or application. URIs also provide a
+ unique address space, ensuring that the distributed users of WebDAV
+ will not have collisions among the property names and XML elements
+ they create.
+
+ This specification defines a distinguished set of property names and
+ XML elements that are understood by all WebDAV applications. The
+ property names and XML elements in this specification are all derived
+ from the base URI DAV: by adding a suffix to this URI, for example,
+ DAV:creationdate for the "creationdate" property.
+
+ This specification also defines a URI scheme for the encoding of lock
+ tokens, the opaquelocktoken URI scheme described in section 6.4.
+
+ To ensure correct interoperation based on this specification, IANA
+ must reserve the URI namespaces starting with "DAV:" and with
+ "opaquelocktoken:" for use by this specification, its revisions, and
+ related WebDAV specifications.
+
+19 Intellectual Property
+
+ The following notice is copied from RFC 2026 [RFC2026], section 10.4,
+ and describes the position of the IETF concerning intellectual
+ property claims made against this document.
+
+ The IETF takes no position regarding the validity or scope of any
+ intellectual property or other rights that might be claimed to
+ pertain to the implementation or use other technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; neither does it represent that it
+ has made any effort to identify any such rights. Information on the
+ IETF's procedures with respect to rights in standards-track and
+ standards-related documentation can be found in BCP-11. Copies of
+ claims of rights made available for publication and any assurances of
+ licenses to be made available, or the result of an attempt made to
+ obtain a general license or permission for the use of such
+ proprietary rights by implementors or users of this specification can
+ be obtained from the IETF Secretariat.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights which may cover technology that may be required to practice
+ this standard. Please address the information to the IETF Executive
+ Director.
+
+
+
+
+Goland, et al. Standards Track [Page 81]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+20 Acknowledgements
+
+ A specification such as this thrives on piercing critical review and
+ withers from apathetic neglect. The authors gratefully acknowledge
+ the contributions of the following people, whose insights were so
+ valuable at every stage of our work.
+
+ Terry Allen, Harald Alvestrand, Jim Amsden, Becky Anderson, Alan
+ Babich, Sanford Barr, Dylan Barrell, Bernard Chester, Tim Berners-
+ Lee, Dan Connolly, Jim Cunningham, Ron Daniel, Jr., Jim Davis, Keith
+ Dawson, Mark Day, Brian Deen, Martin Duerst, David Durand, Lee
+ Farrell, Chuck Fay, Wesley Felter, Roy Fielding, Mark Fisher, Alan
+ Freier, George Florentine, Jim Gettys, Phill Hallam-Baker, Dennis
+ Hamilton, Steve Henning, Mead Himelstein, Alex Hopmann, Andre van der
+ Hoek, Ben Laurie, Paul Leach, Ora Lassila, Karen MacArthur, Steven
+ Martin, Larry Masinter, Michael Mealling, Keith Moore, Thomas Narten,
+ Henrik Nielsen, Kenji Ota, Bob Parker, Glenn Peterson, Jon Radoff,
+ Saveen Reddy, Henry Sanders, Christopher Seiwald, Judith Slein, Mike
+ Spreitzer, Einar Stefferud, Greg Stein, Ralph Swick, Kenji Takahashi,
+ Richard N. Taylor, Robert Thau, John Turner, Sankar Virdhagriswaran,
+ Fabio Vitali, Gregory Woodhouse, and Lauren Wood.
+
+ Two from this list deserve special mention. The contributions by
+ Larry Masinter have been invaluable, both in helping the formation of
+ the working group and in patiently coaching the authors along the
+ way. In so many ways he has set high standards we have toiled to
+ meet. The contributions of Judith Slein in clarifying the
+ requirements, and in patiently reviewing draft after draft, both
+ improved this specification and expanded our minds on document
+ management.
+
+ We would also like to thank John Turner for developing the XML DTD.
+
+21 References
+
+21.1 Normative References
+
+ [RFC1766] Alvestrand, H., "Tags for the Identification of
+ Languages", RFC 1766, March 1995.
+
+ [RFC2277] Alvestrand, H., "IETF Policy on Character Sets and
+ Languages", BCP 18, RFC 2277, January 1998.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 82]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter,
+ "Uniform Resource Identifiers (URI): Generic Syntax",
+ RFC 2396, August 1998.
+
+ [REC-XML] T. Bray, J. Paoli, C. M. Sperberg-McQueen,
+ "Extensible Markup Language (XML)." World Wide Web
+ Consortium Recommendation REC-xml-19980210.
+ http://www.w3.org/TR/1998/REC-xml-19980210.
+
+ [REC-XML-NAMES] T. Bray, D. Hollander, A. Layman, "Namespaces in
+ XML". World Wide Web Consortium Recommendation REC-
+ xml-names-19990114. http://www.w3.org/TR/1999/REC-
+ xml-names-19990114/
+
+ [RFC2069] Franks, J., Hallam-Baker, P., Hostetler, J., Leach,
+ P, Luotonen, A., Sink, E. and L. Stewart, "An
+ Extension to HTTP : Digest Access Authentication",
+ RFC 2069, January 1997.
+
+ [RFC2068] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and
+ T. Berners-Lee, "Hypertext Transfer Protocol --
+ HTTP/1.1", RFC 2068, January 1997.
+
+ [ISO-639] ISO (International Organization for Standardization).
+ ISO 639:1988. "Code for the representation of names
+ of languages."
+
+ [ISO-8601] ISO (International Organization for Standardization).
+ ISO 8601:1988. "Data elements and interchange formats
+ - Information interchange - Representation of dates
+ and times."
+
+ [ISO-11578] ISO (International Organization for Standardization).
+ ISO/IEC 11578:1996. "Information technology - Open
+ Systems Interconnection - Remote Procedure Call
+ (RPC)"
+
+ [RFC2141] Moats, R., "URN Syntax", RFC 2141, May 1997.
+
+ [UTF-8] Yergeau, F., "UTF-8, a transformation format of
+ Unicode and ISO 10646", RFC 2279, January 1998.
+
+21.2 Informational References
+
+ [RFC2026] Bradner, S., "The Internet Standards Process - Revision
+ 3", BCP 9, RFC 2026, October 1996.
+
+
+
+
+
+Goland, et al. Standards Track [Page 83]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ [RFC1807] Lasher, R. and D. Cohen, "A Format for Bibliographic
+ Records", RFC 1807, June 1995.
+
+ [WF] C. Lagoze, "The Warwick Framework: A Container
+ Architecture for Diverse Sets of Metadata", D-Lib
+ Magazine, July/August 1996.
+ http://www.dlib.org/dlib/july96/lagoze/07lagoze.html
+
+ [USMARC] Network Development and MARC Standards, Office, ed. 1994.
+ "USMARC Format for Bibliographic Data", 1994. Washington,
+ DC: Cataloging Distribution Service, Library of Congress.
+
+ [REC-PICS] J. Miller, T. Krauskopf, P. Resnick, W. Treese, "PICS
+ Label Distribution Label Syntax and Communication
+ Protocols" Version 1.1, World Wide Web Consortium
+ Recommendation REC-PICS-labels-961031.
+ http://www.w3.org/pub/WWW/TR/REC-PICS-labels-961031.html.
+
+ [RFC2291] Slein, J., Vitali, F., Whitehead, E. and D. Durand,
+ "Requirements for Distributed Authoring and Versioning
+ Protocol for the World Wide Web", RFC 2291, February 1998.
+
+ [RFC2413] Weibel, S., Kunze, J., Lagoze, C. and M. Wolf, "Dublin
+ Core Metadata for Resource Discovery", RFC 2413, September
+ 1998.
+
+ [RFC2376] Whitehead, E. and M. Murata, "XML Media Types", RFC 2376,
+ July 1998.
+
+22 Authors' Addresses
+
+ Y. Y. Goland
+ Microsoft Corporation
+ One Microsoft Way
+ Redmond, WA 98052-6399
+
+ EMail: yarong@microsoft.com
+
+
+ E. J. Whitehead, Jr.
+ Dept. Of Information and Computer Science
+ University of California, Irvine
+ Irvine, CA 92697-3425
+
+ EMail: ejw@ics.uci.edu
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 84]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ A. Faizi
+ Netscape
+ 685 East Middlefield Road
+ Mountain View, CA 94043
+
+ EMail: asad@netscape.com
+
+
+ S. R. Carter
+ Novell
+ 1555 N. Technology Way
+ M/S ORM F111
+ Orem, UT 84097-2399
+
+ EMail: srcarter@novell.com
+
+
+ D. Jensen
+ Novell
+ 1555 N. Technology Way
+ M/S ORM F111
+ Orem, UT 84097-2399
+
+ EMail: dcjensen@novell.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 85]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+23 Appendices
+
+23.1 Appendix 1 - WebDAV Document Type Definition
+
+ This section provides a document type definition, following the rules
+ in [REC-XML], for the XML elements used in the protocol stream and in
+ the values of properties. It collects the element definitions given
+ in sections 12 and 13.
+
+ <!DOCTYPE webdav-1.0 [
+
+ <!--============ XML Elements from Section 12 ==================-->
+
+ <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?,
+ locktoken?) >
+
+ <!ELEMENT lockentry (lockscope, locktype) >
+ <!ELEMENT lockinfo (lockscope, locktype, owner?) >
+
+ <!ELEMENT locktype (write) >
+ <!ELEMENT write EMPTY >
+
+ <!ELEMENT lockscope (exclusive | shared) >
+ <!ELEMENT exclusive EMPTY >
+ <!ELEMENT shared EMPTY >
+
+ <!ELEMENT depth (#PCDATA) >
+
+ <!ELEMENT owner ANY >
+
+ <!ELEMENT timeout (#PCDATA) >
+
+ <!ELEMENT locktoken (href+) >
+
+ <!ELEMENT href (#PCDATA) >
+
+ <!ELEMENT link (src+, dst+) >
+ <!ELEMENT dst (#PCDATA) >
+ <!ELEMENT src (#PCDATA) >
+
+ <!ELEMENT multistatus (response+, responsedescription?) >
+
+ <!ELEMENT response (href, ((href*, status)|(propstat+)),
+ responsedescription?) >
+ <!ELEMENT status (#PCDATA) >
+ <!ELEMENT propstat (prop, status, responsedescription?) >
+ <!ELEMENT responsedescription (#PCDATA) >
+
+
+
+
+Goland, et al. Standards Track [Page 86]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <!ELEMENT prop ANY >
+
+ <!ELEMENT propertybehavior (omit | keepalive) >
+ <!ELEMENT omit EMPTY >
+
+ <!ELEMENT keepalive (#PCDATA | href+) >
+
+ <!ELEMENT propertyupdate (remove | set)+ >
+ <!ELEMENT remove (prop) >
+ <!ELEMENT set (prop) >
+
+ <!ELEMENT propfind (allprop | propname | prop) >
+ <!ELEMENT allprop EMPTY >
+ <!ELEMENT propname EMPTY >
+
+ <!ELEMENT collection EMPTY >
+
+ <!--=========== Property Elements from Section 13 ===============-->
+ <!ELEMENT creationdate (#PCDATA) >
+ <!ELEMENT displayname (#PCDATA) >
+ <!ELEMENT getcontentlanguage (#PCDATA) >
+ <!ELEMENT getcontentlength (#PCDATA) >
+ <!ELEMENT getcontenttype (#PCDATA) >
+ <!ELEMENT getetag (#PCDATA) >
+ <!ELEMENT getlastmodified (#PCDATA) >
+ <!ELEMENT lockdiscovery (activelock)* >
+ <!ELEMENT resourcetype ANY >
+ <!ELEMENT source (link)* >
+ <!ELEMENT supportedlock (lockentry)* >
+ ]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 87]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+23.2 Appendix 2 - ISO 8601 Date and Time Profile
+
+ The creationdate property specifies the use of the ISO 8601 date
+ format [ISO-8601]. This section defines a profile of the ISO 8601
+ date format for use with this specification. This profile is quoted
+ from an Internet-Draft by Chris Newman, and is mentioned here to
+ properly attribute his work.
+
+ date-time = full-date "T" full-time
+
+ full-date = date-fullyear "-" date-month "-" date-mday
+ full-time = partial-time time-offset
+
+ date-fullyear = 4DIGIT
+ date-month = 2DIGIT ; 01-12
+ date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
+ month/year
+ time-hour = 2DIGIT ; 00-23
+ time-minute = 2DIGIT ; 00-59
+ time-second = 2DIGIT ; 00-59, 00-60 based on leap second rules
+ time-secfrac = "." 1*DIGIT
+ time-numoffset = ("+" / "-") time-hour ":" time-minute
+ time-offset = "Z" / time-numoffset
+
+ partial-time = time-hour ":" time-minute ":" time-second
+ [time-secfrac]
+
+ Numeric offsets are calculated as local time minus UTC (Coordinated
+ Universal Time). So the equivalent time in UTC can be determined by
+ subtracting the offset from the local time. For example, 18:50:00-
+ 04:00 is the same time as 22:58:00Z.
+
+ If the time in UTC is known, but the offset to local time is unknown,
+ this can be represented with an offset of "-00:00". This differs
+ from an offset of "Z" which implies that UTC is the preferred
+ reference point for the specified time.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 88]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+23.3 Appendix 3 - Notes on Processing XML Elements
+
+23.3.1 Notes on Empty XML Elements
+
+ XML supports two mechanisms for indicating that an XML element does
+ not have any content. The first is to declare an XML element of the
+ form <A></A>. The second is to declare an XML element of the form
+ <A/>. The two XML elements are semantically identical.
+
+ It is a violation of the XML specification to use the <A></A> form if
+ the associated DTD declares the element to be EMPTY (e.g., <!ELEMENT
+ A EMPTY>). If such a statement is included, then the empty element
+ format, <A/> must be used. If the element is not declared to be
+ EMPTY, then either form <A></A> or <A/> may be used for empty
+ elements.
+
+ 23.3.2 Notes on Illegal XML Processing
+
+ XML is a flexible data format that makes it easy to submit data that
+ appears legal but in fact is not. The philosophy of "Be flexible in
+ what you accept and strict in what you send" still applies, but it
+ must not be applied inappropriately. XML is extremely flexible in
+ dealing with issues of white space, element ordering, inserting new
+ elements, etc. This flexibility does not require extension,
+ especially not in the area of the meaning of elements.
+
+ There is no kindness in accepting illegal combinations of XML
+ elements. At best it will cause an unwanted result and at worst it
+ can cause real damage.
+
+23.3.2.1 Example - XML Syntax Error
+
+ The following request body for a PROPFIND method is illegal.
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:">
+ <D:allprop/>
+ <D:propname/>
+ </D:propfind>
+
+ The definition of the propfind element only allows for the allprop or
+ the propname element, not both. Thus the above is an error and must
+ be responded to with a 400 (Bad Request).
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 89]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ Imagine, however, that a server wanted to be "kind" and decided to
+ pick the allprop element as the true element and respond to it. A
+ client running over a bandwidth limited line who intended to execute
+ a propname would be in for a big surprise if the server treated the
+ command as an allprop.
+
+ Additionally, if a server were lenient and decided to reply to this
+ request, the results would vary randomly from server to server, with
+ some servers executing the allprop directive, and others executing
+ the propname directive. This reduces interoperability rather than
+ increasing it.
+
+23.3.2.2 Example - Unknown XML Element
+
+ The previous example was illegal because it contained two elements
+ that were explicitly banned from appearing together in the propfind
+ element. However, XML is an extensible language, so one can imagine
+ new elements being defined for use with propfind. Below is the
+ request body of a PROPFIND and, like the previous example, must be
+ rejected with a 400 (Bad Request) by a server that does not
+ understand the expired-props element.
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:"
+ xmlns:E="http://www.foo.bar/standards/props/">
+ <E:expired-props/>
+ </D:propfind>
+
+ To understand why a 400 (Bad Request) is returned let us look at the
+ request body as the server unfamiliar with expired-props sees it.
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:"
+ xmlns:E="http://www.foo.bar/standards/props/">
+ </D:propfind>
+
+ As the server does not understand the expired-props element,
+ according to the WebDAV-specific XML processing rules specified in
+ section 14, it must ignore it. Thus the server sees an empty
+ propfind, which by the definition of the propfind element is illegal.
+
+ Please note that had the extension been additive it would not
+ necessarily have resulted in a 400 (Bad Request). For example,
+ imagine the following request body for a PROPFIND:
+
+ <?xml version="1.0" encoding="utf-8" ?>
+ <D:propfind xmlns:D="DAV:"
+ xmlns:E="http://www.foo.bar/standards/props/">
+
+
+
+Goland, et al. Standards Track [Page 90]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ <D:propname/>
+ <E:leave-out>*boss*</E:leave-out>
+ </D:propfind>
+
+ The previous example contains the fictitious element leave-out. Its
+ purpose is to prevent the return of any property whose name matches
+ the submitted pattern. If the previous example were submitted to a
+ server unfamiliar with leave-out, the only result would be that the
+ leave-out element would be ignored and a propname would be executed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 91]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+23.4 Appendix 4 -- XML Namespaces for WebDAV
+
+23.4.1 Introduction
+
+ All DAV compliant systems MUST support the XML namespace extensions
+ as specified in [REC-XML-NAMES].
+
+23.4.2 Meaning of Qualified Names
+
+ [Note to the reader: This section does not appear in [REC-XML-NAMES],
+ but is necessary to avoid ambiguity for WebDAV XML processors.]
+
+ WebDAV compliant XML processors MUST interpret a qualified name as a
+ URI constructed by appending the LocalPart to the namespace name URI.
+
+ Example
+
+ <del:glider xmlns:del="http://www.del.jensen.org/">
+ <del:glidername>
+ Johnny Updraft
+ </del:glidername>
+ <del:glideraccidents/>
+ </del:glider>
+
+ In this example, the qualified element name "del:glider" is
+ interpreted as the URL "http://www.del.jensen.org/glider".
+
+ <bar:glider xmlns:del="http://www.del.jensen.org/">
+ <bar:glidername>
+ Johnny Updraft
+ </bar:glidername>
+ <bar:glideraccidents/>
+ </bar:glider>
+
+ Even though this example is syntactically different from the previous
+ example, it is semantically identical. Each instance of the
+ namespace name "bar" is replaced with "http://www.del.jensen.org/"
+ and then appended to the local name for each element tag. The
+ resulting tag names in this example are exactly the same as for the
+ previous example.
+
+ <foo:r xmlns:foo="http://www.del.jensen.org/glide">
+ <foo:rname>
+ Johnny Updraft
+ </foo:rname>
+ <foo:raccidents/>
+ </foo:r>
+
+
+
+
+Goland, et al. Standards Track [Page 92]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+ This example is semantically identical to the two previous ones.
+ Each instance of the namespace name "foo" is replaced with
+ "http://www.del.jensen.org/glide" which is then appended to the local
+ name for each element tag, the resulting tag names are identical to
+ those in the previous examples.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 93]
+\f
+RFC 2518 WEBDAV February 1999
+
+
+24. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Goland, et al. Standards Track [Page 94]
+\f
--- /dev/null
+
+[[ Text in double brackets is from the unofficial errata at ]]
+[[ http://skrb.org/ietf/http_errata.html ]]
+
+
+Network Working Group R. Fielding
+Request for Comments: 2616 UC Irvine
+Obsoletes: 2068 J. Gettys
+Category: Standards Track Compaq/W3C
+ J. Mogul
+ Compaq
+ H. Frystyk
+ W3C/MIT
+ L. Masinter
+ Xerox
+ P. Leach
+ Microsoft
+ T. Berners-Lee
+ W3C/MIT
+ June 1999
+
+
+ Hypertext Transfer Protocol -- HTTP/1.1
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Abstract
+
+ The Hypertext Transfer Protocol (HTTP) is an application-level
+ protocol for distributed, collaborative, hypermedia information
+ systems. It is a generic, stateless, protocol which can be used for
+ many tasks beyond its use for hypertext, such as name servers and
+ distributed object management systems, through extension of its
+ request methods, error codes and headers [47]. A feature of HTTP is
+ the typing and negotiation of data representation, allowing systems
+ to be built independently of the data being transferred.
+
+ HTTP has been in use by the World-Wide Web global information
+ initiative since 1990. This specification defines the protocol
+ referred to as "HTTP/1.1", and is an update to RFC 2068 [33].
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 1]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+Table of Contents
+
+ 1 Introduction ...................................................7
+ 1.1 Purpose......................................................7
+ 1.2 Requirements .................................................8
+ 1.3 Terminology ..................................................8
+ 1.4 Overall Operation ...........................................12
+ 2 Notational Conventions and Generic Grammar ....................14
+ 2.1 Augmented BNF ...............................................14
+ 2.2 Basic Rules .................................................15
+ 3 Protocol Parameters ...........................................17
+ 3.1 HTTP Version ................................................17
+ 3.2 Uniform Resource Identifiers ................................18
+ 3.2.1 General Syntax ...........................................19
+ 3.2.2 http URL .................................................19
+ 3.2.3 URI Comparison ...........................................20
+ 3.3 Date/Time Formats ...........................................20
+ 3.3.1 Full Date ................................................20
+ 3.3.2 Delta Seconds ............................................21
+ 3.4 Character Sets ..............................................21
+ 3.4.1 Missing Charset ..........................................22
+ 3.5 Content Codings .............................................23
+ 3.6 Transfer Codings ............................................24
+ 3.6.1 Chunked Transfer Coding ..................................25
+ 3.7 Media Types .................................................26
+ 3.7.1 Canonicalization and Text Defaults .......................27
+ 3.7.2 Multipart Types ..........................................27
+ 3.8 Product Tokens ..............................................28
+ 3.9 Quality Values ..............................................29
+ 3.10 Language Tags ...............................................29
+ 3.11 Entity Tags .................................................30
+ 3.12 Range Units .................................................30
+ 4 HTTP Message ..................................................31
+ 4.1 Message Types ...............................................31
+ 4.2 Message Headers .............................................31
+ 4.3 Message Body ................................................32
+ 4.4 Message Length ..............................................33
+ 4.5 General Header Fields .......................................34
+ 5 Request .......................................................35
+ 5.1 Request-Line ................................................35
+ 5.1.1 Method ...................................................36
+ 5.1.2 Request-URI ..............................................36
+ 5.2 The Resource Identified by a Request ........................38
+ 5.3 Request Header Fields .......................................38
+ 6 Response ......................................................39
+ 6.1 Status-Line .................................................39
+ 6.1.1 Status Code and Reason Phrase ............................39
+ 6.2 Response Header Fields ......................................41
+
+
+
+Fielding, et al. Standards Track [Page 2]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 7 Entity ........................................................42
+ 7.1 Entity Header Fields ........................................42
+ 7.2 Entity Body .................................................43
+ 7.2.1 Type .....................................................43
+ 7.2.2 Entity Length ............................................43
+ 8 Connections ...................................................44
+ 8.1 Persistent Connections ......................................44
+ 8.1.1 Purpose ..................................................44
+ 8.1.2 Overall Operation ........................................45
+ 8.1.3 Proxy Servers ............................................46
+ 8.1.4 Practical Considerations .................................46
+ 8.2 Message Transmission Requirements ...........................47
+ 8.2.1 Persistent Connections and Flow Control ..................47
+ 8.2.2 Monitoring Connections for Error Status Messages .........48
+ 8.2.3 Use of the 100 (Continue) Status .........................48
+ 8.2.4 Client Behavior if Server Prematurely Closes Connection ..50
+ 9 Method Definitions ............................................51
+ 9.1 Safe and Idempotent Methods .................................51
+ 9.1.1 Safe Methods .............................................51
+ 9.1.2 Idempotent Methods .......................................51
+ 9.2 OPTIONS .....................................................52
+ 9.3 GET .........................................................53
+ 9.4 HEAD ........................................................54
+ 9.5 POST ........................................................54
+ 9.6 PUT .........................................................55
+ 9.7 DELETE ......................................................56
+ 9.8 TRACE .......................................................56
+ 9.9 CONNECT .....................................................57
+ 10 Status Code Definitions ......................................57
+ 10.1 Informational 1xx ...........................................57
+ 10.1.1 100 Continue .............................................58
+ 10.1.2 101 Switching Protocols ..................................58
+ 10.2 Successful 2xx ..............................................58
+ 10.2.1 200 OK ...................................................58
+ 10.2.2 201 Created ..............................................59
+ 10.2.3 202 Accepted .............................................59
+ 10.2.4 203 Non-Authoritative Information ........................59
+ 10.2.5 204 No Content ...........................................60
+ 10.2.6 205 Reset Content ........................................60
+ 10.2.7 206 Partial Content ......................................60
+ 10.3 Redirection 3xx .............................................61
+ 10.3.1 300 Multiple Choices .....................................61
+ 10.3.2 301 Moved Permanently ....................................62
+ 10.3.3 302 Found ................................................62
+ 10.3.4 303 See Other ............................................63
+ 10.3.5 304 Not Modified .........................................63
+ 10.3.6 305 Use Proxy ............................................64
+ 10.3.7 306 (Unused) .............................................64
+
+
+
+Fielding, et al. Standards Track [Page 3]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 10.3.8 307 Temporary Redirect ...................................65
+ 10.4 Client Error 4xx ............................................65
+ 10.4.1 400 Bad Request .........................................65
+ 10.4.2 401 Unauthorized ........................................66
+ 10.4.3 402 Payment Required ....................................66
+ 10.4.4 403 Forbidden ...........................................66
+ 10.4.5 404 Not Found ...........................................66
+ 10.4.6 405 Method Not Allowed ..................................66
+ 10.4.7 406 Not Acceptable ......................................67
+ 10.4.8 407 Proxy Authentication Required .......................67
+ 10.4.9 408 Request Timeout .....................................67
+ 10.4.10 409 Conflict ............................................67
+ 10.4.11 410 Gone ................................................68
+ 10.4.12 411 Length Required .....................................68
+ 10.4.13 412 Precondition Failed .................................68
+ 10.4.14 413 Request Entity Too Large ............................69
+ 10.4.15 414 Request-URI Too Long ................................69
+ 10.4.16 415 Unsupported Media Type ..............................69
+ 10.4.17 416 Requested Range Not Satisfiable .....................69
+ 10.4.18 417 Expectation Failed ..................................70
+ 10.5 Server Error 5xx ............................................70
+ 10.5.1 500 Internal Server Error ................................70
+ 10.5.2 501 Not Implemented ......................................70
+ 10.5.3 502 Bad Gateway ..........................................70
+ 10.5.4 503 Service Unavailable ..................................70
+ 10.5.5 504 Gateway Timeout ......................................71
+ 10.5.6 505 HTTP Version Not Supported ...........................71
+ 11 Access Authentication ........................................71
+ 12 Content Negotiation ..........................................71
+ 12.1 Server-driven Negotiation ...................................72
+ 12.2 Agent-driven Negotiation ....................................73
+ 12.3 Transparent Negotiation .....................................74
+ 13 Caching in HTTP ..............................................74
+ 13.1.1 Cache Correctness ........................................75
+ 13.1.2 Warnings .................................................76
+ 13.1.3 Cache-control Mechanisms .................................77
+ 13.1.4 Explicit User Agent Warnings .............................78
+ 13.1.5 Exceptions to the Rules and Warnings .....................78
+ 13.1.6 Client-controlled Behavior ...............................79
+ 13.2 Expiration Model ............................................79
+ 13.2.1 Server-Specified Expiration ..............................79
+ 13.2.2 Heuristic Expiration .....................................80
+ 13.2.3 Age Calculations .........................................80
+ 13.2.4 Expiration Calculations ..................................83
+ 13.2.5 Disambiguating Expiration Values .........................84
+ 13.2.6 Disambiguating Multiple Responses ........................84
+ 13.3 Validation Model ............................................85
+ 13.3.1 Last-Modified Dates ......................................86
+
+
+
+Fielding, et al. Standards Track [Page 4]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 13.3.2 Entity Tag Cache Validators ..............................86
+ 13.3.3 Weak and Strong Validators ...............................86
+ 13.3.4 Rules for When to Use Entity Tags and Last-Modified Dates.89
+ 13.3.5 Non-validating Conditionals ..............................90
+ 13.4 Response Cacheability .......................................91
+ 13.5 Constructing Responses From Caches ..........................92
+ 13.5.1 End-to-end and Hop-by-hop Headers ........................92
+ 13.5.2 Non-modifiable Headers ...................................92
+ 13.5.3 Combining Headers ........................................94
+ 13.5.4 Combining Byte Ranges ....................................95
+ 13.6 Caching Negotiated Responses ................................95
+ 13.7 Shared and Non-Shared Caches ................................96
+ 13.8 Errors or Incomplete Response Cache Behavior ................97
+ 13.9 Side Effects of GET and HEAD ................................97
+ 13.10 Invalidation After Updates or Deletions ...................97
+ 13.11 Write-Through Mandatory ...................................98
+ 13.12 Cache Replacement .........................................99
+ 13.13 History Lists .............................................99
+ 14 Header Field Definitions ....................................100
+ 14.1 Accept .....................................................100
+ 14.2 Accept-Charset .............................................102
+ 14.3 Accept-Encoding ............................................102
+ 14.4 Accept-Language ............................................104
+ 14.5 Accept-Ranges ..............................................105
+ 14.6 Age ........................................................106
+ 14.7 Allow ......................................................106
+ 14.8 Authorization ..............................................107
+ 14.9 Cache-Control ..............................................108
+ 14.9.1 What is Cacheable .......................................109
+ 14.9.2 What May be Stored by Caches ............................110
+ 14.9.3 Modifications of the Basic Expiration Mechanism .........111
+ 14.9.4 Cache Revalidation and Reload Controls ..................113
+ 14.9.5 No-Transform Directive ..................................115
+ 14.9.6 Cache Control Extensions ................................116
+ 14.10 Connection ...............................................117
+ 14.11 Content-Encoding .........................................118
+ 14.12 Content-Language .........................................118
+ 14.13 Content-Length ...........................................119
+ 14.14 Content-Location .........................................120
+ 14.15 Content-MD5 ..............................................121
+ 14.16 Content-Range ............................................122
+ 14.17 Content-Type .............................................124
+ 14.18 Date .....................................................124
+ 14.18.1 Clockless Origin Server Operation ......................125
+ 14.19 ETag .....................................................126
+ 14.20 Expect ...................................................126
+ 14.21 Expires ..................................................127
+ 14.22 From .....................................................128
+
+
+
+Fielding, et al. Standards Track [Page 5]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 14.23 Host .....................................................128
+ 14.24 If-Match .................................................129
+ 14.25 If-Modified-Since ........................................130
+ 14.26 If-None-Match ............................................132
+ 14.27 If-Range .................................................133
+ 14.28 If-Unmodified-Since ......................................134
+ 14.29 Last-Modified ............................................134
+ 14.30 Location .................................................135
+ 14.31 Max-Forwards .............................................136
+ 14.32 Pragma ...................................................136
+ 14.33 Proxy-Authenticate .......................................137
+ 14.34 Proxy-Authorization ......................................137
+ 14.35 Range ....................................................138
+ 14.35.1 Byte Ranges ...........................................138
+ 14.35.2 Range Retrieval Requests ..............................139
+ 14.36 Referer ..................................................140
+ 14.37 Retry-After ..............................................141
+ 14.38 Server ...................................................141
+ 14.39 TE .......................................................142
+ 14.40 Trailer ..................................................143
+ 14.41 Transfer-Encoding..........................................143
+ 14.42 Upgrade ..................................................144
+ 14.43 User-Agent ...............................................145
+ 14.44 Vary .....................................................145
+ 14.45 Via ......................................................146
+ 14.46 Warning ..................................................148
+ 14.47 WWW-Authenticate .........................................150
+ 15 Security Considerations .......................................150
+ 15.1 Personal Information....................................151
+ 15.1.1 Abuse of Server Log Information .........................151
+ 15.1.2 Transfer of Sensitive Information .......................151
+ 15.1.3 Encoding Sensitive Information in URI's .................152
+ 15.1.4 Privacy Issues Connected to Accept Headers ..............152
+ 15.2 Attacks Based On File and Path Names .......................153
+ 15.3 DNS Spoofing ...............................................154
+ 15.4 Location Headers and Spoofing ..............................154
+ 15.5 Content-Disposition Issues .................................154
+ 15.6 Authentication Credentials and Idle Clients ................155
+ 15.7 Proxies and Caching ........................................155
+ 15.7.1 Denial of Service Attacks on Proxies....................156
+ 16 Acknowledgments .............................................156
+ 17 References ..................................................158
+ 18 Authors' Addresses ..........................................162
+ 19 Appendices ..................................................164
+ 19.1 Internet Media Type message/http and application/http ......164
+ 19.2 Internet Media Type multipart/byteranges ...................165
+ 19.3 Tolerant Applications ......................................166
+ 19.4 Differences Between HTTP Entities and RFC 2045 Entities ....167
+
+
+
+Fielding, et al. Standards Track [Page 6]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 19.4.1 MIME-Version ............................................167
+ 19.4.2 Conversion to Canonical Form ............................167
+ 19.4.3 Conversion of Date Formats ..............................168
+ 19.4.4 Introduction of Content-Encoding ........................168
+ 19.4.5 No Content-Transfer-Encoding ............................168
+ 19.4.6 Introduction of Transfer-Encoding .......................169
+ 19.4.7 MHTML and Line Length Limitations .......................169
+ 19.5 Additional Features ........................................169
+ 19.5.1 Content-Disposition .....................................170
+ 19.6 Compatibility with Previous Versions .......................170
+ 19.6.1 Changes from HTTP/1.0 ...................................171
+ 19.6.2 Compatibility with HTTP/1.0 Persistent Connections ......172
+ 19.6.3 Changes from RFC 2068 ...................................172
+ 20 Index .......................................................175
+ 21 Full Copyright Statement ....................................176
+
+1 Introduction
+
+1.1 Purpose
+
+ The Hypertext Transfer Protocol (HTTP) is an application-level
+ protocol for distributed, collaborative, hypermedia information
+ systems. HTTP has been in use by the World-Wide Web global
+ information initiative since 1990. The first version of HTTP,
+ referred to as HTTP/0.9, was a simple protocol for raw data transfer
+ across the Internet. HTTP/1.0, as defined by RFC 1945 [6], improved
+ the protocol by allowing messages to be in the format of MIME-like
+ messages, containing metainformation about the data transferred and
+ modifiers on the request/response semantics. However, HTTP/1.0 does
+ not sufficiently take into consideration the effects of hierarchical
+ proxies, caching, the need for persistent connections, or virtual
+ hosts. In addition, the proliferation of incompletely-implemented
+ applications calling themselves "HTTP/1.0" has necessitated a
+ protocol version change in order for two communicating applications
+ to determine each other's true capabilities.
+
+ This specification defines the protocol referred to as "HTTP/1.1".
+ This protocol includes more stringent requirements than HTTP/1.0 in
+ order to ensure reliable implementation of its features.
+
+ Practical information systems require more functionality than simple
+ retrieval, including search, front-end update, and annotation. HTTP
+ allows an open-ended set of methods and headers that indicate the
+ purpose of a request [47]. It builds on the discipline of reference
+ provided by the Uniform Resource Identifier (URI) [3], as a location
+ (URL) [4] or name (URN) [20], for indicating the resource to which a
+
+
+
+
+
+Fielding, et al. Standards Track [Page 7]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ method is to be applied. Messages are passed in a format similar to
+ that used by Internet mail [9] as defined by the Multipurpose
+ Internet Mail Extensions (MIME) [7].
+
+ HTTP is also used as a generic protocol for communication between
+ user agents and proxies/gateways to other Internet systems, including
+ those supported by the SMTP [16], NNTP [13], FTP [18], Gopher [2],
+ and WAIS [10] protocols. In this way, HTTP allows basic hypermedia
+ access to resources available from diverse applications.
+
+1.2 Requirements
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [34].
+
+ An implementation is not compliant if it fails to satisfy one or more
+ of the MUST or REQUIRED level requirements for the protocols it
+ implements. An implementation that satisfies all the MUST or REQUIRED
+ level and all the SHOULD level requirements for its protocols is said
+ to be "unconditionally compliant"; one that satisfies all the MUST
+ level requirements but not all the SHOULD level requirements for its
+ protocols is said to be "conditionally compliant."
+
+1.3 Terminology
+
+ This specification uses a number of terms to refer to the roles
+ played by participants in, and objects of, the HTTP communication.
+
+ connection
+ A transport layer virtual circuit established between two programs
+ for the purpose of communication.
+
+ message
+ The basic unit of HTTP communication, consisting of a structured
+ sequence of octets matching the syntax defined in section 4 and
+ transmitted via the connection.
+
+ request
+ An HTTP request message, as defined in section 5.
+
+ response
+ An HTTP response message, as defined in section 6.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 8]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ resource
+ A network data object or service that can be identified by a URI,
+ as defined in section 3.2. Resources may be available in multiple
+ representations (e.g. multiple languages, data formats, size, and
+ resolutions) or vary in other ways.
+
+ entity
+ The information transferred as the payload of a request or
+ response. An entity consists of metainformation in the form of
+ entity-header fields and content in the form of an entity-body, as
+ described in section 7.
+
+ representation
+ An entity included with a response that is subject to content
+ negotiation, as described in section 12. There may exist multiple
+ representations associated with a particular response status.
+
+ content negotiation
+ The mechanism for selecting the appropriate representation when
+ servicing a request, as described in section 12. The
+ representation of entities in any response can be negotiated
+ (including error responses).
+
+ variant
+ A resource may have one, or more than one, representation(s)
+ associated with it at any given instant. Each of these
+ representations is termed a `varriant'. Use of the term `variant'
+ does not necessarily imply that the resource is subject to content
+ negotiation.
+
+ client
+ A program that establishes connections for the purpose of sending
+ requests.
+
+ user agent
+ The client which initiates a request. These are often browsers,
+ editors, spiders (web-traversing robots), or other end user tools.
+
+ server
+ An application program that accepts connections in order to
+ service requests by sending back responses. Any given program may
+ be capable of being both a client and a server; our use of these
+ terms refers only to the role being performed by the program for a
+ particular connection, rather than to the program's capabilities
+ in general. Likewise, any server may act as an origin server,
+ proxy, gateway, or tunnel, switching behavior based on the nature
+ of each request.
+
+
+
+
+Fielding, et al. Standards Track [Page 9]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ origin server
+ The server on which a given resource resides or is to be created.
+
+ proxy
+ An intermediary program which acts as both a server and a client
+ for the purpose of making requests on behalf of other clients.
+ Requests are serviced internally or by passing them on, with
+ possible translation, to other servers. A proxy MUST implement
+ both the client and server requirements of this specification. A
+ "transparent proxy" is a proxy that does not modify the request or
+ response beyond what is required for proxy authentication and
+ identification. A "non-transparent proxy" is a proxy that modifies
+ the request or response in order to provide some added service to
+ the user agent, such as group annotation services, media type
+ transformation, protocol reduction, or anonymity filtering. Except
+ where either transparent or non-transparent behavior is explicitly
+ stated, the HTTP proxy requirements apply to both types of
+ proxies.
+
+ gateway
+ A server which acts as an intermediary for some other server.
+ Unlike a proxy, a gateway receives requests as if it were the
+ origin server for the requested resource; the requesting client
+ may not be aware that it is communicating with a gateway.
+
+ tunnel
+ An intermediary program which is acting as a blind relay between
+ two connections. Once active, a tunnel is not considered a party
+ to the HTTP communication, though the tunnel may have been
+ initiated by an HTTP request. The tunnel ceases to exist when both
+ ends of the relayed connections are closed.
+
+ cache
+ A program's local store of response messages and the subsystem
+ that controls its message storage, retrieval, and deletion. A
+ cache stores cacheable responses in order to reduce the response
+ time and network bandwidth consumption on future, equivalent
+ requests. Any client or server may include a cache, though a cache
+ cannot be used by a server that is acting as a tunnel.
+
+ cacheable
+ A response is cacheable if a cache is allowed to store a copy of
+ the response message for use in answering subsequent requests. The
+ rules for determining the cacheability of HTTP responses are
+ defined in section 13. Even if a resource is cacheable, there may
+ be additional constraints on whether a cache can use the cached
+ copy for a particular request.
+
+
+
+
+Fielding, et al. Standards Track [Page 10]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ first-hand
+ A response is first-hand if it comes directly and without
+ unnecessary delay from the origin server, perhaps via one or more
+ proxies. A response is also first-hand if its validity has just
+ been checked directly with the origin server.
+
+ explicit expiration time
+ The time at which the origin server intends that an entity should
+ no longer be returned by a cache without further validation.
+
+ heuristic expiration time
+ An expiration time assigned by a cache when no explicit expiration
+ time is available.
+
+ age
+ The age of a response is the time since it was sent by, or
+ successfully validated with, the origin server.
+
+ freshness lifetime
+ The length of time between the generation of a response and its
+ expiration time.
+
+ fresh
+ A response is fresh if its age has not yet exceeded its freshness
+ lifetime.
+
+ stale
+ A response is stale if its age has passed its freshness lifetime.
+
+ semantically transparent
+ A cache behaves in a "semantically transparent" manner, with
+ respect to a particular response, when its use affects neither the
+ requesting client nor the origin server, except to improve
+ performance. When a cache is semantically transparent, the client
+ receives exactly the same response (except for hop-by-hop headers)
+ that it would have received had its request been handled directly
+ by the origin server.
+
+ validator
+ A protocol element (e.g., an entity tag or a Last-Modified time)
+ that is used to find out whether a cache entry is an equivalent
+ copy of an entity.
+
+ upstream/downstream
+ Upstream and downstream describe the flow of a message: all
+ messages flow from upstream to downstream.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 11]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ inbound/outbound
+ Inbound and outbound refer to the request and response paths for
+ messages: "inbound" means "traveling toward the origin server",
+ and "outbound" means "traveling toward the user agent"
+
+1.4 Overall Operation
+
+ The HTTP protocol is a request/response protocol. A client sends a
+ request to the server in the form of a request method, URI, and
+ protocol version, followed by a MIME-like message containing request
+ modifiers, client information, and possible body content over a
+ connection with a server. The server responds with a status line,
+ including the message's protocol version and a success or error code,
+ followed by a MIME-like message containing server information, entity
+ metainformation, and possible entity-body content. The relationship
+ between HTTP and MIME is described in appendix 19.4.
+
+ Most HTTP communication is initiated by a user agent and consists of
+ a request to be applied to a resource on some origin server. In the
+ simplest case, this may be accomplished via a single connection (v)
+ between the user agent (UA) and the origin server (O).
+
+ request chain ------------------------>
+ UA -------------------v------------------- O
+ <----------------------- response chain
+
+ A more complicated situation occurs when one or more intermediaries
+ are present in the request/response chain. There are three common
+ forms of intermediary: proxy, gateway, and tunnel. A proxy is a
+ forwarding agent, receiving requests for a URI in its absolute form,
+ rewriting all or part of the message, and forwarding the reformatted
+ request toward the server identified by the URI. A gateway is a
+ receiving agent, acting as a layer above some other server(s) and, if
+ necessary, translating the requests to the underlying server's
+ protocol. A tunnel acts as a relay point between two connections
+ without changing the messages; tunnels are used when the
+ communication needs to pass through an intermediary (such as a
+ firewall) even when the intermediary cannot understand the contents
+ of the messages.
+
+ request chain -------------------------------------->
+ UA -----v----- A -----v----- B -----v----- C -----v----- O
+ <------------------------------------- response chain
+
+ The figure above shows three intermediaries (A, B, and C) between the
+ user agent and origin server. A request or response message that
+ travels the whole chain will pass through four separate connections.
+ This distinction is important because some HTTP communication options
+
+
+
+Fielding, et al. Standards Track [Page 12]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ may apply only to the connection with the nearest, non-tunnel
+ neighbor, only to the end-points of the chain, or to all connections
+ along the chain. Although the diagram is linear, each participant may
+ be engaged in multiple, simultaneous communications. For example, B
+ may be receiving requests from many clients other than A, and/or
+ forwarding requests to servers other than C, at the same time that it
+ is handling A's request.
+
+ Any party to the communication which is not acting as a tunnel may
+ employ an internal cache for handling requests. The effect of a cache
+ is that the request/response chain is shortened if one of the
+ participants along the chain has a cached response applicable to that
+ request. The following illustrates the resulting chain if B has a
+ cached copy of an earlier response from O (via C) for a request which
+ has not been cached by UA or A.
+
+ request chain ---------->
+ UA -----v----- A -----v----- B - - - - - - C - - - - - - O
+ <--------- response chain
+
+ Not all responses are usefully cacheable, and some requests may
+ contain modifiers which place special requirements on cache behavior.
+ HTTP requirements for cache behavior and cacheable responses are
+ defined in section 13.
+
+ In fact, there are a wide variety of architectures and configurations
+ of caches and proxies currently being experimented with or deployed
+ across the World Wide Web. These systems include national hierarchies
+ of proxy caches to save transoceanic bandwidth, systems that
+ broadcast or multicast cache entries, organizations that distribute
+ subsets of cached data via CD-ROM, and so on. HTTP systems are used
+ in corporate intranets over high-bandwidth links, and for access via
+ PDAs with low-power radio links and intermittent connectivity. The
+ goal of HTTP/1.1 is to support the wide diversity of configurations
+ already deployed while introducing protocol constructs that meet the
+ needs of those who build web applications that require high
+ reliability and, failing that, at least reliable indications of
+ failure.
+
+ HTTP communication usually takes place over TCP/IP connections. The
+ default port is TCP 80 [19], but other ports can be used. This does
+ not preclude HTTP from being implemented on top of any other protocol
+ on the Internet, or on other networks. HTTP only presumes a reliable
+ transport; any protocol that provides such guarantees can be used;
+ the mapping of the HTTP/1.1 request and response structures onto the
+ transport data units of the protocol in question is outside the scope
+ of this specification.
+
+
+
+
+Fielding, et al. Standards Track [Page 13]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ In HTTP/1.0, most implementations used a new connection for each
+ request/response exchange. In HTTP/1.1, a connection may be used for
+ one or more request/response exchanges, although connections may be
+ closed for a variety of reasons (see section 8.1).
+
+2 Notational Conventions and Generic Grammar
+
+2.1 Augmented BNF
+
+ All of the mechanisms specified in this document are described in
+ both prose and an augmented Backus-Naur Form (BNF) similar to that
+ used by RFC 822 [9]. Implementors will need to be familiar with the
+ notation in order to understand this specification. The augmented BNF
+ includes the following constructs:
+
+ name = definition
+ The name of a rule is simply the name itself (without any
+ enclosing "<" and ">") and is separated from its definition by the
+ equal "=" character. White space is only significant in that
+ indentation of continuation lines is used to indicate a rule
+ definition that spans more than one line. Certain basic rules are
+ in uppercase, such as SP, LWS, HT, CRLF, DIGIT, ALPHA, etc. Angle
+ brackets are used within definitions whenever their presence will
+ facilitate discerning the use of rule names.
+
+ "literal"
+ Quotation marks surround literal text. Unless stated otherwise,
+ the text is case-insensitive.
+
+ rule1 | rule2
+ Elements separated by a bar ("|") are alternatives, e.g., "yes |
+ no" will accept yes or no.
+
+ (rule1 rule2)
+ Elements enclosed in parentheses are treated as a single element.
+ Thus, "(elem (foo | bar) elem)" allows the token sequences "elem
+ foo elem" and "elem bar elem".
+
+ *rule
+ The character "*" preceding an element indicates repetition. The
+ full form is "<n>*<m>element" indicating at least <n> and at most
+ <m> occurrences of element. Default values are 0 and infinity so
+ that "*(element)" allows any number, including zero; "1*element"
+ requires at least one; and "1*2element" allows one or two.
+
+ [rule]
+ Square brackets enclose optional elements; "[foo bar]" is
+ equivalent to "*1(foo bar)".
+
+
+
+Fielding, et al. Standards Track [Page 14]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ N rule
+ Specific repetition: "<n>(element)" is equivalent to
+ "<n>*<n>(element)"; that is, exactly <n> occurrences of (element).
+ Thus 2DIGIT is a 2-digit number, and 3ALPHA is a string of three
+ alphabetic characters.
+
+ #rule
+ A construct "#" is defined, similar to "*", for defining lists of
+ elements. The full form is "<n>#<m>element" indicating at least
+ <n> and at most <m> elements, each separated by one or more commas
+ (",") and OPTIONAL linear white space (LWS). This makes the usual
+ form of lists very easy; a rule such as
+ ( *LWS element *( *LWS "," *LWS element ))
+ can be shown as
+ 1#element
+ Wherever this construct is used, null elements are allowed, but do
+ not contribute to the count of elements present. That is,
+ "(element), , (element) " is permitted, but counts as only two
+ elements. Therefore, where at least one element is required, at
+ least one non-null element MUST be present. Default values are 0
+ and infinity so that "#element" allows any number, including zero;
+ "1#element" requires at least one; and "1#2element" allows one or
+ two.
+
+ ; comment
+ A semi-colon, set off some distance to the right of rule text,
+ starts a comment that continues to the end of line. This is a
+ simple way of including useful notes in parallel with the
+ specifications.
+
+ implied *LWS
+ The grammar described by this specification is word-based. Except
+ where noted otherwise, linear white space (LWS) can be included
+ between any two adjacent words (token or quoted-string), and
+ between adjacent words and separators, without changing the
+ interpretation of a field. At least one delimiter (LWS and/or
+
+ separators) MUST exist between any two tokens (for the definition
+ of "token" below), since they would otherwise be interpreted as a
+ single token.
+
+2.2 Basic Rules
+
+ The following rules are used throughout this specification to
+ describe basic parsing constructs. The US-ASCII coded character set
+ is defined by ANSI X3.4-1986 [21].
+
+
+
+
+
+Fielding, et al. Standards Track [Page 15]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ OCTET = <any 8-bit sequence of data>
+ CHAR = <any US-ASCII character (octets 0 - 127)>
+ UPALPHA = <any US-ASCII uppercase letter "A".."Z">
+ LOALPHA = <any US-ASCII lowercase letter "a".."z">
+ ALPHA = UPALPHA | LOALPHA
+ DIGIT = <any US-ASCII digit "0".."9">
+ CTL = <any US-ASCII control character
+ (octets 0 - 31) and DEL (127)>
+ CR = <US-ASCII CR, carriage return (13)>
+ LF = <US-ASCII LF, linefeed (10)>
+ SP = <US-ASCII SP, space (32)>
+ HT = <US-ASCII HT, horizontal-tab (9)>
+ <"> = <US-ASCII double-quote mark (34)>
+
+ HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all
+ protocol elements except the entity-body (see appendix 19.3 for
+ tolerant applications). The end-of-line marker within an entity-body
+ is defined by its associated media type, as described in section 3.7.
+
+ CRLF = CR LF
+
+ HTTP/1.1 header field values can be folded onto multiple lines if the
+ continuation line begins with a space or horizontal tab. All linear
+ white space, including folding, has the same semantics as SP. A
+ recipient MAY replace any linear white space with a single SP before
+ interpreting the field value or forwarding the message downstream.
+
+ LWS = [CRLF] 1*( SP | HT )
+
+ The TEXT rule is only used for descriptive field contents and values
+ that are not intended to be interpreted by the message parser. Words
+ of *TEXT MAY contain characters from character sets other than ISO-
+ 8859-1 [22] only when encoded according to the rules of RFC 2047
+ [14].
+
+ TEXT = <any OCTET except CTLs,
+ but including LWS>
+
+ A CRLF is allowed in the definition of TEXT only as part of a header
+ field continuation. It is expected that the folding LWS will be
+ replaced with a single SP before interpretation of the TEXT value.
+
+ Hexadecimal numeric characters are used in several protocol elements.
+
+ HEX = "A" | "B" | "C" | "D" | "E" | "F"
+ | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
+
+
+
+
+
+Fielding, et al. Standards Track [Page 16]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Many HTTP/1.1 header field values consist of words separated by LWS
+ or special characters. These special characters MUST be in a quoted
+ string to be used within a parameter value (as defined in section
+ 3.6).
+
+ token = 1*<any CHAR except CTLs or separators>
+ separators = "(" | ")" | "<" | ">" | "@"
+ | "," | ";" | ":" | "\" | <">
+ | "/" | "[" | "]" | "?" | "="
+ | "{" | "}" | SP | HT
+
+ Comments can be included in some HTTP header fields by surrounding
+ the comment text with parentheses. Comments are only allowed in
+ fields containing "comment" as part of their field value definition.
+ In all other fields, parentheses are considered part of the field
+ value.
+
+ comment = "(" *( ctext | quoted-pair | comment ) ")"
+ ctext = <any TEXT excluding "(" and ")">
+
+ A string of text is parsed as a single word if it is quoted using
+ double-quote marks.
+
+ quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ qdtext = <any TEXT except <">>
+
+ The backslash character ("\") MAY be used as a single-character
+ quoting mechanism only within quoted-string and comment constructs.
+
+ quoted-pair = "\" CHAR
+
+3 Protocol Parameters
+
+3.1 HTTP Version
+
+ HTTP uses a "<major>.<minor>" numbering scheme to indicate versions
+ of the protocol. The protocol versioning policy is intended to allow
+ the sender to indicate the format of a message and its capacity for
+ understanding further HTTP communication, rather than the features
+ obtained via that communication. No change is made to the version
+ number for the addition of message components which do not affect
+ communication behavior or which only add to extensible field values.
+ The <minor> number is incremented when the changes made to the
+ protocol add features which do not change the general message parsing
+ algorithm, but which may add to the message semantics and imply
+ additional capabilities of the sender. The <major> number is
+ incremented when the format of a message within the protocol is
+ changed. See RFC 2145 [36] for a fuller explanation.
+
+
+
+Fielding, et al. Standards Track [Page 17]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The version of an HTTP message is indicated by an HTTP-Version field
+ in the first line of the message. [[HTTP-Version is case-sensitive.]]
+
+ HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+
+ Note that the major and minor numbers MUST be treated as separate
+ integers and that each MAY be incremented higher than a single digit.
+ Thus, HTTP/2.4 is a lower version than HTTP/2.13, which in turn is
+ lower than HTTP/12.3. Leading zeros MUST be ignored by recipients and
+ MUST NOT be sent.
+
+ An application that sends a request or response message that includes
+ HTTP-Version of "HTTP/1.1" MUST be at least conditionally compliant
+ with this specification. Applications that are at least conditionally
+ compliant with this specification SHOULD use an HTTP-Version of
+ "HTTP/1.1" in their messages, and MUST do so for any message that is
+ not compatible with HTTP/1.0. For more details on when to send
+ specific HTTP-Version values, see RFC 2145 [36].
+
+ The HTTP version of an application is the highest HTTP version for
+ which the application is at least conditionally compliant.
+
+ Proxy and gateway applications need to be careful when forwarding
+ messages in protocol versions different from that of the application.
+ Since the protocol version indicates the protocol capability of the
+ sender, a proxy/gateway MUST NOT send a message with a version
+ indicator which is greater than its actual version. If a higher
+ version request is received, the proxy/gateway MUST either downgrade
+ the request version, or respond with an error, or switch to tunnel
+ behavior.
+
+ Due to interoperability problems with HTTP/1.0 proxies discovered
+ since the publication of RFC 2068[33], caching proxies MUST, gateways
+ MAY, and tunnels MUST NOT upgrade the request to the highest version
+ they support. The proxy/gateway's response to that request MUST be in
+ the same major version as the request.
+
+ Note: Converting between versions of HTTP may involve modification
+ of header fields required or forbidden by the versions involved.
+
+3.2 Uniform Resource Identifiers
+
+ URIs have been known by many names: WWW addresses, Universal Document
+ Identifiers, Universal Resource Identifiers [3], and finally the
+ combination of Uniform Resource Locators (URL) [4] and Names (URN)
+ [20]. As far as HTTP is concerned, Uniform Resource Identifiers are
+ simply formatted strings which identify--via name, location, or any
+ other characteristic--a resource.
+
+
+
+Fielding, et al. Standards Track [Page 18]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+3.2.1 General Syntax
+
+ URIs in HTTP can be represented in absolute form or relative to some
+ known base URI [11], depending upon the context of their use. The two
+ forms are differentiated by the fact that absolute URIs always begin
+ with a scheme name followed by a colon. For definitive information on
+ URL syntax and semantics, see "Uniform Resource Identifiers (URI):
+ Generic Syntax and Semantics," RFC 2396 [42] (which replaces RFCs
+ 1738 [4] and RFC 1808 [11]). This specification adopts the
+ definitions of "URI-reference", "absoluteURI", "relativeURI", "port",
+ "host","abs_path", "rel_path", and "authority" from that
+ specification.
+
+ The HTTP protocol does not place any a priori limit on the length of
+ a URI. Servers MUST be able to handle the URI of any resource they
+ serve, and SHOULD be able to handle URIs of unbounded length if they
+ provide GET-based forms that could generate such URIs. A server
+ SHOULD return 414 (Request-URI Too Long) status if a URI is longer
+ than the server can handle (see section 10.4.15).
+
+ Note: Servers ought to be cautious about depending on URI lengths
+ above 255 bytes, because some older client or proxy
+ implementations might not properly support these lengths.
+
+3.2.2 http URL
+
+ The "http" scheme is used to locate network resources via the HTTP
+ protocol. This section defines the scheme-specific syntax and
+ semantics for http URLs.
+
+ http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
+
+ If the port is empty or not given, port 80 is assumed. The semantics
+ are that the identified resource is located at the server listening
+ for TCP connections on that port of that host, and the Request-URI
+ for the resource is abs_path (section 5.1.2). The use of IP addresses
+ in URLs SHOULD be avoided whenever possible (see RFC 1900 [24]). If
+ the abs_path is not present in the URL, it MUST be given as "/" when
+ used as a Request-URI for a resource (section 5.1.2). If a proxy
+ receives a host name which is not a fully qualified domain name, it
+ MAY add its domain to the host name it received. If a proxy receives
+ a fully qualified domain name, the proxy MUST NOT change the host
+ name.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 19]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+3.2.3 URI Comparison
+
+ When comparing two URIs to decide if they match or not, a client
+ SHOULD use a case-sensitive octet-by-octet comparison of the entire
+ URIs, with these exceptions:
+
+ - A port that is empty or not given is equivalent to the default
+ port for that URI-reference;
+
+ - Comparisons of host names MUST be case-insensitive;
+
+ - Comparisons of scheme names MUST be case-insensitive;
+
+ - An empty abs_path is equivalent to an abs_path of "/".
+
+ Characters other than those in the "reserved" and "unsafe" sets (see
+ RFC 2396 [42]) are equivalent to their ""%" HEX HEX" encoding.
+ [[ Ignore reference to "unsafe" set. ]]
+
+ For example, the following three URIs are equivalent:
+
+ http://abc.com:80/~smith/home.html
+ http://ABC.com/%7Esmith/home.html
+ http://ABC.com:/%7esmith/home.html
+
+3.3 Date/Time Formats
+
+3.3.1 Full Date
+
+ HTTP applications have historically allowed three different formats
+ for the representation of date/time stamps:
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+ The first format is preferred as an Internet standard and represents
+ a fixed-length subset of that defined by RFC 1123 [8] (an update to
+ RFC 822 [9]). The second format is in common use, but is based on the
+ obsolete RFC 850 [12] date format and lacks a four-digit year.
+ HTTP/1.1 clients and servers that parse the date value MUST accept
+ all three formats (for compatibility with HTTP/1.0), though they MUST
+ only generate the RFC 1123 format for representing HTTP-date values
+ in header fields. See section 19.3 for further information.
+
+ Note: Recipients of date values are encouraged to be robust in
+ accepting date values that may have been sent by non-HTTP
+ applications, as is sometimes the case when retrieving or posting
+ messages via proxies/gateways to SMTP or NNTP.
+
+
+
+Fielding, et al. Standards Track [Page 20]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ All HTTP date/time stamps MUST be represented in Greenwich Mean Time
+ (GMT), without exception. For the purposes of HTTP, GMT is exactly
+ equal to UTC (Coordinated Universal Time). This is indicated in the
+ first two formats by the inclusion of "GMT" as the three-letter
+ abbreviation for time zone, and MUST be assumed when reading the
+ asctime format. HTTP-date is case sensitive and MUST NOT include
+ additional LWS beyond that specifically included as SP in the
+ grammar.
+
+ HTTP-date = rfc1123-date | rfc850-date | asctime-date
+ rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+ rfc850-date = weekday "," SP date2 SP time SP "GMT"
+ asctime-date = wkday SP date3 SP time SP 4DIGIT
+ date1 = 2DIGIT SP month SP 4DIGIT
+ ; day month year (e.g., 02 Jun 1982)
+ date2 = 2DIGIT "-" month "-" 2DIGIT
+ ; day-month-year (e.g., 02-Jun-82)
+ date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
+ ; month day (e.g., Jun 2)
+ time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ ; 00:00:00 - 23:59:59
+ wkday = "Mon" | "Tue" | "Wed"
+ | "Thu" | "Fri" | "Sat" | "Sun"
+ weekday = "Monday" | "Tuesday" | "Wednesday"
+ | "Thursday" | "Friday" | "Saturday" | "Sunday"
+ month = "Jan" | "Feb" | "Mar" | "Apr"
+ | "May" | "Jun" | "Jul" | "Aug"
+ | "Sep" | "Oct" | "Nov" | "Dec"
+
+ Note: HTTP requirements for the date/time stamp format apply only
+ to their usage within the protocol stream. Clients and servers are
+ not required to use these formats for user presentation, request
+ logging, etc.
+
+3.3.2 Delta Seconds
+
+ Some HTTP header fields allow a time value to be specified as an
+ integer number of seconds, represented in decimal, after the time
+ that the message was received.
+
+ delta-seconds = 1*DIGIT
+
+3.4 Character Sets
+
+ HTTP uses the same definition of the term "character set" as that
+ described for MIME:
+
+
+
+
+
+Fielding, et al. Standards Track [Page 21]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The term "character set" is used in this document to refer to a
+ method used with one or more tables to convert a sequence of octets
+ into a sequence of characters. Note that unconditional conversion in
+ the other direction is not required, in that not all characters may
+ be available in a given character set and a character set may provide
+ more than one sequence of octets to represent a particular character.
+ This definition is intended to allow various kinds of character
+ encoding, from simple single-table mappings such as US-ASCII to
+ complex table switching methods such as those that use ISO-2022's
+ techniques. However, the definition associated with a MIME character
+ set name MUST fully specify the mapping to be performed from octets
+ to characters. In particular, use of external profiling information
+ to determine the exact mapping is not permitted.
+
+ Note: This use of the term "character set" is more commonly
+ referred to as a "character encoding." However, since HTTP and
+ MIME share the same registry, it is important that the terminology
+ also be shared.
+
+ HTTP character sets are identified by case-insensitive tokens. The
+ complete set of tokens is defined by the IANA Character Set registry
+ [19].
+
+ charset = token
+
+[[ HTTP uses charset in two contexts: within an Accept-Charset request ]]
+[[ header (in which the charset value is an unquoted token) and as the ]]
+[[ value of a parameter in a Content-type header (within a request or ]]
+[[ response), in which case the parameter value of the charset ]]
+[[ parameter may be quoted. ]]
+
+ Although HTTP allows an arbitrary token to be used as a charset
+ value, any token that has a predefined value within the IANA
+ Character Set registry [19] MUST represent the character set defined
+ by that registry. Applications SHOULD limit their use of character
+ sets to those defined by the IANA registry.
+
+ Implementors should be aware of IETF character set requirements [38]
+ [41].
+
+3.4.1 Missing Charset
+
+ Some HTTP/1.0 software has interpreted a Content-Type header without
+ charset parameter incorrectly to mean "recipient should guess."
+ Senders wishing to defeat this behavior MAY include a charset
+ parameter even when the charset is ISO-8859-1 and SHOULD do so when
+ it is known that it will not confuse the recipient.
+
+ Unfortunately, some older HTTP/1.0 clients did not deal properly with
+ an explicit charset parameter. HTTP/1.1 recipients MUST respect the
+ charset label provided by the sender; and those user agents that have
+ a provision to "guess" a charset MUST use the charset from the
+
+
+
+
+
+Fielding, et al. Standards Track [Page 22]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ content-type field if they support that charset, rather than the
+ recipient's preference, when initially displaying a document. See
+ section 3.7.1.
+
+3.5 Content Codings
+
+ Content coding values indicate an encoding transformation that has
+ been or can be applied to an entity. Content codings are primarily
+ used to allow a document to be compressed or otherwise usefully
+ transformed without losing the identity of its underlying media type
+ and without loss of information. Frequently, the entity is stored in
+ coded form, transmitted directly, and only decoded by the recipient.
+
+ content-coding = token
+
+ All content-coding values are case-insensitive. HTTP/1.1 uses
+ content-coding values in the Accept-Encoding (section 14.3) and
+ Content-Encoding (section 14.11) header fields. Although the value
+ describes the content-coding, what is more important is that it
+ indicates what decoding mechanism will be required to remove the
+ encoding.
+
+ The Internet Assigned Numbers Authority (IANA) acts as a registry for
+ content-coding value tokens. Initially, the registry contains the
+ following tokens:
+
+ gzip An encoding format produced by the file compression program
+ "gzip" (GNU zip) as described in RFC 1952 [25]. This format is a
+ Lempel-Ziv coding (LZ77) with a 32 bit CRC.
+
+ compress
+ The encoding format produced by the common UNIX file compression
+ program "compress". This format is an adaptive Lempel-Ziv-Welch
+ coding (LZW).
+
+ Use of program names for the identification of encoding formats
+ is not desirable and is discouraged for future encodings. Their
+ use here is representative of historical practice, not good
+ design. For compatibility with previous implementations of HTTP,
+ applications SHOULD consider "x-gzip" and "x-compress" to be
+ equivalent to "gzip" and "compress" respectively.
+
+ deflate
+ The "zlib" format defined in RFC 1950 [31] in combination with
+ the "deflate" compression mechanism described in RFC 1951 [29].
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 23]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ identity
+ The default (identity) encoding; the use of no transformation
+ whatsoever. This content-coding is used only in the Accept-
+ Encoding header, and SHOULD NOT be used in the Content-Encoding
+ header.
+
+ New content-coding value tokens SHOULD be registered; to allow
+ interoperability between clients and servers, specifications of the
+ content coding algorithms needed to implement a new value SHOULD be
+ publicly available and adequate for independent implementation, and
+ conform to the purpose of content coding defined in this section.
+
+3.6 Transfer Codings
+
+ Transfer-coding values are used to indicate an encoding
+ transformation that has been, can be, or may need to be applied to an
+ entity-body in order to ensure "safe transport" through the network.
+ This differs from a content coding in that the transfer-coding is a
+ property of the message, not of the original entity.
+
+ transfer-coding = "chunked" | transfer-extension
+ transfer-extension = token *( ";" parameter )
+
+ Parameters are in the form of attribute/value pairs.
+
+ parameter = attribute "=" value
+ attribute = token
+ value = token | quoted-string
+
+ All transfer-coding values are case-insensitive. HTTP/1.1 uses
+ transfer-coding values in the TE header field (section 14.39) and in
+ the Transfer-Encoding header field (section 14.41).
+
+ Whenever a transfer-coding is applied to a message-body, the set of
+ transfer-codings MUST include "chunked", unless the message is
+ terminated by closing the connection. When the "chunked" transfer-
+ coding is used, it MUST be the last transfer-coding applied to the
+ message-body. The "chunked" transfer-coding MUST NOT be applied more
+ than once to a message-body. These rules allow the recipient to
+ determine the transfer-length of the message (section 4.4).
+
+ Transfer-codings are analogous to the Content-Transfer-Encoding
+ values of MIME [7], which were designed to enable safe transport of
+ binary data over a 7-bit transport service. However, safe transport
+ has a different focus for an 8bit-clean transfer protocol. In HTTP,
+ the only unsafe characteristic of message-bodies is the difficulty in
+ determining the exact body length (section 7.2.2), or the desire to
+ encrypt data over a shared transport.
+
+
+
+Fielding, et al. Standards Track [Page 24]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The Internet Assigned Numbers Authority (IANA) acts as a registry for
+ transfer-coding value tokens. Initially, the registry contains the
+ following tokens: "chunked" (section 3.6.1), "identity" (section
+ 3.6.2), "gzip" (section 3.5), "compress" (section 3.5), and "deflate"
+ (section 3.5).
+
+ [[ Remove reference to "identity" token ]]
+
+ New transfer-coding value tokens SHOULD be registered in the same way
+ as new content-coding value tokens (section 3.5).
+
+ A server which receives an entity-body with a transfer-coding it does
+ not understand SHOULD return 501 (Unimplemented), and close the
+ connection. A server MUST NOT send transfer-codings to an HTTP/1.0
+ client.
+
+3.6.1 Chunked Transfer Coding
+
+ The chunked encoding modifies the body of a message in order to
+ transfer it as a series of chunks, each with its own size indicator,
+ followed by an OPTIONAL trailer containing entity-header fields. This
+ allows dynamically produced content to be transferred along with the
+ information necessary for the recipient to verify that it has
+ received the full message.
+
+ Chunked-Body = *chunk
+ last-chunk
+ trailer
+ CRLF
+
+ chunk = chunk-size [ chunk-extension ] CRLF
+ chunk-data CRLF
+ chunk-size = 1*HEX
+ last-chunk = 1*("0") [ chunk-extension ] CRLF
+
+ chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token | quoted-string
+ chunk-data = chunk-size(OCTET)
+ trailer = *(entity-header CRLF)
+
+ The chunk-size field is a string of hex digits indicating the size of
+ the chunk. The chunked encoding is ended by any chunk whose size is
+ zero, followed by the trailer, which is terminated by an empty line.
+
+ [[ "the size of the chunk" means "the size of the chunk-data in ]]
+ [[ octets" ]]
+
+ The trailer allows the sender to include additional HTTP header
+ fields at the end of the message. The Trailer header field can be
+ used to indicate which header fields are included in a trailer (see
+ section 14.40).
+
+
+
+
+Fielding, et al. Standards Track [Page 25]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A server using chunked transfer-coding in a response MUST NOT use the
+ trailer for any header fields unless at least one of the following is
+ true:
+
+ a)the request included a TE header field that indicates "trailers" is
+ acceptable in the transfer-coding of the response, as described in
+ section 14.39; or,
+
+ b)the server is the origin server for the response, the trailer
+ fields consist entirely of optional metadata, and the recipient
+ could use the message (in a manner acceptable to the origin server)
+ without receiving this metadata. In other words, the origin server
+ is willing to accept the possibility that the trailer fields might
+ be silently discarded along the path to the client.
+
+ This requirement prevents an interoperability failure when the
+ message is being received by an HTTP/1.1 (or later) proxy and
+ forwarded to an HTTP/1.0 recipient. It avoids a situation where
+ compliance with the protocol would have necessitated a possibly
+ infinite buffer on the proxy.
+
+ An example process for decoding a Chunked-Body is presented in
+ appendix 19.4.6.
+
+ All HTTP/1.1 applications MUST be able to receive and decode the
+ "chunked" transfer-coding, and MUST ignore chunk-extension extensions
+ they do not understand.
+
+3.7 Media Types
+
+ HTTP uses Internet Media Types [17] in the Content-Type (section
+ 14.17) and Accept (section 14.1) header fields in order to provide
+ open and extensible data typing and type negotiation.
+
+ media-type = type "/" subtype *( ";" parameter )
+ type = token
+ subtype = token
+
+ Parameters MAY follow the type/subtype in the form of attribute/value
+ pairs (as defined in section 3.6).
+
+ The type, subtype, and parameter attribute names are case-
+ insensitive. Parameter values might or might not be case-sensitive,
+ depending on the semantics of the parameter name. Linear white space
+ (LWS) MUST NOT be used between the type and subtype, nor between an
+ attribute and its value. The presence or absence of a parameter might
+ be significant to the processing of a media-type, depending on its
+ definition within the media type registry.
+
+
+
+Fielding, et al. Standards Track [Page 26]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Note that some older HTTP applications do not recognize media type
+ parameters. When sending data to older HTTP applications,
+ implementations SHOULD only use media type parameters when they are
+ required by that type/subtype definition.
+
+ Media-type values are registered with the Internet Assigned Number
+ Authority (IANA [19]). The media type registration process is
+ outlined in RFC 1590 [17]. Use of non-registered media types is
+ discouraged.
+
+ [[ "RFC 1590" should be "RFC 2048" ]]
+
+3.7.1 Canonicalization and Text Defaults
+
+ Internet media types are registered with a canonical form. An
+ entity-body transferred via HTTP messages MUST be represented in the
+ appropriate canonical form prior to its transmission except for
+ "text" types, as defined in the next paragraph.
+
+ When in canonical form, media subtypes of the "text" type use CRLF as
+ the text line break. HTTP relaxes this requirement and allows the
+ transport of text media with plain CR or LF alone representing a line
+ break when it is done consistently for an entire entity-body. HTTP
+ applications MUST accept CRLF, bare CR, and bare LF as being
+ representative of a line break in text media received via HTTP. In
+ addition, if the text is represented in a character set that does not
+ use octets 13 and 10 for CR and LF respectively, as is the case for
+ some multi-byte character sets, HTTP allows the use of whatever octet
+ sequences are defined by that character set to represent the
+ equivalent of CR and LF for line breaks. This flexibility regarding
+ line breaks applies only to text media in the entity-body; a bare CR
+ or LF MUST NOT be substituted for CRLF within any of the HTTP control
+ structures (such as header fields and multipart boundaries).
+
+ If an entity-body is encoded with a content-coding, the underlying
+ data MUST be in a form defined above prior to being encoded.
+
+ The "charset" parameter is used with some media types to define the
+ character set (section 3.4) of the data. When no explicit charset
+ parameter is provided by the sender, media subtypes of the "text"
+ type are defined to have a default charset value of "ISO-8859-1" when
+ received via HTTP. Data in character sets other than "ISO-8859-1" or
+ its subsets MUST be labeled with an appropriate charset value. See
+ section 3.4.1 for compatibility problems.
+
+3.7.2 Multipart Types
+
+ MIME provides for a number of "multipart" types -- encapsulations of
+ one or more entities within a single message-body. All multipart
+ types share a common syntax, as defined in section 5.1.1 of RFC 2046
+
+
+
+Fielding, et al. Standards Track [Page 27]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ [40], and MUST include a boundary parameter as part of the media type
+ value. The message body is itself a protocol element and MUST
+ therefore use only CRLF to represent line breaks between body-parts.
+ Unlike in RFC 2046, the epilogue of any multipart message MUST be
+ empty; HTTP applications MUST NOT transmit the epilogue (even if the
+ original multipart contains an epilogue). These restrictions exist in
+ order to preserve the self-delimiting nature of a multipart message-
+ body, wherein the "end" of the message-body is indicated by the
+ ending multipart boundary.
+
+ In general, HTTP treats a multipart message-body no differently than
+ any other media type: strictly as payload. The one exception is the
+ "multipart/byteranges" type (appendix 19.2) when it appears in a 206
+ (Partial Content) response, which will be interpreted by some HTTP
+ caching mechanisms as described in sections 13.5.4 and 14.16. In all
+ other cases, an HTTP user agent SHOULD follow the same or similar
+ behavior as a MIME user agent would upon receipt of a multipart type.
+ The MIME header fields within each body-part of a multipart message-
+ body do not have any significance to HTTP beyond that defined by
+ their MIME semantics.
+
+ In general, an HTTP user agent SHOULD follow the same or similar
+ behavior as a MIME user agent would upon receipt of a multipart type.
+ If an application receives an unrecognized multipart subtype, the
+ application MUST treat it as being equivalent to "multipart/mixed".
+
+ Note: The "multipart/form-data" type has been specifically defined
+ for carrying form data suitable for processing via the POST
+ request method, as described in RFC 1867 [15].
+
+3.8 Product Tokens
+
+ Product tokens are used to allow communicating applications to
+ identify themselves by software name and version. Most fields using
+ product tokens also allow sub-products which form a significant part
+ of the application to be listed, separated by white space. By
+ convention, the products are listed in order of their significance
+ for identifying the application.
+
+ product = token ["/" product-version]
+ product-version = token
+
+ Examples:
+
+ User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+ Server: Apache/0.8.4
+
+
+
+
+
+Fielding, et al. Standards Track [Page 28]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Product tokens SHOULD be short and to the point. They MUST NOT be
+ used for advertising or other non-essential information. Although any
+ token character MAY appear in a product-version, this token SHOULD
+ only be used for a version identifier (i.e., successive versions of
+ the same product SHOULD only differ in the product-version portion of
+ the product value).
+
+3.9 Quality Values
+
+ HTTP content negotiation (section 12) uses short "floating point"
+ numbers to indicate the relative importance ("weight") of various
+ negotiable parameters. A weight is normalized to a real number in
+ the range 0 through 1, where 0 is the minimum and 1 the maximum
+ value. If a parameter has a quality value of 0, then content with
+ this parameter is `not acceptable' for the client. HTTP/1.1
+ applications MUST NOT generate more than three digits after the
+ decimal point. User configuration of these values SHOULD also be
+ limited in this fashion.
+
+ qvalue = ( "0" [ "." 0*3DIGIT ] )
+ | ( "1" [ "." 0*3("0") ] )
+
+ "Quality values" is a misnomer, since these values merely represent
+ relative degradation in desired quality.
+
+3.10 Language Tags
+
+ A language tag identifies a natural language spoken, written, or
+ otherwise conveyed by human beings for communication of information
+ to other human beings. Computer languages are explicitly excluded.
+ HTTP uses language tags within the Accept-Language and Content-
+ Language fields.
+
+ The syntax and registry of HTTP language tags is the same as that
+ defined by RFC 1766 [1]. In summary, a language tag is composed of 1
+ or more parts: A primary language tag and a possibly empty series of
+ subtags:
+
+ language-tag = primary-tag *( "-" subtag )
+ primary-tag = 1*8ALPHA
+ subtag = 1*8ALPHA
+
+ [[ Updated by RFC 3066: subtags may now contain digits ]]
+
+ White space is not allowed within the tag and all tags are case-
+ insensitive. The name space of language tags is administered by the
+ IANA. Example tags include:
+
+ en, en-US, en-cockney, i-cherokee, x-pig-latin
+
+
+
+
+Fielding, et al. Standards Track [Page 29]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ where any two-letter primary-tag is an ISO-639 language abbreviation
+ and any two-letter initial subtag is an ISO-3166 country code. (The
+ last three tags above are not registered tags; all but the last are
+ examples of tags which could be registered in future.)
+
+3.11 Entity Tags
+
+ Entity tags are used for comparing two or more entities from the same
+ requested resource. HTTP/1.1 uses entity tags in the ETag (section
+ 14.19), If-Match (section 14.24), If-None-Match (section 14.26), and
+ If-Range (section 14.27) header fields. The definition of how they
+ are used and compared as cache validators is in section 13.3.3. An
+ entity tag consists of an opaque quoted string, possibly prefixed by
+ a weakness indicator.
+
+ entity-tag = [ weak ] opaque-tag
+ weak = "W/"
+ opaque-tag = quoted-string
+
+ A "strong entity tag" MAY be shared by two entities of a resource
+ only if they are equivalent by octet equality.
+
+ A "weak entity tag," indicated by the "W/" prefix, MAY be shared by
+ two entities of a resource only if the entities are equivalent and
+ could be substituted for each other with no significant change in
+ semantics. A weak entity tag can only be used for weak comparison.
+
+ An entity tag MUST be unique across all versions of all entities
+ associated with a particular resource. A given entity tag value MAY
+ be used for entities obtained by requests on different URIs. The use
+ of the same entity tag value in conjunction with entities obtained by
+ requests on different URIs does not imply the equivalence of those
+ entities.
+
+3.12 Range Units
+
+ HTTP/1.1 allows a client to request that only part (a range of) the
+ response entity be included within the response. HTTP/1.1 uses range
+ units in the Range (section 14.35) and Content-Range (section 14.16)
+ header fields. An entity can be broken down into subranges according
+ to various structural units.
+
+ range-unit = bytes-unit | other-range-unit
+ bytes-unit = "bytes"
+ other-range-unit = token
+
+ The only range unit defined by HTTP/1.1 is "bytes". HTTP/1.1
+ implementations MAY ignore ranges specified using other units.
+
+
+
+Fielding, et al. Standards Track [Page 30]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ HTTP/1.1 has been designed to allow implementations of applications
+ that do not depend on knowledge of ranges.
+
+4 HTTP Message
+
+4.1 Message Types
+
+ HTTP messages consist of requests from client to server and responses
+ from server to client.
+
+ HTTP-message = Request | Response ; HTTP/1.1 messages
+
+ Request (section 5) and Response (section 6) messages use the generic
+ message format of RFC 822 [9] for transferring entities (the payload
+ of the message). Both types of message consist of a start-line, zero
+ or more header fields (also known as "headers"), an empty line (i.e.,
+ a line with nothing preceding the CRLF) indicating the end of the
+ header fields, and possibly a message-body.
+
+ generic-message = start-line
+ *(message-header CRLF)
+ CRLF
+ [ message-body ]
+ start-line = Request-Line | Status-Line
+
+ In the interest of robustness, servers SHOULD ignore any empty
+ line(s) received where a Request-Line is expected. In other words, if
+ the server is reading the protocol stream at the beginning of a
+ message and receives a CRLF first, it should ignore the CRLF.
+
+ Certain buggy HTTP/1.0 client implementations generate extra CRLF's
+ after a POST request. To restate what is explicitly forbidden by the
+ BNF, an HTTP/1.1 client MUST NOT preface or follow a request with an
+ extra CRLF.
+
+4.2 Message Headers
+
+ HTTP header fields, which include general-header (section 4.5),
+ request-header (section 5.3), response-header (section 6.2), and
+ entity-header (section 7.1) fields, follow the same generic format as
+ that given in Section 3.1 of RFC 822 [9]. Each header field consists
+ of a name followed by a colon (":") and the field value. Field names
+ are case-insensitive. The field value MAY be preceded by any amount
+ of LWS, though a single SP is preferred. Header fields can be
+ extended over multiple lines by preceding each extra line with at
+ least one SP or HT. Applications ought to follow "common form", where
+ one is known or indicated, when generating HTTP constructs, since
+ there might exist some implementations that fail to accept anything
+
+
+
+Fielding, et al. Standards Track [Page 31]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ beyond the common forms.
+
+ message-header = field-name ":" [ field-value ]
+ field-name = token
+ field-value = *( field-content | LWS )
+ field-content = <the OCTETs making up the field-value
+ and consisting of either *TEXT or combinations
+ of token, separators, and quoted-string>
+
+ The field-content does not include any leading or trailing LWS:
+ linear white space occurring before the first non-whitespace
+ character of the field-value or after the last non-whitespace
+ character of the field-value. Such leading or trailing LWS MAY be
+ removed without changing the semantics of the field value. Any LWS
+ that occurs between field-content MAY be replaced with a single SP
+ before interpreting the field value or forwarding the message
+ downstream.
+
+ The order in which header fields with differing field names are
+ received is not significant. However, it is "good practice" to send
+ general-header fields first, followed by request-header or response-
+ header fields, and ending with the entity-header fields.
+
+ Multiple message-header fields with the same field-name MAY be
+ present in a message if and only if the entire field-value for that
+ header field is defined as a comma-separated list [i.e., #(values)].
+ It MUST be possible to combine the multiple header fields into one
+ "field-name: field-value" pair, without changing the semantics of the
+ message, by appending each subsequent field-value to the first, each
+ separated by a comma. The order in which header fields with the same
+ field-name are received is therefore significant to the
+ interpretation of the combined field value, and thus a proxy MUST NOT
+ change the order of these field values when a message is forwarded.
+
+4.3 Message Body
+
+ The message-body (if any) of an HTTP message is used to carry the
+ entity-body associated with the request or response. The message-body
+ differs from the entity-body only when a transfer-coding has been
+ applied, as indicated by the Transfer-Encoding header field (section
+ 14.41).
+
+ message-body = entity-body
+ | <entity-body encoded as per Transfer-Encoding>
+
+ Transfer-Encoding MUST be used to indicate any transfer-codings
+ applied by an application to ensure safe and proper transfer of the
+ message. Transfer-Encoding is a property of the message, not of the
+
+
+
+Fielding, et al. Standards Track [Page 32]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ entity, and thus MAY be added or removed by any application along the
+ request/response chain. (However, section 3.6 places restrictions on
+ when certain transfer-codings may be used.)
+
+ The rules for when a message-body is allowed in a message differ for
+ requests and responses.
+
+ The presence of a message-body in a request is signaled by the
+ inclusion of a Content-Length or Transfer-Encoding header field in
+ the request's message-headers. A message-body MUST NOT be included in
+ a request if the specification of the request method (section 5.1.1)
+ does not allow sending an entity-body in requests. A server SHOULD
+ read and forward a message-body on any request; if the request method
+ does not include defined semantics for an entity-body, then the
+ message-body SHOULD be ignored when handling the request.
+
+ For response messages, whether or not a message-body is included with
+ a message is dependent on both the request method and the response
+ status code (section 6.1.1). All responses to the HEAD request method
+ MUST NOT include a message-body, even though the presence of entity-
+ header fields might lead one to believe they do. All 1xx
+ (informational), 204 (no content), and 304 (not modified) responses
+ MUST NOT include a message-body. All other responses do include a
+ message-body, although it MAY be of zero length.
+
+4.4 Message Length
+
+ The transfer-length of a message is the length of the message-body as
+ it appears in the message; that is, after any transfer-codings have
+ been applied. When a message-body is included with a message, the
+ transfer-length of that body is determined by one of the following
+ (in order of precedence):
+
+ 1.Any response message which "MUST NOT" include a message-body (such
+ as the 1xx, 204, and 304 responses and any response to a HEAD
+ request) is always terminated by the first empty line after the
+ header fields, regardless of the entity-header fields present in
+ the message.
+
+ 2.If a Transfer-Encoding header field (section 14.41) is present and
+ has any value other than "identity", then the transfer-length is
+ defined by use of the "chunked" transfer-coding (section 3.6),
+ unless the message is terminated by closing the connection.
+
+ [[ Remove 'and has any value other than "identity"' ]]
+
+ 3.If a Content-Length header field (section 14.13) is present, its
+ decimal value in OCTETs represents both the entity-length and the
+ transfer-length. The Content-Length header field MUST NOT be sent
+ if these two lengths are different (i.e., if a Transfer-Encoding
+
+
+
+Fielding, et al. Standards Track [Page 33]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ header field is present). If a message is received with both a
+ Transfer-Encoding header field and a Content-Length header field,
+ the latter MUST be ignored.
+
+ 4.If the message uses the media type "multipart/byteranges", and the
+ ransfer-length is not otherwise specified, then this self-
+ elimiting media type defines the transfer-length. This media type
+ UST NOT be used unless the sender knows that the recipient can arse
+ it; the presence in a request of a Range header with ultiple byte-
+ range specifiers from a 1.1 client implies that the lient can parse
+ multipart/byteranges responses.
+
+ A range header might be forwarded by a 1.0 proxy that does not
+ understand multipart/byteranges; in this case the server MUST
+ delimit the message using methods defined in items 1,3 or 5 of
+ this section.
+
+ 5.By the server closing the connection. (Closing the connection
+ cannot be used to indicate the end of a request body, since that
+ would leave no possibility for the server to send back a response.)
+
+ For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
+ containing a message-body MUST include a valid Content-Length header
+ field unless the server is known to be HTTP/1.1 compliant. If a
+ request contains a message-body and a Content-Length is not given,
+ the server SHOULD respond with 400 (bad request) if it cannot
+ determine the length of the message, or with 411 (length required) if
+ it wishes to insist on receiving a valid Content-Length.
+
+ All HTTP/1.1 applications that receive entities MUST accept the
+ "chunked" transfer-coding (section 3.6), thus allowing this mechanism
+ to be used for messages when the message length cannot be determined
+ in advance.
+
+ Messages MUST NOT include both a Content-Length header field and a
+ non-identity transfer-coding. If the message does include a non-
+ identity transfer-coding, the Content-Length MUST be ignored.
+
+ [[ Remove "non-identity" both times ]]
+
+ When a Content-Length is given in a message where a message-body is
+ allowed, its field value MUST exactly match the number of OCTETs in
+ the message-body. HTTP/1.1 user agents MUST notify the user when an
+ invalid length is received and detected.
+
+4.5 General Header Fields
+
+ There are a few header fields which have general applicability for
+ both request and response messages, but which do not apply to the
+ entity being transferred. These header fields apply only to the
+
+
+
+Fielding, et al. Standards Track [Page 34]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ message being transmitted.
+
+ general-header = Cache-Control ; Section 14.9
+ | Connection ; Section 14.10
+ | Date ; Section 14.18
+ | Pragma ; Section 14.32
+ | Trailer ; Section 14.40
+ | Transfer-Encoding ; Section 14.41
+ | Upgrade ; Section 14.42
+ | Via ; Section 14.45
+ | Warning ; Section 14.46
+
+ General-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields may be given the semantics of general
+ header fields if all parties in the communication recognize them to
+ be general-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+5 Request
+
+ A request message from a client to a server includes, within the
+ first line of that message, the method to be applied to the resource,
+ the identifier of the resource, and the protocol version in use.
+
+ Request = Request-Line ; Section 5.1
+ *(( general-header ; Section 4.5
+ | request-header ; Section 5.3
+ | entity-header ) CRLF) ; Section 7.1
+ CRLF
+ [ message-body ] ; Section 4.3
+
+5.1 Request-Line
+
+ The Request-Line begins with a method token, followed by the
+ Request-URI and the protocol version, and ending with CRLF. The
+ elements are separated by SP characters. No CR or LF is allowed
+ except in the final CRLF sequence.
+
+ Request-Line = Method SP Request-URI SP HTTP-Version CRLF
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 35]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+5.1.1 Method
+
+ The Method token indicates the method to be performed on the
+ resource identified by the Request-URI. The method is case-sensitive.
+
+ Method = "OPTIONS" ; Section 9.2
+ | "GET" ; Section 9.3
+ | "HEAD" ; Section 9.4
+ | "POST" ; Section 9.5
+ | "PUT" ; Section 9.6
+ | "DELETE" ; Section 9.7
+ | "TRACE" ; Section 9.8
+ | "CONNECT" ; Section 9.9
+ | extension-method
+ extension-method = token
+
+ The list of methods allowed by a resource can be specified in an
+ Allow header field (section 14.7). The return code of the response
+ always notifies the client whether a method is currently allowed on a
+ resource, since the set of allowed methods can change dynamically. An
+ origin server SHOULD return the status code 405 (Method Not Allowed)
+ if the method is known by the origin server but not allowed for the
+ requested resource, and 501 (Not Implemented) if the method is
+ unrecognized or not implemented by the origin server. The methods GET
+ and HEAD MUST be supported by all general-purpose servers. All other
+ methods are OPTIONAL; however, if the above methods are implemented,
+ they MUST be implemented with the same semantics as those specified
+ in section 9.
+
+5.1.2 Request-URI
+
+ The Request-URI is a Uniform Resource Identifier (section 3.2) and
+ identifies the resource upon which to apply the request.
+
+ Request-URI = "*" | absoluteURI | abs_path | authority
+ [[ Request-URI = "*" | absoluteURI | abs_path [ "?" query ] | authority ]]
+
+ The four options for Request-URI are dependent on the nature of the
+ request. The asterisk "*" means that the request does not apply to a
+ particular resource, but to the server itself, and is only allowed
+ when the method used does not necessarily apply to a resource. One
+ example would be
+
+ OPTIONS * HTTP/1.1
+
+ The absoluteURI form is REQUIRED when the request is being made to a
+ proxy. The proxy is requested to forward the request or service it
+ from a valid cache, and return the response. Note that the proxy MAY
+ forward the request on to another proxy or directly to the server
+
+
+
+Fielding, et al. Standards Track [Page 36]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ specified by the absoluteURI. In order to avoid request loops, a
+ proxy MUST be able to recognize all of its server names, including
+ any aliases, local variations, and the numeric IP address. An example
+ Request-Line would be:
+
+ GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1
+
+ To allow for transition to absoluteURIs in all requests in future
+ versions of HTTP, all HTTP/1.1 servers MUST accept the absoluteURI
+ form in requests, even though HTTP/1.1 clients will only generate
+ them in requests to proxies.
+
+ The authority form is only used by the CONNECT method (section 9.9).
+
+ The most common form of Request-URI is that used to identify a
+ resource on an origin server or gateway. In this case the absolute
+ path of the URI MUST be transmitted (see section 3.2.1, abs_path) as
+ the Request-URI, and the network location of the URI (authority) MUST
+ be transmitted in a Host header field. For example, a client wishing
+ to retrieve the resource above directly from the origin server would
+ create a TCP connection to port 80 of the host "www.w3.org" and send
+ the lines:
+
+ GET /pub/WWW/TheProject.html HTTP/1.1
+ Host: www.w3.org
+
+ followed by the remainder of the Request. Note that the absolute path
+ cannot be empty; if none is present in the original URI, it MUST be
+ given as "/" (the server root).
+
+ The Request-URI is transmitted in the format specified in section
+ 3.2.1. If the Request-URI is encoded using the "% HEX HEX" encoding
+ [42], the origin server MUST decode the Request-URI in order to
+ properly interpret the request. Servers SHOULD respond to invalid
+ Request-URIs with an appropriate status code.
+
+ A transparent proxy MUST NOT rewrite the "abs_path" part of the
+ received Request-URI when forwarding it to the next inbound server,
+ except as noted above to replace a null abs_path with "/".
+
+ Note: The "no rewrite" rule prevents the proxy from changing the
+ meaning of the request when the origin server is improperly using
+ a non-reserved URI character for a reserved purpose. Implementors
+ should be aware that some pre-HTTP/1.1 proxies have been known to
+ rewrite the Request-URI.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 37]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+5.2 The Resource Identified by a Request
+
+ The exact resource identified by an Internet request is determined by
+ examining both the Request-URI and the Host header field.
+
+ An origin server that does not allow resources to differ by the
+ requested host MAY ignore the Host header field value when
+ determining the resource identified by an HTTP/1.1 request. (But see
+ section 19.6.1.1 for other requirements on Host support in HTTP/1.1.)
+
+ An origin server that does differentiate resources based on the host
+ requested (sometimes referred to as virtual hosts or vanity host
+ names) MUST use the following rules for determining the requested
+ resource on an HTTP/1.1 request:
+
+ 1. If Request-URI is an absoluteURI, the host is part of the
+ Request-URI. Any Host header field value in the request MUST be
+ ignored.
+
+ 2. If the Request-URI is not an absoluteURI, and the request includes
+ a Host header field, the host is determined by the Host header
+ field value.
+
+ 3. If the host as determined by rule 1 or 2 is not a valid host on
+ the server, the response MUST be a 400 (Bad Request) error message.
+
+ Recipients of an HTTP/1.0 request that lacks a Host header field MAY
+ attempt to use heuristics (e.g., examination of the URI path for
+ something unique to a particular host) in order to determine what
+ exact resource is being requested.
+
+5.3 Request Header Fields
+
+ The request-header fields allow the client to pass additional
+ information about the request, and about the client itself, to the
+ server. These fields act as request modifiers, with semantics
+ equivalent to the parameters on a programming language method
+ invocation.
+
+ request-header = Accept ; Section 14.1
+ | Accept-Charset ; Section 14.2
+ | Accept-Encoding ; Section 14.3
+ | Accept-Language ; Section 14.4
+ | Authorization ; Section 14.8
+ | Expect ; Section 14.20
+ | From ; Section 14.22
+ | Host ; Section 14.23
+ | If-Match ; Section 14.24
+
+
+
+Fielding, et al. Standards Track [Page 38]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ | If-Modified-Since ; Section 14.25
+ | If-None-Match ; Section 14.26
+ | If-Range ; Section 14.27
+ | If-Unmodified-Since ; Section 14.28
+ | Max-Forwards ; Section 14.31
+ | Proxy-Authorization ; Section 14.34
+ | Range ; Section 14.35
+ | Referer ; Section 14.36
+ | TE ; Section 14.39
+ | User-Agent ; Section 14.43
+
+ Request-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields MAY be given the semantics of request-
+ header fields if all parties in the communication recognize them to
+ be request-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+6 Response
+
+ After receiving and interpreting a request message, a server responds
+ with an HTTP response message.
+
+ Response = Status-Line ; Section 6.1
+ *(( general-header ; Section 4.5
+ | response-header ; Section 6.2
+ | entity-header ) CRLF) ; Section 7.1
+ CRLF
+ [ message-body ] ; Section 7.2
+
+6.1 Status-Line
+
+ The first line of a Response message is the Status-Line, consisting
+ of the protocol version followed by a numeric status code and its
+ associated textual phrase, with each element separated by SP
+ characters. No CR or LF is allowed except in the final CRLF sequence.
+
+ Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+
+6.1.1 Status Code and Reason Phrase
+
+ The Status-Code element is a 3-digit integer result code of the
+ attempt to understand and satisfy the request. These codes are fully
+ defined in section 10. The Reason-Phrase is intended to give a short
+ textual description of the Status-Code. The Status-Code is intended
+ for use by automata and the Reason-Phrase is intended for the human
+ user. The client is not required to examine or display the Reason-
+ Phrase.
+
+
+
+Fielding, et al. Standards Track [Page 39]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The first digit of the Status-Code defines the class of response. The
+ last two digits do not have any categorization role. There are 5
+ values for the first digit:
+
+ - 1xx: Informational - Request received, continuing process
+
+ - 2xx: Success - The action was successfully received,
+ understood, and accepted
+
+ - 3xx: Redirection - Further action must be taken in order to
+ complete the request
+
+ - 4xx: Client Error - The request contains bad syntax or cannot
+ be fulfilled
+
+ - 5xx: Server Error - The server failed to fulfill an apparently
+ valid request
+
+ The individual values of the numeric status codes defined for
+ HTTP/1.1, and an example set of corresponding Reason-Phrase's, are
+ presented below. The reason phrases listed here are only
+ recommendations -- they MAY be replaced by local equivalents without
+ affecting the protocol.
+
+ Status-Code =
+ "100" ; Section 10.1.1: Continue
+ | "101" ; Section 10.1.2: Switching Protocols
+ | "200" ; Section 10.2.1: OK
+ | "201" ; Section 10.2.2: Created
+ | "202" ; Section 10.2.3: Accepted
+ | "203" ; Section 10.2.4: Non-Authoritative Information
+ | "204" ; Section 10.2.5: No Content
+ | "205" ; Section 10.2.6: Reset Content
+ | "206" ; Section 10.2.7: Partial Content
+ | "300" ; Section 10.3.1: Multiple Choices
+ | "301" ; Section 10.3.2: Moved Permanently
+ | "302" ; Section 10.3.3: Found
+ | "303" ; Section 10.3.4: See Other
+ | "304" ; Section 10.3.5: Not Modified
+ | "305" ; Section 10.3.6: Use Proxy
+ | "307" ; Section 10.3.8: Temporary Redirect
+ | "400" ; Section 10.4.1: Bad Request
+ | "401" ; Section 10.4.2: Unauthorized
+ | "402" ; Section 10.4.3: Payment Required
+ | "403" ; Section 10.4.4: Forbidden
+ | "404" ; Section 10.4.5: Not Found
+ | "405" ; Section 10.4.6: Method Not Allowed
+ | "406" ; Section 10.4.7: Not Acceptable
+
+
+
+Fielding, et al. Standards Track [Page 40]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ | "407" ; Section 10.4.8: Proxy Authentication Required
+ | "408" ; Section 10.4.9: Request Time-out
+ | "409" ; Section 10.4.10: Conflict
+ | "410" ; Section 10.4.11: Gone
+ | "411" ; Section 10.4.12: Length Required
+ | "412" ; Section 10.4.13: Precondition Failed
+ | "413" ; Section 10.4.14: Request Entity Too Large
+ | "414" ; Section 10.4.15: Request-URI Too Large
+ | "415" ; Section 10.4.16: Unsupported Media Type
+ | "416" ; Section 10.4.17: Requested range not satisfiable
+ | "417" ; Section 10.4.18: Expectation Failed
+ | "500" ; Section 10.5.1: Internal Server Error
+ | "501" ; Section 10.5.2: Not Implemented
+ | "502" ; Section 10.5.3: Bad Gateway
+ | "503" ; Section 10.5.4: Service Unavailable
+ | "504" ; Section 10.5.5: Gateway Time-out
+ | "505" ; Section 10.5.6: HTTP Version not supported
+ | extension-code
+
+ extension-code = 3DIGIT
+ Reason-Phrase = *<TEXT, excluding CR, LF>
+
+ HTTP status codes are extensible. HTTP applications are not required
+ to understand the meaning of all registered status codes, though such
+ understanding is obviously desirable. However, applications MUST
+ understand the class of any status code, as indicated by the first
+ digit, and treat any unrecognized response as being equivalent to the
+ x00 status code of that class, with the exception that an
+ unrecognized response MUST NOT be cached. For example, if an
+ unrecognized status code of 431 is received by the client, it can
+ safely assume that there was something wrong with its request and
+ treat the response as if it had received a 400 status code. In such
+ cases, user agents SHOULD present to the user the entity returned
+ with the response, since that entity is likely to include human-
+ readable information which will explain the unusual status.
+
+6.2 Response Header Fields
+
+ The response-header fields allow the server to pass additional
+ information about the response which cannot be placed in the Status-
+ Line. These header fields give information about the server and about
+ further access to the resource identified by the Request-URI.
+
+ response-header = Accept-Ranges ; Section 14.5
+ | Age ; Section 14.6
+ | ETag ; Section 14.19
+ | Location ; Section 14.30
+ | Proxy-Authenticate ; Section 14.33
+
+
+
+Fielding, et al. Standards Track [Page 41]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ | Retry-After ; Section 14.37
+ | Server ; Section 14.38
+ | Vary ; Section 14.44
+ | WWW-Authenticate ; Section 14.47
+
+ Response-header field names can be extended reliably only in
+ combination with a change in the protocol version. However, new or
+ experimental header fields MAY be given the semantics of response-
+ header fields if all parties in the communication recognize them to
+ be response-header fields. Unrecognized header fields are treated as
+ entity-header fields.
+
+7 Entity
+
+ Request and Response messages MAY transfer an entity if not otherwise
+ restricted by the request method or response status code. An entity
+ consists of entity-header fields and an entity-body, although some
+ responses will only include the entity-headers.
+
+ In this section, both sender and recipient refer to either the client
+ or the server, depending on who sends and who receives the entity.
+
+7.1 Entity Header Fields
+
+ Entity-header fields define metainformation about the entity-body or,
+ if no body is present, about the resource identified by the request.
+ Some of this metainformation is OPTIONAL; some might be REQUIRED by
+ portions of this specification.
+
+ entity-header = Allow ; Section 14.7
+ | Content-Encoding ; Section 14.11
+ | Content-Language ; Section 14.12
+ | Content-Length ; Section 14.13
+ | Content-Location ; Section 14.14
+ | Content-MD5 ; Section 14.15
+ | Content-Range ; Section 14.16
+ | Content-Type ; Section 14.17
+ | Expires ; Section 14.21
+ | Last-Modified ; Section 14.29
+ | extension-header
+
+ extension-header = message-header
+
+ The extension-header mechanism allows additional entity-header fields
+ to be defined without changing the protocol, but these fields cannot
+ be assumed to be recognizable by the recipient. Unrecognized header
+ fields SHOULD be ignored by the recipient and MUST be forwarded by
+ transparent proxies.
+
+
+
+Fielding, et al. Standards Track [Page 42]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+7.2 Entity Body
+
+ The entity-body (if any) sent with an HTTP request or response is in
+ a format and encoding defined by the entity-header fields.
+
+ entity-body = *OCTET
+
+ An entity-body is only present in a message when a message-body is
+ present, as described in section 4.3. The entity-body is obtained
+ from the message-body by decoding any Transfer-Encoding that might
+ have been applied to ensure safe and proper transfer of the message.
+
+7.2.1 Type
+
+ When an entity-body is included with a message, the data type of that
+ body is determined via the header fields Content-Type and Content-
+ Encoding. These define a two-layer, ordered encoding model:
+
+ entity-body := Content-Encoding( Content-Type( data ) )
+
+ Content-Type specifies the media type of the underlying data.
+ Content-Encoding may be used to indicate any additional content
+ codings applied to the data, usually for the purpose of data
+ compression, that are a property of the requested resource. There is
+ no default encoding.
+
+ Any HTTP/1.1 message containing an entity-body SHOULD include a
+ Content-Type header field defining the media type of that body. If
+ and only if the media type is not given by a Content-Type field, the
+ recipient MAY attempt to guess the media type via inspection of its
+ content and/or the name extension(s) of the URI used to identify the
+ resource. If the media type remains unknown, the recipient SHOULD
+ treat it as type "application/octet-stream".
+
+7.2.2 Entity Length
+
+ The entity-length of a message is the length of the message-body
+ before any transfer-codings have been applied. Section 4.4 defines
+ how the transfer-length of a message-body is determined.
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 43]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+8 Connections
+
+8.1 Persistent Connections
+
+8.1.1 Purpose
+
+ Prior to persistent connections, a separate TCP connection was
+ established to fetch each URL, increasing the load on HTTP servers
+ and causing congestion on the Internet. The use of inline images and
+ other associated data often require a client to make multiple
+ requests of the same server in a short amount of time. Analysis of
+ these performance problems and results from a prototype
+ implementation are available [26] [30]. Implementation experience and
+ measurements of actual HTTP/1.1 (RFC 2068) implementations show good
+ results [39]. Alternatives have also been explored, for example,
+ T/TCP [27].
+
+ Persistent HTTP connections have a number of advantages:
+
+ - By opening and closing fewer TCP connections, CPU time is saved
+ in routers and hosts (clients, servers, proxies, gateways,
+ tunnels, or caches), and memory used for TCP protocol control
+ blocks can be saved in hosts.
+
+ - HTTP requests and responses can be pipelined on a connection.
+ Pipelining allows a client to make multiple requests without
+ waiting for each response, allowing a single TCP connection to
+ be used much more efficiently, with much lower elapsed time.
+
+ - Network congestion is reduced by reducing the number of packets
+ caused by TCP opens, and by allowing TCP sufficient time to
+ determine the congestion state of the network.
+
+ - Latency on subsequent requests is reduced since there is no time
+ spent in TCP's connection opening handshake.
+
+ - HTTP can evolve more gracefully, since errors can be reported
+ without the penalty of closing the TCP connection. Clients using
+ future versions of HTTP might optimistically try a new feature,
+ but if communicating with an older server, retry with old
+ semantics after an error is reported.
+
+ HTTP implementations SHOULD implement persistent connections.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 44]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+8.1.2 Overall Operation
+
+ A significant difference between HTTP/1.1 and earlier versions of
+ HTTP is that persistent connections are the default behavior of any
+ HTTP connection. That is, unless otherwise indicated, the client
+ SHOULD assume that the server will maintain a persistent connection,
+ even after error responses from the server.
+
+ Persistent connections provide a mechanism by which a client and a
+ server can signal the close of a TCP connection. This signaling takes
+ place using the Connection header field (section 14.10). Once a close
+ has been signaled, the client MUST NOT send any more requests on that
+ connection.
+
+8.1.2.1 Negotiation
+
+ An HTTP/1.1 server MAY assume that a HTTP/1.1 client intends to
+ maintain a persistent connection unless a Connection header including
+ the connection-token "close" was sent in the request. If the server
+ chooses to close the connection immediately after sending the
+ response, it SHOULD send a Connection header including the
+ connection-token close.
+
+ An HTTP/1.1 client MAY expect a connection to remain open, but would
+ decide to keep it open based on whether the response from a server
+ contains a Connection header with the connection-token close. In case
+ the client does not want to maintain a connection for more than that
+ request, it SHOULD send a Connection header including the
+ connection-token close.
+
+ If either the client or the server sends the close token in the
+ Connection header, that request becomes the last one for the
+ connection.
+
+ Clients and servers SHOULD NOT assume that a persistent connection is
+ maintained for HTTP versions less than 1.1 unless it is explicitly
+ signaled. See section 19.6.2 for more information on backward
+ compatibility with HTTP/1.0 clients.
+
+ In order to remain persistent, all messages on the connection MUST
+ have a self-defined message length (i.e., one not defined by closure
+ of the connection), as described in section 4.4.
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 45]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+8.1.2.2 Pipelining
+
+ A client that supports persistent connections MAY "pipeline" its
+ requests (i.e., send multiple requests without waiting for each
+ response). A server MUST send its responses to those requests in the
+ same order that the requests were received.
+
+ Clients which assume persistent connections and pipeline immediately
+ after connection establishment SHOULD be prepared to retry their
+ connection if the first pipelined attempt fails. If a client does
+ such a retry, it MUST NOT pipeline before it knows the connection is
+ persistent. Clients MUST also be prepared to resend their requests if
+ the server closes the connection before sending all of the
+ corresponding responses.
+
+ Clients SHOULD NOT pipeline requests using non-idempotent methods or
+ non-idempotent sequences of methods (see section 9.1.2). Otherwise, a
+ premature termination of the transport connection could lead to
+ indeterminate results. A client wishing to send a non-idempotent
+ request SHOULD wait to send that request until it has received the
+ response status for the previous request.
+
+8.1.3 Proxy Servers
+
+ It is especially important that proxies correctly implement the
+ properties of the Connection header field as specified in section
+ 14.10.
+
+ The proxy server MUST signal persistent connections separately with
+ its clients and the origin servers (or other proxy servers) that it
+ connects to. Each persistent connection applies to only one transport
+ link.
+
+ A proxy server MUST NOT establish a HTTP/1.1 persistent connection
+ with an HTTP/1.0 client (but see RFC 2068 [33] for information and
+ discussion of the problems with the Keep-Alive header implemented by
+ many HTTP/1.0 clients).
+
+8.1.4 Practical Considerations
+
+ Servers will usually have some time-out value beyond which they will
+ no longer maintain an inactive connection. Proxy servers might make
+ this a higher value since it is likely that the client will be making
+ more connections through the same server. The use of persistent
+ connections places no requirements on the length (or existence) of
+ this time-out for either the client or the server.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 46]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ When a client or server wishes to time-out it SHOULD issue a graceful
+ close on the transport connection. Clients and servers SHOULD both
+ constantly watch for the other side of the transport close, and
+ respond to it as appropriate. If a client or server does not detect
+ the other side's close promptly it could cause unnecessary resource
+ drain on the network.
+
+ A client, server, or proxy MAY close the transport connection at any
+ time. For example, a client might have started to send a new request
+ at the same time that the server has decided to close the "idle"
+ connection. From the server's point of view, the connection is being
+ closed while it was idle, but from the client's point of view, a
+ request is in progress.
+
+ This means that clients, servers, and proxies MUST be able to recover
+ from asynchronous close events. Client software SHOULD reopen the
+ transport connection and retransmit the aborted sequence of requests
+ without user interaction so long as the request sequence is
+ idempotent (see section 9.1.2). Non-idempotent methods or sequences
+ MUST NOT be automatically retried, although user agents MAY offer a
+ human operator the choice of retrying the request(s). Confirmation by
+ user-agent software with semantic understanding of the application
+ MAY substitute for user confirmation. The automatic retry SHOULD NOT
+ be repeated if the second sequence of requests fails.
+
+ Servers SHOULD always respond to at least one request per connection,
+ if at all possible. Servers SHOULD NOT close a connection in the
+ middle of transmitting a response, unless a network or client failure
+ is suspected.
+
+ Clients that use persistent connections SHOULD limit the number of
+ simultaneous connections that they maintain to a given server. A
+ single-user client SHOULD NOT maintain more than 2 connections with
+ any server or proxy. A proxy SHOULD use up to 2*N connections to
+ another server or proxy, where N is the number of simultaneously
+ active users. These guidelines are intended to improve HTTP response
+ times and avoid congestion.
+
+8.2 Message Transmission Requirements
+
+8.2.1 Persistent Connections and Flow Control
+
+ HTTP/1.1 servers SHOULD maintain persistent connections and use TCP's
+ flow control mechanisms to resolve temporary overloads, rather than
+ terminating connections with the expectation that clients will retry.
+ The latter technique can exacerbate network congestion.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 47]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+8.2.2 Monitoring Connections for Error Status Messages
+
+ An HTTP/1.1 (or later) client sending a message-body SHOULD monitor
+ the network connection for an error status while it is transmitting
+ the request. If the client sees an error status, it SHOULD
+ immediately cease transmitting the body. If the body is being sent
+ using a "chunked" encoding (section 3.6), a zero length chunk and
+ empty trailer MAY be used to prematurely mark the end of the message.
+ If the body was preceded by a Content-Length header, the client MUST
+ close the connection.
+
+8.2.3 Use of the 100 (Continue) Status
+
+ The purpose of the 100 (Continue) status (see section 10.1.1) is to
+ allow a client that is sending a request message with a request body
+ to determine if the origin server is willing to accept the request
+ (based on the request headers) before the client sends the request
+ body. In some cases, it might either be inappropriate or highly
+ inefficient for the client to send the body if the server will reject
+ the message without looking at the body.
+
+ Requirements for HTTP/1.1 clients:
+
+ - If a client will wait for a 100 (Continue) response before
+ sending the request body, it MUST send an Expect request-header
+ field (section 14.20) with the "100-continue" expectation.
+
+ - A client MUST NOT send an Expect request-header field (section
+ 14.20) with the "100-continue" expectation if it does not intend
+ to send a request body.
+
+ Because of the presence of older implementations, the protocol allows
+ ambiguous situations in which a client may send "Expect: 100-
+ continue" without receiving either a 417 (Expectation Failed) status
+ or a 100 (Continue) status. Therefore, when a client sends this
+ header field to an origin server (possibly via a proxy) from which it
+ has never seen a 100 (Continue) status, the client SHOULD NOT wait
+ for an indefinite period before sending the request body.
+
+ Requirements for HTTP/1.1 origin servers:
+
+ - Upon receiving a request which includes an Expect request-header
+ field with the "100-continue" expectation, an origin server MUST
+ either respond with 100 (Continue) status and continue to read
+ from the input stream, or respond with a final status code. The
+ origin server MUST NOT wait for the request body before sending
+ the 100 (Continue) response. If it responds with a final status
+ code, it MAY close the transport connection or it MAY continue
+
+
+
+Fielding, et al. Standards Track [Page 48]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ to read and discard the rest of the request. It MUST NOT
+ perform the requested method if it returns a final status code.
+
+ - An origin server SHOULD NOT send a 100 (Continue) response if
+ the request message does not include an Expect request-header
+ field with the "100-continue" expectation, and MUST NOT send a
+ 100 (Continue) response if such a request comes from an HTTP/1.0
+ (or earlier) client. There is an exception to this rule: for
+ compatibility with RFC 2068, a server MAY send a 100 (Continue)
+ status in response to an HTTP/1.1 PUT or POST request that does
+ not include an Expect request-header field with the "100-
+ continue" expectation. This exception, the purpose of which is
+ to minimize any client processing delays associated with an
+ undeclared wait for 100 (Continue) status, applies only to
+ HTTP/1.1 requests, and not to requests with any other HTTP-
+ version value.
+
+ - An origin server MAY omit a 100 (Continue) response if it has
+ already received some or all of the request body for the
+ corresponding request.
+
+ - An origin server that sends a 100 (Continue) response MUST
+ ultimately send a final status code, once the request body is
+ received and processed, unless it terminates the transport
+ connection prematurely.
+
+ - If an origin server receives a request that does not include an
+ Expect request-header field with the "100-continue" expectation,
+ the request includes a request body, and the server responds
+ with a final status code before reading the entire request body
+ from the transport connection, then the server SHOULD NOT close
+ the transport connection until it has read the entire request,
+ or until the client closes the connection. Otherwise, the client
+ might not reliably receive the response message. However, this
+ requirement is not be construed as preventing a server from
+ defending itself against denial-of-service attacks, or from
+ badly broken client implementations.
+
+ Requirements for HTTP/1.1 proxies:
+
+ - If a proxy receives a request that includes an Expect request-
+ header field with the "100-continue" expectation, and the proxy
+ either knows that the next-hop server complies with HTTP/1.1 or
+ higher, or does not know the HTTP version of the next-hop
+ server, it MUST forward the request, including the Expect header
+ field.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 49]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - If the proxy knows that the version of the next-hop server is
+ HTTP/1.0 or lower, it MUST NOT forward the request, and it MUST
+ respond with a 417 (Expectation Failed) status.
+
+ - Proxies SHOULD maintain a cache recording the HTTP version
+ numbers received from recently-referenced next-hop servers.
+
+ - A proxy MUST NOT forward a 100 (Continue) response if the
+ request message was received from an HTTP/1.0 (or earlier)
+ client and did not include an Expect request-header field with
+ the "100-continue" expectation. This requirement overrides the
+ general rule for forwarding of 1xx responses (see section 10.1).
+
+8.2.4 Client Behavior if Server Prematurely Closes Connection
+
+ If an HTTP/1.1 client sends a request which includes a request body,
+ but which does not include an Expect request-header field with the
+ "100-continue" expectation, and if the client is not directly
+ connected to an HTTP/1.1 origin server, and if the client sees the
+ connection close before receiving any status from the server, the
+ client SHOULD retry the request. If the client does retry this
+ request, it MAY use the following "binary exponential backoff"
+ algorithm to be assured of obtaining a reliable response:
+
+ 1. Initiate a new connection to the server
+
+ 2. Transmit the request-headers
+
+ 3. Initialize a variable R to the estimated round-trip time to the
+ server (e.g., based on the time it took to establish the
+ connection), or to a constant value of 5 seconds if the round-
+ trip time is not available.
+
+ 4. Compute T = R * (2**N), where N is the number of previous
+ retries of this request.
+
+ 5. Wait either for an error response from the server, or for T
+ seconds (whichever comes first)
+
+ 6. If no error response is received, after T seconds transmit the
+ body of the request.
+
+ 7. If client sees that the connection is closed prematurely,
+ repeat from step 1 until the request is accepted, an error
+ response is received, or the user becomes impatient and
+ terminates the retry process.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 50]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If at any point an error status is received, the client
+
+ - SHOULD NOT continue and
+
+ - SHOULD close the connection if it has not completed sending the
+ request message.
+
+9 Method Definitions
+
+ The set of common methods for HTTP/1.1 is defined below. Although
+ this set can be expanded, additional methods cannot be assumed to
+ share the same semantics for separately extended clients and servers.
+
+ The Host request-header field (section 14.23) MUST accompany all
+ HTTP/1.1 requests.
+
+9.1 Safe and Idempotent Methods
+
+9.1.1 Safe Methods
+
+ Implementors should be aware that the software represents the user in
+ their interactions over the Internet, and should be careful to allow
+ the user to be aware of any actions they might take which may have an
+ unexpected significance to themselves or others.
+
+ In particular, the convention has been established that the GET and
+ HEAD methods SHOULD NOT have the significance of taking an action
+ other than retrieval. These methods ought to be considered "safe".
+ This allows user agents to represent other methods, such as POST, PUT
+ and DELETE, in a special way, so that the user is made aware of the
+ fact that a possibly unsafe action is being requested.
+
+ Naturally, it is not possible to ensure that the server does not
+ generate side-effects as a result of performing a GET request; in
+ fact, some dynamic resources consider that a feature. The important
+ distinction here is that the user did not request the side-effects,
+ so therefore cannot be held accountable for them.
+
+9.1.2 Idempotent Methods
+
+ Methods can also have the property of "idempotence" in that (aside
+ from error or expiration issues) the side-effects of N > 0 identical
+ requests is the same as for a single request. The methods GET, HEAD,
+ PUT and DELETE share this property. Also, the methods OPTIONS and
+ TRACE SHOULD NOT have side effects, and so are inherently idempotent.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 51]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ However, it is possible that a sequence of several requests is non-
+ idempotent, even if all of the methods executed in that sequence are
+ idempotent. (A sequence is idempotent if a single execution of the
+ entire sequence always yields a result that is not changed by a
+ reexecution of all, or part, of that sequence.) For example, a
+ sequence is non-idempotent if its result depends on a value that is
+ later modified in the same sequence.
+
+ A sequence that never has side effects is idempotent, by definition
+ (provided that no concurrent operations are being executed on the
+ same set of resources).
+
+9.2 OPTIONS
+
+ The OPTIONS method represents a request for information about the
+ communication options available on the request/response chain
+ identified by the Request-URI. This method allows the client to
+ determine the options and/or requirements associated with a resource,
+ or the capabilities of a server, without implying a resource action
+ or initiating a resource retrieval.
+
+ Responses to this method are not cacheable.
+
+ If the OPTIONS request includes an entity-body (as indicated by the
+ presence of Content-Length or Transfer-Encoding), then the media type
+ MUST be indicated by a Content-Type field. Although this
+ specification does not define any use for such a body, future
+ extensions to HTTP might use the OPTIONS body to make more detailed
+ queries on the server. A server that does not support such an
+ extension MAY discard the request body.
+
+ If the Request-URI is an asterisk ("*"), the OPTIONS request is
+ intended to apply to the server in general rather than to a specific
+ resource. Since a server's communication options typically depend on
+ the resource, the "*" request is only useful as a "ping" or "no-op"
+ type of method; it does nothing beyond allowing the client to test
+ the capabilities of the server. For example, this can be used to test
+ a proxy for HTTP/1.1 compliance (or lack thereof).
+
+ If the Request-URI is not an asterisk, the OPTIONS request applies
+ only to the options that are available when communicating with that
+ resource.
+
+ A 200 response SHOULD include any header fields that indicate
+ optional features implemented by the server and applicable to that
+ resource (e.g., Allow), possibly including extensions not defined by
+ this specification. The response body, if any, SHOULD also include
+ information about the communication options. The format for such a
+
+
+
+Fielding, et al. Standards Track [Page 52]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ body is not defined by this specification, but might be defined by
+ future extensions to HTTP. Content negotiation MAY be used to select
+ the appropriate response format. If no response body is included, the
+ response MUST include a Content-Length field with a field-value of
+ "0".
+
+ The Max-Forwards request-header field MAY be used to target a
+ specific proxy in the request chain. When a proxy receives an OPTIONS
+ request on an absoluteURI for which request forwarding is permitted,
+ the proxy MUST check for a Max-Forwards field. If the Max-Forwards
+ field-value is zero ("0"), the proxy MUST NOT forward the message;
+ instead, the proxy SHOULD respond with its own communication options.
+ If the Max-Forwards field-value is an integer greater than zero, the
+ proxy MUST decrement the field-value when it forwards the request. If
+ no Max-Forwards field is present in the request, then the forwarded
+ request MUST NOT include a Max-Forwards field.
+
+9.3 GET
+
+ The GET method means retrieve whatever information (in the form of an
+ entity) is identified by the Request-URI. If the Request-URI refers
+ to a data-producing process, it is the produced data which shall be
+ returned as the entity in the response and not the source text of the
+ process, unless that text happens to be the output of the process.
+
+ The semantics of the GET method change to a "conditional GET" if the
+ request message includes an If-Modified-Since, If-Unmodified-Since,
+ If-Match, If-None-Match, or If-Range header field. A conditional GET
+ method requests that the entity be transferred only under the
+ circumstances described by the conditional header field(s). The
+ conditional GET method is intended to reduce unnecessary network
+ usage by allowing cached entities to be refreshed without requiring
+ multiple requests or transferring data already held by the client.
+
+ The semantics of the GET method change to a "partial GET" if the
+ request message includes a Range header field. A partial GET requests
+ that only part of the entity be transferred, as described in section
+ 14.35. The partial GET method is intended to reduce unnecessary
+ network usage by allowing partially-retrieved entities to be
+ completed without transferring data already held by the client.
+
+ The response to a GET request is cacheable if and only if it meets
+ the requirements for HTTP caching described in section 13.
+
+ See section 15.1.3 for security considerations when used for forms.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 53]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+9.4 HEAD
+
+ The HEAD method is identical to GET except that the server MUST NOT
+ return a message-body in the response. The metainformation contained
+ in the HTTP headers in response to a HEAD request SHOULD be identical
+ to the information sent in response to a GET request. This method can
+ be used for obtaining metainformation about the entity implied by the
+ request without transferring the entity-body itself. This method is
+ often used for testing hypertext links for validity, accessibility,
+ and recent modification.
+
+ The response to a HEAD request MAY be cacheable in the sense that the
+ information contained in the response MAY be used to update a
+ previously cached entity from that resource. If the new field values
+ indicate that the cached entity differs from the current entity (as
+ would be indicated by a change in Content-Length, Content-MD5, ETag
+ or Last-Modified), then the cache MUST treat the cache entry as
+ stale.
+
+9.5 POST
+
+ The POST method is used to request that the origin server accept the
+ entity enclosed in the request as a new subordinate of the resource
+ identified by the Request-URI in the Request-Line. POST is designed
+ to allow a uniform method to cover the following functions:
+
+[[ Should be: ]]
+[[ The POST method is used to request that the origin server accept the ]]
+[[ entity enclosed in the request as data to be processed by the resource ]]
+[[ identified by the Request-URI in the Request-Line. POST is designed ]]
+[[ to allow a uniform method to cover the following functions: ]]
+
+ - Annotation of existing resources;
+
+ - Posting a message to a bulletin board, newsgroup, mailing list,
+ or similar group of articles;
+
+ - Providing a block of data, such as the result of submitting a
+ form, to a data-handling process;
+
+ - Extending a database through an append operation.
+
+ The actual function performed by the POST method is determined by the
+ server and is usually dependent on the Request-URI. The posted entity
+ is subordinate to that URI in the same way that a file is subordinate
+ to a directory containing it, a news article is subordinate to a
+ newsgroup to which it is posted, or a record is subordinate to a
+ database.
+
+ [[ Remove second sentence ("The posted entity is subordinate") above ]]
+
+ The action performed by the POST method might not result in a
+ resource that can be identified by a URI. In this case, either 200
+ (OK) or 204 (No Content) is the appropriate response status,
+ depending on whether or not the response includes an entity that
+ describes the result.
+
+
+
+Fielding, et al. Standards Track [Page 54]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If a resource has been created on the origin server, the response
+ SHOULD be 201 (Created) and contain an entity which describes the
+ status of the request and refers to the new resource, and a Location
+ header (see section 14.30).
+
+ Responses to this method are not cacheable, unless the response
+ includes appropriate Cache-Control or Expires header fields. However,
+ the 303 (See Other) response can be used to direct the user agent to
+ retrieve a cacheable resource.
+
+ POST requests MUST obey the message transmission requirements set out
+ in section 8.2.
+
+ See section 15.1.3 for security considerations.
+
+9.6 PUT
+
+ The PUT method requests that the enclosed entity be stored under the
+ supplied Request-URI. If the Request-URI refers to an already
+ existing resource, the enclosed entity SHOULD be considered as a
+ modified version of the one residing on the origin server. If the
+ Request-URI does not point to an existing resource, and that URI is
+ capable of being defined as a new resource by the requesting user
+ agent, the origin server can create the resource with that URI. If a
+ new resource is created, the origin server MUST inform the user agent
+ via the 201 (Created) response. If an existing resource is modified,
+ either the 200 (OK) or 204 (No Content) response codes SHOULD be sent
+ to indicate successful completion of the request. If the resource
+ could not be created or modified with the Request-URI, an appropriate
+ error response SHOULD be given that reflects the nature of the
+ problem. The recipient of the entity MUST NOT ignore any Content-*
+ (e.g. Content-Range) headers that it does not understand or implement
+ and MUST return a 501 (Not Implemented) response in such cases.
+
+ If the request passes through a cache and the Request-URI identifies
+ one or more currently cached entities, those entries SHOULD be
+ treated as stale. Responses to this method are not cacheable.
+
+ The fundamental difference between the POST and PUT requests is
+ reflected in the different meaning of the Request-URI. The URI in a
+ POST request identifies the resource that will handle the enclosed
+ entity. That resource might be a data-accepting process, a gateway to
+ some other protocol, or a separate entity that accepts annotations.
+ In contrast, the URI in a PUT request identifies the entity enclosed
+ with the request -- the user agent knows what URI is intended and the
+ server MUST NOT attempt to apply the request to some other resource.
+ If the server desires that the request be applied to a different URI,
+
+
+
+
+Fielding, et al. Standards Track [Page 55]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ it MUST send a 301 (Moved Permanently) response; the user agent MAY
+ then make its own decision regarding whether or not to redirect the
+ request.
+
+ A single resource MAY be identified by many different URIs. For
+ example, an article might have a URI for identifying "the current
+ version" which is separate from the URI identifying each particular
+ version. In this case, a PUT request on a general URI might result in
+ several other URIs being defined by the origin server.
+
+ HTTP/1.1 does not define how a PUT method affects the state of an
+ origin server.
+
+ PUT requests MUST obey the message transmission requirements set out
+ in section 8.2.
+
+ Unless otherwise specified for a particular entity-header, the
+ entity-headers in the PUT request SHOULD be applied to the resource
+ created or modified by the PUT.
+
+9.7 DELETE
+
+ The DELETE method requests that the origin server delete the resource
+ identified by the Request-URI. This method MAY be overridden by human
+ intervention (or other means) on the origin server. The client cannot
+ be guaranteed that the operation has been carried out, even if the
+ status code returned from the origin server indicates that the action
+ has been completed successfully. However, the server SHOULD NOT
+ indicate success unless, at the time the response is given, it
+ intends to delete the resource or move it to an inaccessible
+ location.
+
+ A successful response SHOULD be 200 (OK) if the response includes an
+ entity describing the status, 202 (Accepted) if the action has not
+ yet been enacted, or 204 (No Content) if the action has been enacted
+ but the response does not include an entity.
+
+ If the request passes through a cache and the Request-URI identifies
+ one or more currently cached entities, those entries SHOULD be
+ treated as stale. Responses to this method are not cacheable.
+
+9.8 TRACE
+
+ The TRACE method is used to invoke a remote, application-layer loop-
+ back of the request message. The final recipient of the request
+ SHOULD reflect the message received back to the client as the
+ entity-body of a 200 (OK) response. The final recipient is either the
+
+
+
+
+Fielding, et al. Standards Track [Page 56]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ origin server or the first proxy or gateway to receive a Max-Forwards
+ value of zero (0) in the request (see section 14.31). A TRACE request
+ MUST NOT include an entity.
+
+ TRACE allows the client to see what is being received at the other
+ end of the request chain and use that data for testing or diagnostic
+ information. The value of the Via header field (section 14.45) is of
+ particular interest, since it acts as a trace of the request chain.
+ Use of the Max-Forwards header field allows the client to limit the
+ length of the request chain, which is useful for testing a chain of
+ proxies forwarding messages in an infinite loop.
+
+ If the request is valid, the response SHOULD contain the entire
+ request message in the entity-body, with a Content-Type of
+ "message/http". Responses to this method MUST NOT be cached.
+
+9.9 CONNECT
+
+ This specification reserves the method name CONNECT for use with a
+ proxy that can dynamically switch to being a tunnel (e.g. SSL
+ tunneling [44]).
+
+10 Status Code Definitions
+
+ Each Status-Code is described below, including a description of which
+ method(s) it can follow and any metainformation required in the
+ response.
+
+10.1 Informational 1xx
+
+ This class of status code indicates a provisional response,
+ consisting only of the Status-Line and optional headers, and is
+ terminated by an empty line. There are no required headers for this
+ class of status code. Since HTTP/1.0 did not define any 1xx status
+ codes, servers MUST NOT send a 1xx response to an HTTP/1.0 client
+ except under experimental conditions.
+
+ A client MUST be prepared to accept one or more 1xx status responses
+ prior to a regular response, even if the client does not expect a 100
+ (Continue) status message. Unexpected 1xx status responses MAY be
+ ignored by a user agent.
+
+ Proxies MUST forward 1xx responses, unless the connection between the
+ proxy and its client has been closed, or unless the proxy itself
+ requested the generation of the 1xx response. (For example, if a
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 57]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ proxy adds a "Expect: 100-continue" field when it forwards a request,
+ then it need not forward the corresponding 100 (Continue)
+ response(s).)
+
+10.1.1 100 Continue
+
+ The client SHOULD continue with its request. This interim response is
+ used to inform the client that the initial part of the request has
+ been received and has not yet been rejected by the server. The client
+ SHOULD continue by sending the remainder of the request or, if the
+ request has already been completed, ignore this response. The server
+ MUST send a final response after the request has been completed. See
+ section 8.2.3 for detailed discussion of the use and handling of this
+ status code.
+
+10.1.2 101 Switching Protocols
+
+ The server understands and is willing to comply with the client's
+ request, via the Upgrade message header field (section 14.42), for a
+ change in the application protocol being used on this connection. The
+ server will switch protocols to those defined by the response's
+ Upgrade header field immediately after the empty line which
+ terminates the 101 response.
+
+ The protocol SHOULD be switched only when it is advantageous to do
+ so. For example, switching to a newer version of HTTP is advantageous
+ over older versions, and switching to a real-time, synchronous
+ protocol might be advantageous when delivering resources that use
+ such features.
+
+10.2 Successful 2xx
+
+ This class of status code indicates that the client's request was
+ successfully received, understood, and accepted.
+
+10.2.1 200 OK
+
+ The request has succeeded. The information returned with the response
+ is dependent on the method used in the request, for example:
+
+ GET an entity corresponding to the requested resource is sent in
+ the response;
+
+ HEAD the entity-header fields corresponding to the requested
+ resource are sent in the response without any message-body;
+
+ POST an entity describing or containing the result of the action;
+
+
+
+
+Fielding, et al. Standards Track [Page 58]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ TRACE an entity containing the request message as received by the
+ end server.
+
+10.2.2 201 Created
+
+ The request has been fulfilled and resulted in a new resource being
+ created. The newly created resource can be referenced by the URI(s)
+ returned in the entity of the response, with the most specific URI
+ for the resource given by a Location header field. The response
+ SHOULD include an entity containing a list of resource
+ characteristics and location(s) from which the user or user agent can
+ choose the one most appropriate. The entity format is specified by
+ the media type given in the Content-Type header field. The origin
+ server MUST create the resource before returning the 201 status code.
+ If the action cannot be carried out immediately, the server SHOULD
+ respond with 202 (Accepted) response instead.
+
+ A 201 response MAY contain an ETag response header field indicating
+ the current value of the entity tag for the requested variant just
+ created, see section 14.19.
+
+10.2.3 202 Accepted
+
+ The request has been accepted for processing, but the processing has
+ not been completed. The request might or might not eventually be
+ acted upon, as it might be disallowed when processing actually takes
+ place. There is no facility for re-sending a status code from an
+ asynchronous operation such as this.
+
+ The 202 response is intentionally non-committal. Its purpose is to
+ allow a server to accept a request for some other process (perhaps a
+ batch-oriented process that is only run once per day) without
+ requiring that the user agent's connection to the server persist
+ until the process is completed. The entity returned with this
+ response SHOULD include an indication of the request's current status
+ and either a pointer to a status monitor or some estimate of when the
+ user can expect the request to be fulfilled.
+
+10.2.4 203 Non-Authoritative Information
+
+ The returned metainformation in the entity-header is not the
+ definitive set as available from the origin server, but is gathered
+ from a local or a third-party copy. The set presented MAY be a subset
+ or superset of the original version. For example, including local
+ annotation information about the resource might result in a superset
+ of the metainformation known by the origin server. Use of this
+ response code is not required and is only appropriate when the
+ response would otherwise be 200 (OK).
+
+
+
+Fielding, et al. Standards Track [Page 59]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.2.5 204 No Content
+
+ The server has fulfilled the request but does not need to return an
+ entity-body, and might want to return updated metainformation. The
+ response MAY include new or updated metainformation in the form of
+ entity-headers, which if present SHOULD be associated with the
+ requested variant.
+
+ If the client is a user agent, it SHOULD NOT change its document view
+ from that which caused the request to be sent. This response is
+ primarily intended to allow input for actions to take place without
+ causing a change to the user agent's active document view, although
+ any new or updated metainformation SHOULD be applied to the document
+ currently in the user agent's active view.
+
+ The 204 response MUST NOT include a message-body, and thus is always
+ terminated by the first empty line after the header fields.
+
+10.2.6 205 Reset Content
+
+ The server has fulfilled the request and the user agent SHOULD reset
+ the document view which caused the request to be sent. This response
+ is primarily intended to allow input for actions to take place via
+ user input, followed by a clearing of the form in which the input is
+ given so that the user can easily initiate another input action. The
+ response MUST NOT include an entity.
+
+10.2.7 206 Partial Content
+
+ The server has fulfilled the partial GET request for the resource.
+ The request MUST have included a Range header field (section 14.35)
+ indicating the desired range, and MAY have included an If-Range
+ header field (section 14.27) to make the request conditional.
+
+ The response MUST include the following header fields:
+
+ - Either a Content-Range header field (section 14.16) indicating
+ the range included with this response, or a multipart/byteranges
+ Content-Type including Content-Range fields for each part. If a
+ Content-Length header field is present in the response, its
+ value MUST match the actual number of OCTETs transmitted in the
+ message-body.
+
+ - Date
+
+ - ETag and/or Content-Location, if the header would have been sent
+ in a 200 response to the same request
+
+
+
+
+Fielding, et al. Standards Track [Page 60]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - Expires, Cache-Control, and/or Vary, if the field-value might
+ differ from that sent in any previous response for the same
+ variant
+
+ If the 206 response is the result of an If-Range request that used a
+ strong cache validator (see section 13.3.3), the response SHOULD NOT
+ include other entity-headers. If the response is the result of an
+ If-Range request that used a weak validator, the response MUST NOT
+ include other entity-headers; this prevents inconsistencies between
+ cached entity-bodies and updated headers. Otherwise, the response
+ MUST include all of the entity-headers that would have been returned
+ with a 200 (OK) response to the same request.
+
+[[ Should be: ]]
+[[ If the 206 response is the result of an If-Range request, the ]]
+[[ response SHOULD NOT include other entity-headers. Otherwise, the ]]
+[[ response MUST include all of the entity-headers that would have ]]
+[[ been returned with a 200 (OK) response to the same request. ]]
+
+ A cache MUST NOT combine a 206 response with other previously cached
+ content if the ETag or Last-Modified headers do not match exactly,
+ see 13.5.4.
+
+ A cache that does not support the Range and Content-Range headers
+ MUST NOT cache 206 (Partial) responses.
+
+10.3 Redirection 3xx
+
+ This class of status code indicates that further action needs to be
+ taken by the user agent in order to fulfill the request. The action
+ required MAY be carried out by the user agent without interaction
+ with the user if and only if the method used in the second request is
+ GET or HEAD. A client SHOULD detect infinite redirection loops, since
+ such loops generate network traffic for each redirection.
+
+ Note: previous versions of this specification recommended a
+ maximum of five redirections. Content developers should be aware
+ that there might be clients that implement such a fixed
+ limitation.
+
+10.3.1 300 Multiple Choices
+
+ The requested resource corresponds to any one of a set of
+ representations, each with its own specific location, and agent-
+ driven negotiation information (section 12) is being provided so that
+ the user (or user agent) can select a preferred representation and
+ redirect its request to that location.
+
+ Unless it was a HEAD request, the response SHOULD include an entity
+ containing a list of resource characteristics and location(s) from
+ which the user or user agent can choose the one most appropriate. The
+ entity format is specified by the media type given in the Content-
+ Type header field. Depending upon the format and the capabilities of
+
+
+
+
+Fielding, et al. Standards Track [Page 61]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ the user agent, selection of the most appropriate choice MAY be
+ performed automatically. However, this specification does not define
+ any standard for such automatic selection.
+
+ If the server has a preferred choice of representation, it SHOULD
+ include the specific URI for that representation in the Location
+ field; user agents MAY use the Location field value for automatic
+ redirection. This response is cacheable unless indicated otherwise.
+
+10.3.2 301 Moved Permanently
+
+ The requested resource has been assigned a new permanent URI and any
+ future references to this resource SHOULD use one of the returned
+ URIs. Clients with link editing capabilities ought to automatically
+ re-link references to the Request-URI to one or more of the new
+ references returned by the server, where possible. This response is
+ cacheable unless indicated otherwise.
+
+ The new permanent URI SHOULD be given by the Location field in the
+ response. Unless the request method was HEAD, the entity of the
+ response SHOULD contain a short hypertext note with a hyperlink to
+ the new URI(s).
+
+ If the 301 status code is received in response to a request other
+ than GET or HEAD, the user agent MUST NOT automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+[[ Should be: ]]
+[[ If the 301 status code is received in response to a request method ]]
+[[ that is known to be "safe", as defined in section 9.1.1, then the ]]
+[[ request MAY be automatically redirected by the user agent without ]]
+[[ confirmation. Otherwise, the user agent MUST NOT automatically ]]
+[[ redirect the request unless it is confirmed by the user, since the ]]
+[[ new URI might change the conditions under which the request was ]]
+[[ issued. ]]
+
+ Note: When automatically redirecting a POST request after
+ receiving a 301 status code, some existing HTTP/1.0 user agents
+ will erroneously change it into a GET request.
+
+10.3.3 302 Found
+
+ The requested resource resides temporarily under a different URI.
+ Since the redirection might be altered on occasion, the client SHOULD
+ continue to use the Request-URI for future requests. This response
+ is only cacheable if indicated by a Cache-Control or Expires header
+ field.
+
+ The temporary URI SHOULD be given by the Location field in the
+ response. Unless the request method was HEAD, the entity of the
+ response SHOULD contain a short hypertext note with a hyperlink to
+ the new URI(s).
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 62]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the 302 status code is received in response to a request other
+ than GET or HEAD, the user agent MUST NOT automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+ [[ See errata to 10.3.3 ]]
+
+ Note: RFC 1945 and RFC 2068 specify that the client is not allowed
+ to change the method on the redirected request. However, most
+ existing user agent implementations treat 302 as if it were a 303
+ response, performing a GET on the Location field-value regardless
+ of the original request method. The status codes 303 and 307 have
+ been added for servers that wish to make unambiguously clear which
+ kind of reaction is expected of the client.
+
+10.3.4 303 See Other
+
+ The response to the request can be found under a different URI and
+ SHOULD be retrieved using a GET method on that resource. This method
+ exists primarily to allow the output of a POST-activated script to
+ redirect the user agent to a selected resource. The new URI is not a
+ substitute reference for the originally requested resource. The 303
+ response MUST NOT be cached, but the response to the second
+ (redirected) request might be cacheable.
+
+ The different URI SHOULD be given by the Location field in the
+ response. Unless the request method was HEAD, the entity of the
+ response SHOULD contain a short hypertext note with a hyperlink to
+ the new URI(s).
+
+ Note: Many pre-HTTP/1.1 user agents do not understand the 303
+ status. When interoperability with such clients is a concern, the
+ 302 status code may be used instead, since most user agents react
+ to a 302 response as described here for 303.
+
+10.3.5 304 Not Modified
+
+ If the client has performed a conditional GET request and access is
+ allowed, but the document has not been modified, the server SHOULD
+ respond with this status code. The 304 response MUST NOT contain a
+ message-body, and thus is always terminated by the first empty line
+ after the header fields.
+
+ The response MUST include the following header fields:
+
+ - Date, unless its omission is required by section 14.18.1
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 63]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If a clockless origin server obeys these rules, and proxies and
+ clients add their own Date to any response received without one (as
+ already specified by [RFC 2068], section 14.19), caches will operate
+ correctly.
+
+ - ETag and/or Content-Location, if the header would have been sent
+ in a 200 response to the same request
+
+ - Expires, Cache-Control, and/or Vary, if the field-value might
+ differ from that sent in any previous response for the same
+ variant
+
+ If the conditional GET used a strong cache validator (see section
+ 13.3.3), the response SHOULD NOT include other entity-headers.
+ Otherwise (i.e., the conditional GET used a weak validator), the
+ response MUST NOT include other entity-headers; this prevents
+ inconsistencies between cached entity-bodies and updated headers.
+
+ If a 304 response indicates an entity not currently cached, then the
+ cache MUST disregard the response and repeat the request without the
+ conditional.
+
+ If a cache uses a received 304 response to update a cache entry, the
+ cache MUST update the entry to reflect any new field values given in
+ the response.
+
+10.3.6 305 Use Proxy
+
+ The requested resource MUST be accessed through the proxy given by
+ the Location field. The Location field gives the URI of the proxy.
+ The recipient is expected to repeat this single request via the
+ proxy. 305 responses MUST only be generated by origin servers.
+
+ Note: RFC 2068 was not clear that 305 was intended to redirect a
+ single request, and to be generated by origin servers only. Not
+ observing these limitations has significant security consequences.
+
+10.3.7 306 (Unused)
+
+ The 306 status code was used in a previous version of the
+ specification, is no longer used, and the code is reserved.
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 64]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.3.8 307 Temporary Redirect
+
+ The requested resource resides temporarily under a different URI.
+ Since the redirection MAY be altered on occasion, the client SHOULD
+ continue to use the Request-URI for future requests. This response
+ is only cacheable if indicated by a Cache-Control or Expires header
+ field.
+
+ The temporary URI SHOULD be given by the Location field in the
+ response. Unless the request method was HEAD, the entity of the
+ response SHOULD contain a short hypertext note with a hyperlink to
+ the new URI(s) , since many pre-HTTP/1.1 user agents do not
+ understand the 307 status. Therefore, the note SHOULD contain the
+ information necessary for a user to repeat the original request on
+ the new URI.
+
+ If the 307 status code is received in response to a request other
+ than GET or HEAD, the user agent MUST NOT automatically redirect the
+ request unless it can be confirmed by the user, since this might
+ change the conditions under which the request was issued.
+
+ [[ See errata to 10.3.3 ]]
+
+10.4 Client Error 4xx
+
+ The 4xx class of status code is intended for cases in which the
+ client seems to have erred. Except when responding to a HEAD request,
+ the server SHOULD include an entity containing an explanation of the
+ error situation, and whether it is a temporary or permanent
+ condition. These status codes are applicable to any request method.
+ User agents SHOULD display any included entity to the user.
+
+ If the client is sending data, a server implementation using TCP
+ SHOULD be careful to ensure that the client acknowledges receipt of
+ the packet(s) containing the response, before the server closes the
+ input connection. If the client continues sending data to the server
+ after the close, the server's TCP stack will send a reset packet to
+ the client, which may erase the client's unacknowledged input buffers
+ before they can be read and interpreted by the HTTP application.
+
+10.4.1 400 Bad Request
+
+ The request could not be understood by the server due to malformed
+ syntax. The client SHOULD NOT repeat the request without
+ modifications.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 65]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.4.2 401 Unauthorized
+
+ The request requires user authentication. The response MUST include a
+ WWW-Authenticate header field (section 14.47) containing a challenge
+ applicable to the requested resource. The client MAY repeat the
+ request with a suitable Authorization header field (section 14.8). If
+ the request already included Authorization credentials, then the 401
+ response indicates that authorization has been refused for those
+ credentials. If the 401 response contains the same challenge as the
+ prior response, and the user agent has already attempted
+ authentication at least once, then the user SHOULD be presented the
+ entity that was given in the response, since that entity might
+ include relevant diagnostic information. HTTP access authentication
+ is explained in "HTTP Authentication: Basic and Digest Access
+ Authentication" [43].
+
+10.4.3 402 Payment Required
+
+ This code is reserved for future use.
+
+10.4.4 403 Forbidden
+
+ The server understood the request, but is refusing to fulfill it.
+ Authorization will not help and the request SHOULD NOT be repeated.
+ If the request method was not HEAD and the server wishes to make
+ public why the request has not been fulfilled, it SHOULD describe the
+ reason for the refusal in the entity. If the server does not wish to
+ make this information available to the client, the status code 404
+ (Not Found) can be used instead.
+
+10.4.5 404 Not Found
+
+ The server has not found anything matching the Request-URI. No
+ indication is given of whether the condition is temporary or
+ permanent. The 410 (Gone) status code SHOULD be used if the server
+ knows, through some internally configurable mechanism, that an old
+ resource is permanently unavailable and has no forwarding address.
+ This status code is commonly used when the server does not wish to
+ reveal exactly why the request has been refused, or when no other
+ response is applicable.
+
+10.4.6 405 Method Not Allowed
+
+ The method specified in the Request-Line is not allowed for the
+ resource identified by the Request-URI. The response MUST include an
+ Allow header containing a list of valid methods for the requested
+ resource.
+
+
+
+
+Fielding, et al. Standards Track [Page 66]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.4.7 406 Not Acceptable
+
+ The resource identified by the request is only capable of generating
+ response entities which have content characteristics not acceptable
+ according to the accept headers sent in the request.
+
+ Unless it was a HEAD request, the response SHOULD include an entity
+ containing a list of available entity characteristics and location(s)
+ from which the user or user agent can choose the one most
+ appropriate. The entity format is specified by the media type given
+ in the Content-Type header field. Depending upon the format and the
+ capabilities of the user agent, selection of the most appropriate
+ choice MAY be performed automatically. However, this specification
+ does not define any standard for such automatic selection.
+
+ Note: HTTP/1.1 servers are allowed to return responses which are
+ not acceptable according to the accept headers sent in the
+ request. In some cases, this may even be preferable to sending a
+ 406 response. User agents are encouraged to inspect the headers of
+ an incoming response to determine if it is acceptable.
+
+ If the response could be unacceptable, a user agent SHOULD
+ temporarily stop receipt of more data and query the user for a
+ decision on further actions.
+
+10.4.8 407 Proxy Authentication Required
+
+ This code is similar to 401 (Unauthorized), but indicates that the
+ client must first authenticate itself with the proxy. The proxy MUST
+ return a Proxy-Authenticate header field (section 14.33) containing a
+ challenge applicable to the proxy for the requested resource. The
+ client MAY repeat the request with a suitable Proxy-Authorization
+ header field (section 14.34). HTTP access authentication is explained
+ in "HTTP Authentication: Basic and Digest Access Authentication"
+ [43].
+
+10.4.9 408 Request Timeout
+
+ The client did not produce a request within the time that the server
+ was prepared to wait. The client MAY repeat the request without
+ modifications at any later time.
+
+10.4.10 409 Conflict
+
+ The request could not be completed due to a conflict with the current
+ state of the resource. This code is only allowed in situations where
+ it is expected that the user might be able to resolve the conflict
+ and resubmit the request. The response body SHOULD include enough
+
+
+
+Fielding, et al. Standards Track [Page 67]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ information for the user to recognize the source of the conflict.
+ Ideally, the response entity would include enough information for the
+ user or user agent to fix the problem; however, that might not be
+ possible and is not required.
+
+ Conflicts are most likely to occur in response to a PUT request. For
+ example, if versioning were being used and the entity being PUT
+ included changes to a resource which conflict with those made by an
+ earlier (third-party) request, the server might use the 409 response
+ to indicate that it can't complete the request. In this case, the
+ response entity would likely contain a list of the differences
+ between the two versions in a format defined by the response
+ Content-Type.
+
+10.4.11 410 Gone
+
+ The requested resource is no longer available at the server and no
+ forwarding address is known. This condition is expected to be
+ considered permanent. Clients with link editing capabilities SHOULD
+ delete references to the Request-URI after user approval. If the
+ server does not know, or has no facility to determine, whether or not
+ the condition is permanent, the status code 404 (Not Found) SHOULD be
+ used instead. This response is cacheable unless indicated otherwise.
+
+ The 410 response is primarily intended to assist the task of web
+ maintenance by notifying the recipient that the resource is
+ intentionally unavailable and that the server owners desire that
+ remote links to that resource be removed. Such an event is common for
+ limited-time, promotional services and for resources belonging to
+ individuals no longer working at the server's site. It is not
+ necessary to mark all permanently unavailable resources as "gone" or
+ to keep the mark for any length of time -- that is left to the
+ discretion of the server owner.
+
+10.4.12 411 Length Required
+
+ The server refuses to accept the request without a defined Content-
+ Length. The client MAY repeat the request if it adds a valid
+ Content-Length header field containing the length of the message-body
+ in the request message.
+
+10.4.13 412 Precondition Failed
+
+ The precondition given in one or more of the request-header fields
+ evaluated to false when it was tested on the server. This response
+ code allows the client to place preconditions on the current resource
+ metainformation (header field data) and thus prevent the requested
+ method from being applied to a resource other than the one intended.
+
+
+
+Fielding, et al. Standards Track [Page 68]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.4.14 413 Request Entity Too Large
+
+ The server is refusing to process a request because the request
+ entity is larger than the server is willing or able to process. The
+ server MAY close the connection to prevent the client from continuing
+ the request.
+
+ If the condition is temporary, the server SHOULD include a Retry-
+ After header field to indicate that it is temporary and after what
+ time the client MAY try again.
+
+10.4.15 414 Request-URI Too Long
+
+ The server is refusing to service the request because the Request-URI
+ is longer than the server is willing to interpret. This rare
+ condition is only likely to occur when a client has improperly
+ converted a POST request to a GET request with long query
+ information, when the client has descended into a URI "black hole" of
+ redirection (e.g., a redirected URI prefix that points to a suffix of
+ itself), or when the server is under attack by a client attempting to
+ exploit security holes present in some servers using fixed-length
+ buffers for reading or manipulating the Request-URI.
+
+10.4.16 415 Unsupported Media Type
+
+ The server is refusing to service the request because the entity of
+ the request is in a format not supported by the requested resource
+ for the requested method.
+
+10.4.17 416 Requested Range Not Satisfiable
+
+ A server SHOULD return a response with this status code if a request
+ included a Range request-header field (section 14.35), and none of
+ the range-specifier values in this field overlap the current extent
+ of the selected resource, and the request did not include an If-Range
+ request-header field. (For byte-ranges, this means that the first-
+ byte-pos of all of the byte-range-spec values were greater than the
+ current length of the selected resource.)
+
+ When this status code is returned for a byte-range request, the
+ response SHOULD include a Content-Range entity-header field
+ specifying the current length of the selected resource (see section
+ 14.16). This response MUST NOT use the multipart/byteranges content-
+ type.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 69]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.4.18 417 Expectation Failed
+
+ The expectation given in an Expect request-header field (see section
+ 14.20) could not be met by this server, or, if the server is a proxy,
+ the server has unambiguous evidence that the request could not be met
+ by the next-hop server.
+
+10.5 Server Error 5xx
+
+ Response status codes beginning with the digit "5" indicate cases in
+ which the server is aware that it has erred or is incapable of
+ performing the request. Except when responding to a HEAD request, the
+ server SHOULD include an entity containing an explanation of the
+ error situation, and whether it is a temporary or permanent
+ condition. User agents SHOULD display any included entity to the
+ user. These response codes are applicable to any request method.
+
+10.5.1 500 Internal Server Error
+
+ The server encountered an unexpected condition which prevented it
+ from fulfilling the request.
+
+10.5.2 501 Not Implemented
+
+ The server does not support the functionality required to fulfill the
+ request. This is the appropriate response when the server does not
+ recognize the request method and is not capable of supporting it for
+ any resource.
+
+10.5.3 502 Bad Gateway
+
+ The server, while acting as a gateway or proxy, received an invalid
+ response from the upstream server it accessed in attempting to
+ fulfill the request.
+
+10.5.4 503 Service Unavailable
+
+ The server is currently unable to handle the request due to a
+ temporary overloading or maintenance of the server. The implication
+ is that this is a temporary condition which will be alleviated after
+ some delay. If known, the length of the delay MAY be indicated in a
+ Retry-After header. If no Retry-After is given, the client SHOULD
+ handle the response as it would for a 500 response.
+
+ Note: The existence of the 503 status code does not imply that a
+ server must use it when becoming overloaded. Some servers may wish
+ to simply refuse the connection.
+
+
+
+
+Fielding, et al. Standards Track [Page 70]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+10.5.5 504 Gateway Timeout
+
+ The server, while acting as a gateway or proxy, did not receive a
+ timely response from the upstream server specified by the URI (e.g.
+ HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed
+ to access in attempting to complete the request.
+
+ Note: Note to implementors: some deployed proxies are known to
+ return 400 or 500 when DNS lookups time out.
+
+10.5.6 505 HTTP Version Not Supported
+
+ The server does not support, or refuses to support, the HTTP protocol
+ version that was used in the request message. The server is
+ indicating that it is unable or unwilling to complete the request
+ using the same major version as the client, as described in section
+ 3.1, other than with this error message. The response SHOULD contain
+ an entity describing why that version is not supported and what other
+ protocols are supported by that server.
+
+11 Access Authentication
+
+ HTTP provides several OPTIONAL challenge-response authentication
+ mechanisms which can be used by a server to challenge a client
+ request and by a client to provide authentication information. The
+ general framework for access authentication, and the specification of
+ "basic" and "digest" authentication, are specified in "HTTP
+ Authentication: Basic and Digest Access Authentication" [43]. This
+ specification adopts the definitions of "challenge" and "credentials"
+ from that specification.
+
+12 Content Negotiation
+
+ Most HTTP responses include an entity which contains information for
+ interpretation by a human user. Naturally, it is desirable to supply
+ the user with the "best available" entity corresponding to the
+ request. Unfortunately for servers and caches, not all users have the
+ same preferences for what is "best," and not all user agents are
+ equally capable of rendering all entity types. For that reason, HTTP
+ has provisions for several mechanisms for "content negotiation" --
+ the process of selecting the best representation for a given response
+ when there are multiple representations available.
+
+ Note: This is not called "format negotiation" because the
+ alternate representations may be of the same media type, but use
+ different capabilities of that type, be in different languages,
+ etc.
+
+
+
+
+Fielding, et al. Standards Track [Page 71]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Any response containing an entity-body MAY be subject to negotiation,
+ including error responses.
+
+ There are two kinds of content negotiation which are possible in
+ HTTP: server-driven and agent-driven negotiation. These two kinds of
+ negotiation are orthogonal and thus may be used separately or in
+ combination. One method of combination, referred to as transparent
+ negotiation, occurs when a cache uses the agent-driven negotiation
+ information provided by the origin server in order to provide
+ server-driven negotiation for subsequent requests.
+
+12.1 Server-driven Negotiation
+
+ If the selection of the best representation for a response is made by
+ an algorithm located at the server, it is called server-driven
+ negotiation. Selection is based on the available representations of
+ the response (the dimensions over which it can vary; e.g. language,
+ content-coding, etc.) and the contents of particular header fields in
+ the request message or on other information pertaining to the request
+ (such as the network address of the client).
+
+ Server-driven negotiation is advantageous when the algorithm for
+ selecting from among the available representations is difficult to
+ describe to the user agent, or when the server desires to send its
+ "best guess" to the client along with the first response (hoping to
+ avoid the round-trip delay of a subsequent request if the "best
+ guess" is good enough for the user). In order to improve the server's
+ guess, the user agent MAY include request header fields (Accept,
+ Accept-Language, Accept-Encoding, etc.) which describe its
+ preferences for such a response.
+
+ Server-driven negotiation has disadvantages:
+
+ 1. It is impossible for the server to accurately determine what
+ might be "best" for any given user, since that would require
+ complete knowledge of both the capabilities of the user agent
+ and the intended use for the response (e.g., does the user want
+ to view it on screen or print it on paper?).
+
+ 2. Having the user agent describe its capabilities in every
+ request can be both very inefficient (given that only a small
+ percentage of responses have multiple representations) and a
+ potential violation of the user's privacy.
+
+ 3. It complicates the implementation of an origin server and the
+ algorithms for generating responses to a request.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 72]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 4. It may limit a public cache's ability to use the same response
+ for multiple user's requests.
+
+ HTTP/1.1 includes the following request-header fields for enabling
+ server-driven negotiation through description of user agent
+ capabilities and user preferences: Accept (section 14.1), Accept-
+ Charset (section 14.2), Accept-Encoding (section 14.3), Accept-
+ Language (section 14.4), and User-Agent (section 14.43). However, an
+ origin server is not limited to these dimensions and MAY vary the
+ response based on any aspect of the request, including information
+ outside the request-header fields or within extension header fields
+ not defined by this specification.
+
+ The Vary header field can be used to express the parameters the
+ server uses to select a representation that is subject to server-
+ driven negotiation. See section 13.6 for use of the Vary header field
+ by caches and section 14.44 for use of the Vary header field by
+ servers.
+
+12.2 Agent-driven Negotiation
+
+ With agent-driven negotiation, selection of the best representation
+ for a response is performed by the user agent after receiving an
+ initial response from the origin server. Selection is based on a list
+ of the available representations of the response included within the
+ header fields or entity-body of the initial response, with each
+ representation identified by its own URI. Selection from among the
+ representations may be performed automatically (if the user agent is
+ capable of doing so) or manually by the user selecting from a
+ generated (possibly hypertext) menu.
+
+ Agent-driven negotiation is advantageous when the response would vary
+ over commonly-used dimensions (such as type, language, or encoding),
+ when the origin server is unable to determine a user agent's
+ capabilities from examining the request, and generally when public
+ caches are used to distribute server load and reduce network usage.
+
+ Agent-driven negotiation suffers from the disadvantage of needing a
+ second request to obtain the best alternate representation. This
+ second request is only efficient when caching is used. In addition,
+ this specification does not define any mechanism for supporting
+ automatic selection, though it also does not prevent any such
+ mechanism from being developed as an extension and used within
+ HTTP/1.1.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 73]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ HTTP/1.1 defines the 300 (Multiple Choices) and 406 (Not Acceptable)
+ status codes for enabling agent-driven negotiation when the server is
+ unwilling or unable to provide a varying response using server-driven
+ negotiation.
+
+12.3 Transparent Negotiation
+
+ Transparent negotiation is a combination of both server-driven and
+ agent-driven negotiation. When a cache is supplied with a form of the
+ list of available representations of the response (as in agent-driven
+ negotiation) and the dimensions of variance are completely understood
+ by the cache, then the cache becomes capable of performing server-
+ driven negotiation on behalf of the origin server for subsequent
+ requests on that resource.
+
+ Transparent negotiation has the advantage of distributing the
+ negotiation work that would otherwise be required of the origin
+ server and also removing the second request delay of agent-driven
+ negotiation when the cache is able to correctly guess the right
+ response.
+
+ This specification does not define any mechanism for transparent
+ negotiation, though it also does not prevent any such mechanism from
+ being developed as an extension that could be used within HTTP/1.1.
+
+13 Caching in HTTP
+
+ HTTP is typically used for distributed information systems, where
+ performance can be improved by the use of response caches. The
+ HTTP/1.1 protocol includes a number of elements intended to make
+ caching work as well as possible. Because these elements are
+ inextricable from other aspects of the protocol, and because they
+ interact with each other, it is useful to describe the basic caching
+ design of HTTP separately from the detailed descriptions of methods,
+ headers, response codes, etc.
+
+ Caching would be useless if it did not significantly improve
+ performance. The goal of caching in HTTP/1.1 is to eliminate the need
+ to send requests in many cases, and to eliminate the need to send
+ full responses in many other cases. The former reduces the number of
+ network round-trips required for many operations; we use an
+ "expiration" mechanism for this purpose (see section 13.2). The
+ latter reduces network bandwidth requirements; we use a "validation"
+ mechanism for this purpose (see section 13.3).
+
+ Requirements for performance, availability, and disconnected
+ operation require us to be able to relax the goal of semantic
+ transparency. The HTTP/1.1 protocol allows origin servers, caches,
+
+
+
+Fielding, et al. Standards Track [Page 74]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ and clients to explicitly reduce transparency when necessary.
+ However, because non-transparent operation may confuse non-expert
+ users, and might be incompatible with certain server applications
+ (such as those for ordering merchandise), the protocol requires that
+ transparency be relaxed
+
+ - only by an explicit protocol-level request when relaxed by
+ client or origin server
+
+ - only with an explicit warning to the end user when relaxed by
+ cache or client
+
+ Therefore, the HTTP/1.1 protocol provides these important elements:
+
+ 1. Protocol features that provide full semantic transparency when
+ this is required by all parties.
+
+ 2. Protocol features that allow an origin server or user agent to
+ explicitly request and control non-transparent operation.
+
+ 3. Protocol features that allow a cache to attach warnings to
+ responses that do not preserve the requested approximation of
+ semantic transparency.
+
+ A basic principle is that it must be possible for the clients to
+ detect any potential relaxation of semantic transparency.
+
+ Note: The server, cache, or client implementor might be faced with
+ design decisions not explicitly discussed in this specification.
+ If a decision might affect semantic transparency, the implementor
+ ought to err on the side of maintaining transparency unless a
+ careful and complete analysis shows significant benefits in
+ breaking transparency.
+
+13.1.1 Cache Correctness
+
+ A correct cache MUST respond to a request with the most up-to-date
+ response held by the cache that is appropriate to the request (see
+ sections 13.2.5, 13.2.6, and 13.12) which meets one of the following
+ conditions:
+
+ 1. It has been checked for equivalence with what the origin server
+ would have returned by revalidating the response with the
+ origin server (section 13.3);
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 75]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 2. It is "fresh enough" (see section 13.2). In the default case,
+ this means it meets the least restrictive freshness requirement
+ of the client, origin server, and cache (see section 14.9); if
+ the origin server so specifies, it is the freshness requirement
+ of the origin server alone.
+
+ If a stored response is not "fresh enough" by the most
+ restrictive freshness requirement of both the client and the
+ origin server, in carefully considered circumstances the cache
+ MAY still return the response with the appropriate Warning
+ header (see section 13.1.5 and 14.46), unless such a response
+ is prohibited (e.g., by a "no-store" cache-directive, or by a
+ "no-cache" cache-request-directive; see section 14.9).
+
+ 3. It is an appropriate 304 (Not Modified), 305 (Proxy Redirect),
+ or error (4xx or 5xx) response message.
+
+ If the cache can not communicate with the origin server, then a
+ correct cache SHOULD respond as above if the response can be
+ correctly served from the cache; if not it MUST return an error or
+ warning indicating that there was a communication failure.
+
+ If a cache receives a response (either an entire response, or a 304
+ (Not Modified) response) that it would normally forward to the
+ requesting client, and the received response is no longer fresh, the
+ cache SHOULD forward it to the requesting client without adding a new
+ Warning (but without removing any existing Warning headers). A cache
+ SHOULD NOT attempt to revalidate a response simply because that
+ response became stale in transit; this might lead to an infinite
+ loop. A user agent that receives a stale response without a Warning
+ MAY display a warning indication to the user.
+
+13.1.2 Warnings
+
+ Whenever a cache returns a response that is neither first-hand nor
+ "fresh enough" (in the sense of condition 2 in section 13.1.1), it
+ MUST attach a warning to that effect, using a Warning general-header.
+ The Warning header and the currently defined warnings are described
+ in section 14.46. The warning allows clients to take appropriate
+ action.
+
+ Warnings MAY be used for other purposes, both cache-related and
+ otherwise. The use of a warning, rather than an error status code,
+ distinguish these responses from true failures.
+
+ Warnings are assigned three digit warn-codes. The first digit
+ indicates whether the Warning MUST or MUST NOT be deleted from a
+ stored cache entry after a successful revalidation:
+
+
+
+Fielding, et al. Standards Track [Page 76]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 1xx Warnings that describe the freshness or revalidation status of
+ the response, and so MUST be deleted after a successful
+ revalidation. 1XX warn-codes MAY be generated by a cache only when
+ validating a cached entry. It MUST NOT be generated by clients.
+
+ 2xx Warnings that describe some aspect of the entity body or entity
+ headers that is not rectified by a revalidation (for example, a
+ lossy compression of the entity bodies) and which MUST NOT be
+ deleted after a successful revalidation.
+
+ See section 14.46 for the definitions of the codes themselves.
+
+ HTTP/1.0 caches will cache all Warnings in responses, without
+ deleting the ones in the first category. Warnings in responses that
+ are passed to HTTP/1.0 caches carry an extra warning-date field,
+ which prevents a future HTTP/1.1 recipient from believing an
+ erroneously cached Warning.
+
+ Warnings also carry a warning text. The text MAY be in any
+ appropriate natural language (perhaps based on the client's Accept
+ headers), and include an OPTIONAL indication of what character set is
+ used.
+
+ Multiple warnings MAY be attached to a response (either by the origin
+ server or by a cache), including multiple warnings with the same code
+ number. For example, a server might provide the same warning with
+ texts in both English and Basque.
+
+ When multiple warnings are attached to a response, it might not be
+ practical or reasonable to display all of them to the user. This
+ version of HTTP does not specify strict priority rules for deciding
+ which warnings to display and in what order, but does suggest some
+ heuristics.
+
+13.1.3 Cache-control Mechanisms
+
+ The basic cache mechanisms in HTTP/1.1 (server-specified expiration
+ times and validators) are implicit directives to caches. In some
+ cases, a server or client might need to provide explicit directives
+ to the HTTP caches. We use the Cache-Control header for this purpose.
+
+ The Cache-Control header allows a client or server to transmit a
+ variety of directives in either requests or responses. These
+ directives typically override the default caching algorithms. As a
+ general rule, if there is any apparent conflict between header
+ values, the most restrictive interpretation is applied (that is, the
+ one that is most likely to preserve semantic transparency). However,
+
+
+
+
+Fielding, et al. Standards Track [Page 77]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ in some cases, cache-control directives are explicitly specified as
+ weakening the approximation of semantic transparency (for example,
+ "max-stale" or "public").
+
+ The cache-control directives are described in detail in section 14.9.
+
+13.1.4 Explicit User Agent Warnings
+
+ Many user agents make it possible for users to override the basic
+ caching mechanisms. For example, the user agent might allow the user
+ to specify that cached entities (even explicitly stale ones) are
+ never validated. Or the user agent might habitually add "Cache-
+ Control: max-stale=3600" to every request. The user agent SHOULD NOT
+ default to either non-transparent behavior, or behavior that results
+ in abnormally ineffective caching, but MAY be explicitly configured
+ to do so by an explicit action of the user.
+
+ If the user has overridden the basic caching mechanisms, the user
+ agent SHOULD explicitly indicate to the user whenever this results in
+ the display of information that might not meet the server's
+ transparency requirements (in particular, if the displayed entity is
+ known to be stale). Since the protocol normally allows the user agent
+ to determine if responses are stale or not, this indication need only
+ be displayed when this actually happens. The indication need not be a
+ dialog box; it could be an icon (for example, a picture of a rotting
+ fish) or some other indicator.
+
+ If the user has overridden the caching mechanisms in a way that would
+ abnormally reduce the effectiveness of caches, the user agent SHOULD
+ continually indicate this state to the user (for example, by a
+ display of a picture of currency in flames) so that the user does not
+ inadvertently consume excess resources or suffer from excessive
+ latency.
+
+13.1.5 Exceptions to the Rules and Warnings
+
+ In some cases, the operator of a cache MAY choose to configure it to
+ return stale responses even when not requested by clients. This
+ decision ought not be made lightly, but may be necessary for reasons
+ of availability or performance, especially when the cache is poorly
+ connected to the origin server. Whenever a cache returns a stale
+ response, it MUST mark it as such (using a Warning header) enabling
+ the client software to alert the user that there might be a potential
+ problem.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 78]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ It also allows the user agent to take steps to obtain a first-hand or
+ fresh response. For this reason, a cache SHOULD NOT return a stale
+ response if the client explicitly requests a first-hand or fresh one,
+ unless it is impossible to comply for technical or policy reasons.
+
+13.1.6 Client-controlled Behavior
+
+ While the origin server (and to a lesser extent, intermediate caches,
+ by their contribution to the age of a response) are the primary
+ source of expiration information, in some cases the client might need
+ to control a cache's decision about whether to return a cached
+ response without validating it. Clients do this using several
+ directives of the Cache-Control header.
+
+ A client's request MAY specify the maximum age it is willing to
+ accept of an unvalidated response; specifying a value of zero forces
+ the cache(s) to revalidate all responses. A client MAY also specify
+ the minimum time remaining before a response expires. Both of these
+ options increase constraints on the behavior of caches, and so cannot
+ further relax the cache's approximation of semantic transparency.
+
+ A client MAY also specify that it will accept stale responses, up to
+ some maximum amount of staleness. This loosens the constraints on the
+ caches, and so might violate the origin server's specified
+ constraints on semantic transparency, but might be necessary to
+ support disconnected operation, or high availability in the face of
+ poor connectivity.
+
+13.2 Expiration Model
+
+13.2.1 Server-Specified Expiration
+
+ HTTP caching works best when caches can entirely avoid making
+ requests to the origin server. The primary mechanism for avoiding
+ requests is for an origin server to provide an explicit expiration
+ time in the future, indicating that a response MAY be used to satisfy
+ subsequent requests. In other words, a cache can return a fresh
+ response without first contacting the server.
+
+ Our expectation is that servers will assign future explicit
+ expiration times to responses in the belief that the entity is not
+ likely to change, in a semantically significant way, before the
+ expiration time is reached. This normally preserves semantic
+ transparency, as long as the server's expiration times are carefully
+ chosen.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 79]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The expiration mechanism applies only to responses taken from a cache
+ and not to first-hand responses forwarded immediately to the
+ requesting client.
+
+ If an origin server wishes to force a semantically transparent cache
+ to validate every request, it MAY assign an explicit expiration time
+ in the past. This means that the response is always stale, and so the
+ cache SHOULD validate it before using it for subsequent requests. See
+ section 14.9.4 for a more restrictive way to force revalidation.
+
+ If an origin server wishes to force any HTTP/1.1 cache, no matter how
+ it is configured, to validate every request, it SHOULD use the "must-
+ revalidate" cache-control directive (see section 14.9).
+
+ Servers specify explicit expiration times using either the Expires
+ header, or the max-age directive of the Cache-Control header.
+
+ An expiration time cannot be used to force a user agent to refresh
+ its display or reload a resource; its semantics apply only to caching
+ mechanisms, and such mechanisms need only check a resource's
+ expiration status when a new request for that resource is initiated.
+ See section 13.13 for an explanation of the difference between caches
+ and history mechanisms.
+
+13.2.2 Heuristic Expiration
+
+ Since origin servers do not always provide explicit expiration times,
+ HTTP caches typically assign heuristic expiration times, employing
+ algorithms that use other header values (such as the Last-Modified
+ time) to estimate a plausible expiration time. The HTTP/1.1
+ specification does not provide specific algorithms, but does impose
+ worst-case constraints on their results. Since heuristic expiration
+ times might compromise semantic transparency, they ought to used
+ cautiously, and we encourage origin servers to provide explicit
+ expiration times as much as possible.
+
+13.2.3 Age Calculations
+
+ In order to know if a cached entry is fresh, a cache needs to know if
+ its age exceeds its freshness lifetime. We discuss how to calculate
+ the latter in section 13.2.4; this section describes how to calculate
+ the age of a response or cache entry.
+
+ In this discussion, we use the term "now" to mean "the current value
+ of the clock at the host performing the calculation." Hosts that use
+ HTTP, but especially hosts running origin servers and caches, SHOULD
+ use NTP [28] or some similar protocol to synchronize their clocks to
+ a globally accurate time standard.
+
+
+
+Fielding, et al. Standards Track [Page 80]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ HTTP/1.1 requires origin servers to send a Date header, if possible,
+ with every response, giving the time at which the response was
+ generated (see section 14.18). We use the term "date_value" to denote
+ the value of the Date header, in a form appropriate for arithmetic
+ operations.
+
+ HTTP/1.1 uses the Age response-header to convey the estimated age of
+ the response message when obtained from a cache. The Age field value
+ is the cache's estimate of the amount of time since the response was
+ generated or revalidated by the origin server.
+
+ In essence, the Age value is the sum of the time that the response
+ has been resident in each of the caches along the path from the
+ origin server, plus the amount of time it has been in transit along
+ network paths.
+
+ We use the term "age_value" to denote the value of the Age header, in
+ a form appropriate for arithmetic operations.
+
+ A response's age can be calculated in two entirely independent ways:
+
+ 1. now minus date_value, if the local clock is reasonably well
+ synchronized to the origin server's clock. If the result is
+ negative, the result is replaced by zero.
+
+ 2. age_value, if all of the caches along the response path
+ implement HTTP/1.1.
+
+ Given that we have two independent ways to compute the age of a
+ response when it is received, we can combine these as
+
+ corrected_received_age = max(now - date_value, age_value)
+
+ and as long as we have either nearly synchronized clocks or all-
+ HTTP/1.1 paths, one gets a reliable (conservative) result.
+
+ Because of network-imposed delays, some significant interval might
+ pass between the time that a server generates a response and the time
+ it is received at the next outbound cache or client. If uncorrected,
+ this delay could result in improperly low ages.
+
+ Because the request that resulted in the returned Age value must have
+ been initiated prior to that Age value's generation, we can correct
+ for delays imposed by the network by recording the time at which the
+ request was initiated. Then, when an Age value is received, it MUST
+ be interpreted relative to the time the request was initiated, not
+
+
+
+
+
+Fielding, et al. Standards Track [Page 81]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ the time that the response was received. This algorithm results in
+ conservative behavior no matter how much delay is experienced. So, we
+ compute:
+
+ corrected_initial_age = corrected_received_age
+ + (now - request_time)
+
+ where "request_time" is the time (according to the local clock) when
+ the request that elicited this response was sent.
+
+ Summary of age calculation algorithm, when a cache receives a
+ response:
+
+ /*
+ * age_value
+ * is the value of Age: header received by the cache with
+ * this response.
+ * date_value
+ * is the value of the origin server's Date: header
+ * request_time
+ * is the (local) time when the cache made the request
+ * that resulted in this cached response
+ * response_time
+ * is the (local) time when the cache received the
+ * response
+ * now
+ * is the current (local) time
+ */
+
+ apparent_age = max(0, response_time - date_value);
+ corrected_received_age = max(apparent_age, age_value);
+ response_delay = response_time - request_time;
+ corrected_initial_age = corrected_received_age + response_delay;
+ resident_time = now - response_time;
+ current_age = corrected_initial_age + resident_time;
+
+ The current_age of a cache entry is calculated by adding the amount
+ of time (in seconds) since the cache entry was last validated by the
+ origin server to the corrected_initial_age. When a response is
+ generated from a cache entry, the cache MUST include a single Age
+ header field in the response with a value equal to the cache entry's
+ current_age.
+
+ The presence of an Age header field in a response implies that a
+ response is not first-hand. However, the converse is not true, since
+ the lack of an Age header field in a response does not imply that the
+
+
+
+
+
+Fielding, et al. Standards Track [Page 82]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ response is first-hand unless all caches along the request path are
+ compliant with HTTP/1.1 (i.e., older HTTP caches did not implement
+ the Age header field).
+
+13.2.4 Expiration Calculations
+
+ In order to decide whether a response is fresh or stale, we need to
+ compare its freshness lifetime to its age. The age is calculated as
+ described in section 13.2.3; this section describes how to calculate
+ the freshness lifetime, and to determine if a response has expired.
+ In the discussion below, the values can be represented in any form
+ appropriate for arithmetic operations.
+
+ We use the term "expires_value" to denote the value of the Expires
+ header. We use the term "max_age_value" to denote an appropriate
+ value of the number of seconds carried by the "max-age" directive of
+ the Cache-Control header in a response (see section 14.9.3).
+
+ The max-age directive takes priority over Expires, so if max-age is
+ present in a response, the calculation is simply:
+
+ freshness_lifetime = max_age_value
+
+ Otherwise, if Expires is present in the response, the calculation is:
+
+ freshness_lifetime = expires_value - date_value
+
+ Note that neither of these calculations is vulnerable to clock skew,
+ since all of the information comes from the origin server.
+
+ If none of Expires, Cache-Control: max-age, or Cache-Control: s-
+ maxage (see section 14.9.3) appears in the response, and the response
+ does not include other restrictions on caching, the cache MAY compute
+ a freshness lifetime using a heuristic. The cache MUST attach Warning
+ 113 to any response whose age is more than 24 hours if such warning
+ has not already been added.
+
+ Also, if the response does have a Last-Modified time, the heuristic
+ expiration value SHOULD be no more than some fraction of the interval
+ since that time. A typical setting of this fraction might be 10%.
+
+ The calculation to determine if a response has expired is quite
+ simple:
+
+ response_is_fresh = (freshness_lifetime > current_age)
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 83]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.2.5 Disambiguating Expiration Values
+
+ Because expiration values are assigned optimistically, it is possible
+ for two caches to contain fresh values for the same resource that are
+ different.
+
+ If a client performing a retrieval receives a non-first-hand response
+ for a request that was already fresh in its own cache, and the Date
+ header in its existing cache entry is newer than the Date on the new
+ response, then the client MAY ignore the response. If so, it MAY
+ retry the request with a "Cache-Control: max-age=0" directive (see
+ section 14.9), to force a check with the origin server.
+
+ If a cache has two fresh responses for the same representation with
+ different validators, it MUST use the one with the more recent Date
+ header. This situation might arise because the cache is pooling
+ responses from other caches, or because a client has asked for a
+ reload or a revalidation of an apparently fresh cache entry.
+
+13.2.6 Disambiguating Multiple Responses
+
+ Because a client might be receiving responses via multiple paths, so
+ that some responses flow through one set of caches and other
+ responses flow through a different set of caches, a client might
+ receive responses in an order different from that in which the origin
+ server sent them. We would like the client to use the most recently
+ generated response, even if older responses are still apparently
+ fresh.
+
+ Neither the entity tag nor the expiration value can impose an
+ ordering on responses, since it is possible that a later response
+ intentionally carries an earlier expiration time. The Date values are
+ ordered to a granularity of one second.
+
+ When a client tries to revalidate a cache entry, and the response it
+ receives contains a Date header that appears to be older than the one
+ for the existing entry, then the client SHOULD repeat the request
+ unconditionally, and include
+
+ Cache-Control: max-age=0
+
+ to force any intermediate caches to validate their copies directly
+ with the origin server, or
+
+ Cache-Control: no-cache
+
+ to force any intermediate caches to obtain a new copy from the origin
+ server.
+
+
+
+Fielding, et al. Standards Track [Page 84]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the Date values are equal, then the client MAY use either response
+ (or MAY, if it is being extremely prudent, request a new response).
+ Servers MUST NOT depend on clients being able to choose
+ deterministically between responses generated during the same second,
+ if their expiration times overlap.
+
+13.3 Validation Model
+
+ When a cache has a stale entry that it would like to use as a
+ response to a client's request, it first has to check with the origin
+ server (or possibly an intermediate cache with a fresh response) to
+ see if its cached entry is still usable. We call this "validating"
+ the cache entry. Since we do not want to have to pay the overhead of
+ retransmitting the full response if the cached entry is good, and we
+ do not want to pay the overhead of an extra round trip if the cached
+ entry is invalid, the HTTP/1.1 protocol supports the use of
+ conditional methods.
+
+ The key protocol features for supporting conditional methods are
+ those concerned with "cache validators." When an origin server
+ generates a full response, it attaches some sort of validator to it,
+ which is kept with the cache entry. When a client (user agent or
+ proxy cache) makes a conditional request for a resource for which it
+ has a cache entry, it includes the associated validator in the
+ request.
+
+ The server then checks that validator against the current validator
+ for the entity, and, if they match (see section 13.3.3), it responds
+ with a special status code (usually, 304 (Not Modified)) and no
+ entity-body. Otherwise, it returns a full response (including
+ entity-body). Thus, we avoid transmitting the full response if the
+ validator matches, and we avoid an extra round trip if it does not
+ match.
+
+ In HTTP/1.1, a conditional request looks exactly the same as a normal
+ request for the same resource, except that it carries a special
+ header (which includes the validator) that implicitly turns the
+ method (usually, GET) into a conditional.
+
+ The protocol includes both positive and negative senses of cache-
+ validating conditions. That is, it is possible to request either that
+ a method be performed if and only if a validator matches or if and
+ only if no validators match.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 85]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Note: a response that lacks a validator may still be cached, and
+ served from cache until it expires, unless this is explicitly
+ prohibited by a cache-control directive. However, a cache cannot
+ do a conditional retrieval if it does not have a validator for the
+ entity, which means it will not be refreshable after it expires.
+
+13.3.1 Last-Modified Dates
+
+ The Last-Modified entity-header field value is often used as a cache
+ validator. In simple terms, a cache entry is considered to be valid
+ if the entity has not been modified since the Last-Modified value.
+
+13.3.2 Entity Tag Cache Validators
+
+ The ETag response-header field value, an entity tag, provides for an
+ "opaque" cache validator. This might allow more reliable validation
+ in situations where it is inconvenient to store modification dates,
+ where the one-second resolution of HTTP date values is not
+ sufficient, or where the origin server wishes to avoid certain
+ paradoxes that might arise from the use of modification dates.
+
+ Entity Tags are described in section 3.11. The headers used with
+ entity tags are described in sections 14.19, 14.24, 14.26 and 14.44.
+
+13.3.3 Weak and Strong Validators
+
+ Since both origin servers and caches will compare two validators to
+ decide if they represent the same or different entities, one normally
+ would expect that if the entity (the entity-body or any entity-
+ headers) changes in any way, then the associated validator would
+ change as well. If this is true, then we call this validator a
+ "strong validator."
+
+ However, there might be cases when a server prefers to change the
+ validator only on semantically significant changes, and not when
+ insignificant aspects of the entity change. A validator that does not
+ always change when the resource changes is a "weak validator."
+
+ Entity tags are normally "strong validators," but the protocol
+ provides a mechanism to tag an entity tag as "weak." One can think of
+ a strong validator as one that changes whenever the bits of an entity
+ changes, while a weak value changes whenever the meaning of an entity
+ changes. Alternatively, one can think of a strong validator as part
+ of an identifier for a specific entity, while a weak validator is
+ part of an identifier for a set of semantically equivalent entities.
+
+ Note: One example of a strong validator is an integer that is
+ incremented in stable storage every time an entity is changed.
+
+
+
+Fielding, et al. Standards Track [Page 86]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ An entity's modification time, if represented with one-second
+ resolution, could be a weak validator, since it is possible that
+ the resource might be modified twice during a single second.
+
+ Support for weak validators is optional. However, weak validators
+ allow for more efficient caching of equivalent objects; for
+ example, a hit counter on a site is probably good enough if it is
+ updated every few days or weeks, and any value during that period
+ is likely "good enough" to be equivalent.
+
+ A "use" of a validator is either when a client generates a request
+ and includes the validator in a validating header field, or when a
+ server compares two validators.
+
+ Strong validators are usable in any context. Weak validators are only
+ usable in contexts that do not depend on exact equality of an entity.
+ For example, either kind is usable for a conditional GET of a full
+ entity. However, only a strong validator is usable for a sub-range
+ retrieval, since otherwise the client might end up with an internally
+ inconsistent entity.
+
+ Clients MAY issue simple (non-subrange) GET requests with either weak
+ validators or strong validators. Clients MUST NOT use weak validators
+ in other forms of request.
+
+ The only function that the HTTP/1.1 protocol defines on validators is
+ comparison. There are two validator comparison functions, depending
+ on whether the comparison context allows the use of weak validators
+ or not:
+
+ - The strong comparison function: in order to be considered equal,
+ both validators MUST be identical in every way, and both MUST
+ NOT be weak.
+
+ - The weak comparison function: in order to be considered equal,
+ both validators MUST be identical in every way, but either or
+ both of them MAY be tagged as "weak" without affecting the
+ result.
+
+ An entity tag is strong unless it is explicitly tagged as weak.
+ Section 3.11 gives the syntax for entity tags.
+
+ A Last-Modified time, when used as a validator in a request, is
+ implicitly weak unless it is possible to deduce that it is strong,
+ using the following rules:
+
+ - The validator is being compared by an origin server to the
+ actual current validator for the entity and,
+
+
+
+Fielding, et al. Standards Track [Page 87]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - That origin server reliably knows that the associated entity did
+ not change twice during the second covered by the presented
+ validator.
+
+ or
+
+ - The validator is about to be used by a client in an If-
+ Modified-Since or If-Unmodified-Since header, because the client
+ has a cache entry for the associated entity, and
+
+ - That cache entry includes a Date value, which gives the time
+ when the origin server sent the original response, and
+
+ - The presented Last-Modified time is at least 60 seconds before
+ the Date value.
+
+ or
+
+ - The validator is being compared by an intermediate cache to the
+ validator stored in its cache entry for the entity, and
+
+ - That cache entry includes a Date value, which gives the time
+ when the origin server sent the original response, and
+
+ - The presented Last-Modified time is at least 60 seconds before
+ the Date value.
+
+ This method relies on the fact that if two different responses were
+ sent by the origin server during the same second, but both had the
+ same Last-Modified time, then at least one of those responses would
+ have a Date value equal to its Last-Modified time. The arbitrary 60-
+ second limit guards against the possibility that the Date and Last-
+ Modified values are generated from different clocks, or at somewhat
+ different times during the preparation of the response. An
+ implementation MAY use a value larger than 60 seconds, if it is
+ believed that 60 seconds is too short.
+
+ If a client wishes to perform a sub-range retrieval on a value for
+ which it has only a Last-Modified time and no opaque validator, it
+ MAY do this only if the Last-Modified time is strong in the sense
+ described here.
+
+ A cache or origin server receiving a conditional request, other than
+ a full-body GET request, MUST use the strong comparison function to
+ evaluate the condition.
+
+ These rules allow HTTP/1.1 caches and clients to safely perform sub-
+ range retrievals on values that have been obtained from HTTP/1.0
+
+
+
+Fielding, et al. Standards Track [Page 88]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ servers.
+
+13.3.4 Rules for When to Use Entity Tags and Last-Modified Dates
+
+ We adopt a set of rules and recommendations for origin servers,
+ clients, and caches regarding when various validator types ought to
+ be used, and for what purposes.
+
+ HTTP/1.1 origin servers:
+
+ - SHOULD send an entity tag validator unless it is not feasible to
+ generate one.
+
+ - MAY send a weak entity tag instead of a strong entity tag, if
+ performance considerations support the use of weak entity tags,
+ or if it is unfeasible to send a strong entity tag.
+
+ - SHOULD send a Last-Modified value if it is feasible to send one,
+ unless the risk of a breakdown in semantic transparency that
+ could result from using this date in an If-Modified-Since header
+ would lead to serious problems.
+
+ In other words, the preferred behavior for an HTTP/1.1 origin server
+ is to send both a strong entity tag and a Last-Modified value.
+
+ In order to be legal, a strong entity tag MUST change whenever the
+ associated entity value changes in any way. A weak entity tag SHOULD
+ change whenever the associated entity changes in a semantically
+ significant way.
+
+ Note: in order to provide semantically transparent caching, an
+ origin server must avoid reusing a specific strong entity tag
+ value for two different entities, or reusing a specific weak
+ entity tag value for two semantically different entities. Cache
+ entries might persist for arbitrarily long periods, regardless of
+ expiration times, so it might be inappropriate to expect that a
+ cache will never again attempt to validate an entry using a
+ validator that it obtained at some point in the past.
+
+ HTTP/1.1 clients:
+
+ - If an entity tag has been provided by the origin server, MUST
+ use that entity tag in any cache-conditional request (using If-
+ Match or If-None-Match).
+
+ - If only a Last-Modified value has been provided by the origin
+ server, SHOULD use that value in non-subrange cache-conditional
+ requests (using If-Modified-Since).
+
+
+
+Fielding, et al. Standards Track [Page 89]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - If only a Last-Modified value has been provided by an HTTP/1.0
+ origin server, MAY use that value in subrange cache-conditional
+ requests (using If-Unmodified-Since:). The user agent SHOULD
+ provide a way to disable this, in case of difficulty.
+
+ - If both an entity tag and a Last-Modified value have been
+ provided by the origin server, SHOULD use both validators in
+ cache-conditional requests. This allows both HTTP/1.0 and
+ HTTP/1.1 caches to respond appropriately.
+
+ An HTTP/1.1 origin server, upon receiving a conditional request that
+ includes both a Last-Modified date (e.g., in an If-Modified-Since or
+ If-Unmodified-Since header field) and one or more entity tags (e.g.,
+ in an If-Match, If-None-Match, or If-Range header field) as cache
+ validators, MUST NOT return a response status of 304 (Not Modified)
+ unless doing so is consistent with all of the conditional header
+ fields in the request.
+
+ An HTTP/1.1 caching proxy, upon receiving a conditional request that
+ includes both a Last-Modified date and one or more entity tags as
+ cache validators, MUST NOT return a locally cached response to the
+ client unless that cached response is consistent with all of the
+ conditional header fields in the request.
+
+ Note: The general principle behind these rules is that HTTP/1.1
+ servers and clients should transmit as much non-redundant
+ information as is available in their responses and requests.
+ HTTP/1.1 systems receiving this information will make the most
+ conservative assumptions about the validators they receive.
+
+ HTTP/1.0 clients and caches will ignore entity tags. Generally,
+ last-modified values received or used by these systems will
+ support transparent and efficient caching, and so HTTP/1.1 origin
+ servers should provide Last-Modified values. In those rare cases
+ where the use of a Last-Modified value as a validator by an
+ HTTP/1.0 system could result in a serious problem, then HTTP/1.1
+ origin servers should not provide one.
+
+13.3.5 Non-validating Conditionals
+
+ The principle behind entity tags is that only the service author
+ knows the semantics of a resource well enough to select an
+ appropriate cache validation mechanism, and the specification of any
+ validator comparison function more complex than byte-equality would
+ open up a can of worms. Thus, comparisons of any other headers
+ (except Last-Modified, for compatibility with HTTP/1.0) are never
+ used for purposes of validating a cache entry.
+
+
+
+
+Fielding, et al. Standards Track [Page 90]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.4 Response Cacheability
+
+ Unless specifically constrained by a cache-control (section 14.9)
+ directive, a caching system MAY always store a successful response
+ (see section 13.8) as a cache entry, MAY return it without validation
+ if it is fresh, and MAY return it after successful validation. If
+ there is neither a cache validator nor an explicit expiration time
+ associated with a response, we do not expect it to be cached, but
+ certain caches MAY violate this expectation (for example, when little
+ or no network connectivity is available). A client can usually detect
+ that such a response was taken from a cache by comparing the Date
+ header to the current time.
+
+ Note: some HTTP/1.0 caches are known to violate this expectation
+ without providing any Warning.
+
+ However, in some cases it might be inappropriate for a cache to
+ retain an entity, or to return it in response to a subsequent
+ request. This might be because absolute semantic transparency is
+ deemed necessary by the service author, or because of security or
+ privacy considerations. Certain cache-control directives are
+ therefore provided so that the server can indicate that certain
+ resource entities, or portions thereof, are not to be cached
+ regardless of other considerations.
+
+ Note that section 14.8 normally prevents a shared cache from saving
+ and returning a response to a previous request if that request
+ included an Authorization header.
+
+ A response received with a status code of 200, 203, 206, 300, 301 or
+ 410 MAY be stored by a cache and used in reply to a subsequent
+ request, subject to the expiration mechanism, unless a cache-control
+ directive prohibits caching. However, a cache that does not support
+ the Range and Content-Range headers MUST NOT cache 206 (Partial
+ Content) responses.
+
+ A response received with any other status code (e.g. status codes 302
+ and 307) MUST NOT be returned in a reply to a subsequent request
+ unless there are cache-control directives or another header(s) that
+ explicitly allow it. For example, these include the following: an
+ Expires header (section 14.21); a "max-age", "s-maxage", "must-
+ revalidate", "proxy-revalidate", "public" or "private" cache-control
+ directive (section 14.9).
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 91]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.5 Constructing Responses From Caches
+
+ The purpose of an HTTP cache is to store information received in
+ response to requests for use in responding to future requests. In
+ many cases, a cache simply returns the appropriate parts of a
+ response to the requester. However, if the cache holds a cache entry
+ based on a previous response, it might have to combine parts of a new
+ response with what is held in the cache entry.
+
+13.5.1 End-to-end and Hop-by-hop Headers
+
+ For the purpose of defining the behavior of caches and non-caching
+ proxies, we divide HTTP headers into two categories:
+
+ - End-to-end headers, which are transmitted to the ultimate
+ recipient of a request or response. End-to-end headers in
+ responses MUST be stored as part of a cache entry and MUST be
+ transmitted in any response formed from a cache entry.
+
+ - Hop-by-hop headers, which are meaningful only for a single
+ transport-level connection, and are not stored by caches or
+ forwarded by proxies.
+
+ The following HTTP/1.1 headers are hop-by-hop headers:
+
+ - Connection
+ - Keep-Alive
+ - Proxy-Authenticate
+ - Proxy-Authorization
+ - TE
+ - Trailers [[should be "Trailer"]]
+ - Transfer-Encoding
+ - Upgrade
+
+ All other headers defined by HTTP/1.1 are end-to-end headers.
+
+ Other hop-by-hop headers MUST be listed in a Connection header,
+ (section 14.10) to be introduced into HTTP/1.1 (or later).
+
+13.5.2 Non-modifiable Headers
+
+ Some features of the HTTP/1.1 protocol, such as Digest
+ Authentication, depend on the value of certain end-to-end headers. A
+ transparent proxy SHOULD NOT modify an end-to-end header unless the
+ definition of that header requires or specifically allows that.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 92]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A transparent proxy MUST NOT modify any of the following fields in a
+ request or response, and it MUST NOT add any of these fields if not
+ already present:
+
+ - Content-Location
+
+ - Content-MD5
+
+ - ETag
+
+ - Last-Modified
+
+ A transparent proxy MUST NOT modify any of the following fields in a
+ response:
+
+ - Expires
+
+ but it MAY add any of these fields if not already present. If an
+ Expires header is added, it MUST be given a field-value identical to
+ that of the Date header in that response.
+
+ A proxy MUST NOT modify or add any of the following fields in a
+ message that contains the no-transform cache-control directive, or in
+ any request:
+
+ - Content-Encoding
+
+ - Content-Range
+
+ - Content-Type
+
+ A non-transparent proxy MAY modify or add these fields to a message
+ that does not include no-transform, but if it does so, it MUST add a
+ Warning 214 (Transformation applied) if one does not already appear
+ in the message (see section 14.46).
+
+ Warning: unnecessary modification of end-to-end headers might
+ cause authentication failures if stronger authentication
+ mechanisms are introduced in later versions of HTTP. Such
+ authentication mechanisms MAY rely on the values of header fields
+ not listed here.
+
+ The Content-Length field of a request or response is added or deleted
+ according to the rules in section 4.4. A transparent proxy MUST
+ preserve the entity-length (section 7.2.2) of the entity-body,
+ although it MAY change the transfer-length (section 4.4).
+
+
+
+
+
+Fielding, et al. Standards Track [Page 93]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.5.3 Combining Headers
+
+ When a cache makes a validating request to a server, and the server
+ provides a 304 (Not Modified) response or a 206 (Partial Content)
+ response, the cache then constructs a response to send to the
+ requesting client.
+
+ If the status code is 304 (Not Modified), the cache uses the entity-
+ body stored in the cache entry as the entity-body of this outgoing
+ response. If the status code is 206 (Partial Content) and the ETag or
+ Last-Modified headers match exactly, the cache MAY combine the
+ contents stored in the cache entry with the new contents received in
+ the response and use the result as the entity-body of this outgoing
+ response, (see 13.5.4).
+
+ The end-to-end headers stored in the cache entry are used for the
+ constructed response, except that
+
+ - any stored Warning headers with warn-code 1xx (see section
+ 14.46) MUST be deleted from the cache entry and the forwarded
+ response.
+
+ - any stored Warning headers with warn-code 2xx MUST be retained
+ in the cache entry and the forwarded response.
+
+ - any end-to-end headers provided in the 304 or 206 response MUST
+ replace the corresponding headers from the cache entry.
+
+ Unless the cache decides to remove the cache entry, it MUST also
+ replace the end-to-end headers stored with the cache entry with
+ corresponding headers received in the incoming response, except for
+ Warning headers as described immediately above. If a header field-
+ name in the incoming response matches more than one header in the
+ cache entry, all such old headers MUST be replaced.
+
+ In other words, the set of end-to-end headers received in the
+ incoming response overrides all corresponding end-to-end headers
+ stored with the cache entry (except for stored Warning headers with
+ warn-code 1xx, which are deleted even if not overridden).
+
+ Note: this rule allows an origin server to use a 304 (Not
+ Modified) or a 206 (Partial Content) response to update any header
+ associated with a previous response for the same entity or sub-
+ ranges thereof, although it might not always be meaningful or
+ correct to do so. This rule does not allow an origin server to use
+ a 304 (Not Modified) or a 206 (Partial Content) response to
+ entirely delete a header that it had provided with a previous
+ response.
+
+
+
+Fielding, et al. Standards Track [Page 94]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.5.4 Combining Byte Ranges
+
+ A response might transfer only a subrange of the bytes of an entity-
+ body, either because the request included one or more Range
+ specifications, or because a connection was broken prematurely. After
+ several such transfers, a cache might have received several ranges of
+ the same entity-body.
+
+ If a cache has a stored non-empty set of subranges for an entity, and
+ an incoming response transfers another subrange, the cache MAY
+ combine the new subrange with the existing set if both the following
+ conditions are met:
+
+ - Both the incoming response and the cache entry have a cache
+ validator.
+
+ - The two cache validators match using the strong comparison
+ function (see section 13.3.3).
+
+ If either requirement is not met, the cache MUST use only the most
+ recent partial response (based on the Date values transmitted with
+ every response, and using the incoming response if these values are
+ equal or missing), and MUST discard the other partial information.
+
+13.6 Caching Negotiated Responses
+
+ Use of server-driven content negotiation (section 12.1), as indicated
+ by the presence of a Vary header field in a response, alters the
+ conditions and procedure by which a cache can use the response for
+ subsequent requests. See section 14.44 for use of the Vary header
+ field by servers.
+
+ A server SHOULD use the Vary header field to inform a cache of what
+ request-header fields were used to select among multiple
+ representations of a cacheable response subject to server-driven
+ negotiation. The set of header fields named by the Vary field value
+ is known as the "selecting" request-headers.
+
+ When the cache receives a subsequent request whose Request-URI
+ specifies one or more cache entries including a Vary header field,
+ the cache MUST NOT use such a cache entry to construct a response to
+ the new request unless all of the selecting request-headers present
+ in the new request match the corresponding stored request-headers in
+ the original request.
+
+ The selecting request-headers from two requests are defined to match
+ if and only if the selecting request-headers in the first request can
+ be transformed to the selecting request-headers in the second request
+
+
+
+Fielding, et al. Standards Track [Page 95]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ by adding or removing linear white space (LWS) at places where this
+ is allowed by the corresponding BNF, and/or combining multiple
+ message-header fields with the same field name following the rules
+ about message headers in section 4.2.
+
+ A Vary header field-value of "*" always fails to match and subsequent
+ requests on that resource can only be properly interpreted by the
+ origin server.
+
+ If the selecting request header fields for the cached entry do not
+ match the selecting request header fields of the new request, then
+ the cache MUST NOT use a cached entry to satisfy the request unless
+ it first relays the new request to the origin server in a conditional
+ request and the server responds with 304 (Not Modified), including an
+ entity tag or Content-Location that indicates the entity to be used.
+
+ If an entity tag was assigned to a cached representation, the
+ forwarded request SHOULD be conditional and include the entity tags
+ in an If-None-Match header field from all its cache entries for the
+ resource. This conveys to the server the set of entities currently
+ held by the cache, so that if any one of these entities matches the
+ requested entity, the server can use the ETag header field in its 304
+ (Not Modified) response to tell the cache which entry is appropriate.
+ If the entity-tag of the new response matches that of an existing
+ entry, the new response SHOULD be used to update the header fields of
+ the existing entry, and the result MUST be returned to the client.
+
+ If any of the existing cache entries contains only partial content
+ for the associated entity, its entity-tag SHOULD NOT be included in
+ the If-None-Match header field unless the request is for a range that
+ would be fully satisfied by that entry.
+
+ If a cache receives a successful response whose Content-Location
+ field matches that of an existing cache entry for the same Request-
+ ]URI, whose entity-tag differs from that of the existing entry, and
+ whose Date is more recent than that of the existing entry, the
+ existing entry SHOULD NOT be returned in response to future requests
+ and SHOULD be deleted from the cache.
+
+13.7 Shared and Non-Shared Caches
+
+ For reasons of security and privacy, it is necessary to make a
+ distinction between "shared" and "non-shared" caches. A non-shared
+ cache is one that is accessible only to a single user. Accessibility
+ in this case SHOULD be enforced by appropriate security mechanisms.
+ All other caches are considered to be "shared." Other sections of
+
+
+
+
+
+Fielding, et al. Standards Track [Page 96]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ this specification place certain constraints on the operation of
+ shared caches in order to prevent loss of privacy or failure of
+ access controls.
+
+13.8 Errors or Incomplete Response Cache Behavior
+
+ A cache that receives an incomplete response (for example, with fewer
+ bytes of data than specified in a Content-Length header) MAY store
+ the response. However, the cache MUST treat this as a partial
+ response. Partial responses MAY be combined as described in section
+ 13.5.4; the result might be a full response or might still be
+ partial. A cache MUST NOT return a partial response to a client
+ without explicitly marking it as such, using the 206 (Partial
+ Content) status code. A cache MUST NOT return a partial response
+ using a status code of 200 (OK).
+
+ If a cache receives a 5xx response while attempting to revalidate an
+ entry, it MAY either forward this response to the requesting client,
+ or act as if the server failed to respond. In the latter case, it MAY
+ return a previously received response unless the cached entry
+ includes the "must-revalidate" cache-control directive (see section
+ 14.9).
+
+13.9 Side Effects of GET and HEAD
+
+ Unless the origin server explicitly prohibits the caching of their
+ responses, the application of GET and HEAD methods to any resources
+ SHOULD NOT have side effects that would lead to erroneous behavior if
+ these responses are taken from a cache. They MAY still have side
+ effects, but a cache is not required to consider such side effects in
+ its caching decisions. Caches are always expected to observe an
+ origin server's explicit restrictions on caching.
+
+ We note one exception to this rule: since some applications have
+ traditionally used GETs and HEADs with query URLs (those containing a
+ "?" in the rel_path part) to perform operations with significant side
+ effects, caches MUST NOT treat responses to such URIs as fresh unless
+ the server provides an explicit expiration time. This specifically
+ means that responses from HTTP/1.0 servers for such URIs SHOULD NOT
+ be taken from a cache. See section 9.1.1 for related information.
+
+13.10 Invalidation After Updates or Deletions
+
+ The effect of certain methods performed on a resource at the origin
+ server might cause one or more existing cache entries to become non-
+ transparently invalid. That is, although they might continue to be
+ "fresh," they do not accurately reflect what the origin server would
+ return for a new request on that resource.
+
+
+
+Fielding, et al. Standards Track [Page 97]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ There is no way for the HTTP protocol to guarantee that all such
+ cache entries are marked invalid. For example, the request that
+ caused the change at the origin server might not have gone through
+ the proxy where a cache entry is stored. However, several rules help
+ reduce the likelihood of erroneous behavior.
+
+ In this section, the phrase "invalidate an entity" means that the
+ cache will either remove all instances of that entity from its
+ storage, or will mark these as "invalid" and in need of a mandatory
+ revalidation before they can be returned in response to a subsequent
+ request.
+
+ Some HTTP methods MUST cause a cache to invalidate an entity. This is
+ either the entity referred to by the Request-URI, or by the Location
+ or Content-Location headers (if present). These methods are:
+
+ - PUT
+
+ - DELETE
+
+ - POST
+
+ In order to prevent denial of service attacks, an invalidation based
+ on the URI in a Location or Content-Location header MUST only be
+ performed if the host part is the same as in the Request-URI.
+
+[[ Should be: ]]
+[[ An invalidation based on the URI in a Location or Content-Location ]]
+[[ header MUST NOT be performed if the host part of that URI differs ]]
+[[ from the host part in the Request-URI. This helps prevent denial of ]]
+[[ service attacks. ]]
+
+ A cache that passes through requests for methods it does not
+ understand SHOULD invalidate any entities referred to by the
+ Request-URI.
+
+13.11 Write-Through Mandatory
+
+ All methods that might be expected to cause modifications to the
+ origin server's resources MUST be written through to the origin
+ server. This currently includes all methods except for GET and HEAD.
+ A cache MUST NOT reply to such a request from a client before having
+ transmitted the request to the inbound server, and having received a
+ corresponding response from the inbound server. This does not prevent
+ a proxy cache from sending a 100 (Continue) response before the
+ inbound server has sent its final reply.
+
+ The alternative (known as "write-back" or "copy-back" caching) is not
+ allowed in HTTP/1.1, due to the difficulty of providing consistent
+ updates and the problems arising from server, cache, or network
+ failure prior to write-back.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 98]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+13.12 Cache Replacement
+
+ If a new cacheable (see sections 14.9.2, 13.2.5, 13.2.6 and 13.8)
+ response is received from a resource while any existing responses for
+ the same resource are cached, the cache SHOULD use the new response
+ to reply to the current request. It MAY insert it into cache storage
+ and MAY, if it meets all other requirements, use it to respond to any
+ future requests that would previously have caused the old response to
+ be returned. If it inserts the new response into cache storage the
+ rules in section 13.5.3 apply.
+
+ Note: a new response that has an older Date header value than
+ existing cached responses is not cacheable.
+
+13.13 History Lists
+
+ User agents often have history mechanisms, such as "Back" buttons and
+ history lists, which can be used to redisplay an entity retrieved
+ earlier in a session.
+
+ History mechanisms and caches are different. In particular history
+ mechanisms SHOULD NOT try to show a semantically transparent view of
+ the current state of a resource. Rather, a history mechanism is meant
+ to show exactly what the user saw at the time when the resource was
+ retrieved.
+
+ By default, an expiration time does not apply to history mechanisms.
+ If the entity is still in storage, a history mechanism SHOULD display
+ it even if the entity has expired, unless the user has specifically
+ configured the agent to refresh expired history documents.
+
+ This is not to be construed to prohibit the history mechanism from
+ telling the user that a view might be stale.
+
+ Note: if history list mechanisms unnecessarily prevent users from
+ viewing stale resources, this will tend to force service authors
+ to avoid using HTTP expiration controls and cache controls when
+ they would otherwise like to. Service authors may consider it
+ important that users not be presented with error messages or
+ warning messages when they use navigation controls (such as BACK)
+ to view previously fetched resources. Even though sometimes such
+ resources ought not to cached, or ought to expire quickly, user
+ interface considerations may force service authors to resort to
+ other means of preventing caching (e.g. "once-only" URLs) in order
+ not to suffer the effects of improperly functioning history
+ mechanisms.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 99]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+14 Header Field Definitions
+
+ This section defines the syntax and semantics of all standard
+ HTTP/1.1 header fields. For entity-header fields, both sender and
+ recipient refer to either the client or the server, depending on who
+ sends and who receives the entity.
+
+14.1 Accept
+
+ The Accept request-header field can be used to specify certain media
+ types which are acceptable for the response. Accept headers can be
+ used to indicate that the request is specifically limited to a small
+ set of desired types, as in the case of a request for an in-line
+ image.
+
+ Accept = "Accept" ":"
+ #( media-range [ accept-params ] )
+
+ media-range = ( "*/*"
+ | ( type "/" "*" )
+ | ( type "/" subtype )
+ ) *( ";" parameter )
+ accept-params = ";" "q" "=" qvalue *( accept-extension )
+ accept-extension = ";" token [ "=" ( token | quoted-string ) ]
+
+ The asterisk "*" character is used to group media types into ranges,
+ with "*/*" indicating all media types and "type/*" indicating all
+ subtypes of that type. The media-range MAY include media type
+ parameters that are applicable to that range.
+
+ Each media-range MAY be followed by one or more accept-params,
+ beginning with the "q" parameter for indicating a relative quality
+ factor. The first "q" parameter (if any) separates the media-range
+ parameter(s) from the accept-params. Quality factors allow the user
+ or user agent to indicate the relative degree of preference for that
+ media-range, using the qvalue scale from 0 to 1 (section 3.9). The
+ default value is q=1.
+
+ Note: Use of the "q" parameter name to separate media type
+ parameters from Accept extension parameters is due to historical
+ practice. Although this prevents any media type parameter named
+ "q" from being used with a media range, such an event is believed
+ to be unlikely given the lack of any "q" parameters in the IANA
+ media type registry and the rare usage of any media type
+ parameters in Accept. Future media types are discouraged from
+ registering any parameter named "q".
+
+
+
+
+
+Fielding, et al. Standards Track [Page 100]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The example
+
+ Accept: audio/*; q=0.2, audio/basic
+
+ SHOULD be interpreted as "I prefer audio/basic, but send me any audio
+ type if it is the best available after an 80% mark-down in quality."
+
+ If no Accept header field is present, then it is assumed that the
+ client accepts all media types. If an Accept header field is present,
+ and if the server cannot send a response which is acceptable
+ according to the combined Accept field value, then the server SHOULD
+ send a 406 (not acceptable) response.
+
+ A more elaborate example is
+
+ Accept: text/plain; q=0.5, text/html,
+ text/x-dvi; q=0.8, text/x-c
+
+ Verbally, this would be interpreted as "text/html and text/x-c are
+ the preferred media types, but if they do not exist, then send the
+ text/x-dvi entity, and if that does not exist, send the text/plain
+ entity."
+
+ Media ranges can be overridden by more specific media ranges or
+ specific media types. If more than one media range applies to a given
+ type, the most specific reference has precedence. For example,
+
+ Accept: text/*, text/html, text/html;level=1, */*
+
+ have the following precedence:
+
+ 1) text/html;level=1
+ 2) text/html
+ 3) text/*
+ 4) */*
+
+ The media type quality factor associated with a given type is
+ determined by finding the media range with the highest precedence
+ which matches that type. For example,
+
+ Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1,
+ text/html;level=2;q=0.4, */*;q=0.5
+
+ would cause the following values to be associated:
+
+ text/html;level=1 = 1
+ text/html = 0.7
+ text/plain = 0.3
+
+
+
+Fielding, et al. Standards Track [Page 101]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ image/jpeg = 0.5
+ text/html;level=2 = 0.4
+ text/html;level=3 = 0.7
+
+ Note: A user agent might be provided with a default set of quality
+ values for certain media ranges. However, unless the user agent is
+ a closed system which cannot interact with other rendering agents,
+ this default set ought to be configurable by the user.
+
+14.2 Accept-Charset
+
+ The Accept-Charset request-header field can be used to indicate what
+ character sets are acceptable for the response. This field allows
+ clients capable of understanding more comprehensive or special-
+ purpose character sets to signal that capability to a server which is
+ capable of representing documents in those character sets.
+
+ Accept-Charset = "Accept-Charset" ":"
+ 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] )
+
+
+ Character set values are described in section 3.4. Each charset MAY
+ be given an associated quality value which represents the user's
+ preference for that charset. The default value is q=1. An example is
+
+ Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
+
+ The special value "*", if present in the Accept-Charset field,
+ matches every character set (including ISO-8859-1) which is not
+ mentioned elsewhere in the Accept-Charset field. If no "*" is present
+ in an Accept-Charset field, then all character sets not explicitly
+ mentioned get a quality value of 0, except for ISO-8859-1, which gets
+ a quality value of 1 if not explicitly mentioned.
+
+ If no Accept-Charset header is present, the default is that any
+ character set is acceptable. If an Accept-Charset header is present,
+ and if the server cannot send a response which is acceptable
+ according to the Accept-Charset header, then the server SHOULD send
+ an error response with the 406 (not acceptable) status code, though
+ the sending of an unacceptable response is also allowed.
+
+14.3 Accept-Encoding
+
+ The Accept-Encoding request-header field is similar to Accept, but
+ restricts the content-codings (section 3.5) that are acceptable in
+ the response.
+
+ Accept-Encoding = "Accept-Encoding" ":"
+
+
+
+Fielding, et al. Standards Track [Page 102]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 1#( codings [ ";" "q" "=" qvalue ] )
+ codings = ( content-coding | "*" )
+
+ [[ http://lists.w3.org/Archives/Public/ietf-http-wg/2005AprJun/0029.html ]]
+ [[ points out that the "1#" must be "#" to make the examples below and ]]
+ [[ the text of rule 4 correct. ]]
+
+ Examples of its use are:
+
+ Accept-Encoding: compress, gzip
+ Accept-Encoding:
+ Accept-Encoding: *
+ Accept-Encoding: compress;q=0.5, gzip;q=1.0
+ Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
+
+ A server tests whether a content-coding is acceptable, according to
+ an Accept-Encoding field, using these rules:
+
+ 1. If the content-coding is one of the content-codings listed in
+ the Accept-Encoding field, then it is acceptable, unless it is
+ accompanied by a qvalue of 0. (As defined in section 3.9, a
+ qvalue of 0 means "not acceptable.")
+
+ 2. The special "*" symbol in an Accept-Encoding field matches any
+ available content-coding not explicitly listed in the header
+ field.
+
+ 3. If multiple content-codings are acceptable, then the acceptable
+ content-coding with the highest non-zero qvalue is preferred.
+
+ 4. The "identity" content-coding is always acceptable, unless
+ specifically refused because the Accept-Encoding field includes
+ "identity;q=0", or because the field includes "*;q=0" and does
+ not explicitly include the "identity" content-coding. If the
+ Accept-Encoding field-value is empty, then only the "identity"
+ encoding is acceptable.
+
+ If an Accept-Encoding field is present in a request, and if the
+ server cannot send a response which is acceptable according to the
+ Accept-Encoding header, then the server SHOULD send an error response
+ with the 406 (Not Acceptable) status code.
+
+ If no Accept-Encoding field is present in a request, the server MAY
+ assume that the client will accept any content coding. In this case,
+ if "identity" is one of the available content-codings, then the
+ server SHOULD use the "identity" content-coding, unless it has
+ additional information that a different content-coding is meaningful
+ to the client.
+
+ Note: If the request does not include an Accept-Encoding field,
+ and if the "identity" content-coding is unavailable, then
+ content-codings commonly understood by HTTP/1.0 clients (i.e.,
+
+
+
+Fielding, et al. Standards Track [Page 103]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ "gzip" and "compress") are preferred; some older clients
+ improperly display messages sent with other content-codings. The
+ server might also make this decision based on information about
+ the particular user-agent or client.
+
+ Note: Most HTTP/1.0 applications do not recognize or obey qvalues
+ associated with content-codings. This means that qvalues will not
+ work and are not permitted with x-gzip or x-compress.
+
+14.4 Accept-Language
+
+ The Accept-Language request-header field is similar to Accept, but
+ restricts the set of natural languages that are preferred as a
+ response to the request. Language tags are defined in section 3.10.
+
+ Accept-Language = "Accept-Language" ":"
+ 1#( language-range [ ";" "q" "=" qvalue ] )
+ language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
+
+ Each language-range MAY be given an associated quality value which
+ represents an estimate of the user's preference for the languages
+ specified by that range. The quality value defaults to "q=1". For
+ example,
+
+ Accept-Language: da, en-gb;q=0.8, en;q=0.7
+
+ would mean: "I prefer Danish, but will accept British English and
+ other types of English." A language-range matches a language-tag if
+ it exactly equals the tag, or if it exactly equals a prefix of the
+ tag such that the first tag character following the prefix is "-".
+ The special range "*", if present in the Accept-Language field,
+ matches every tag not matched by any other range present in the
+ Accept-Language field.
+
+ Note: This use of a prefix matching rule does not imply that
+ language tags are assigned to languages in such a way that it is
+ always true that if a user understands a language with a certain
+ tag, then this user will also understand all languages with tags
+ for which this tag is a prefix. The prefix rule simply allows the
+ use of prefix tags if this is the case.
+
+ The language quality factor assigned to a language-tag by the
+ Accept-Language field is the quality value of the longest language-
+ range in the field that matches the language-tag. If no language-
+ range in the field matches the tag, the language quality factor
+ assigned is 0. If no Accept-Language header is present in the
+ request, the server
+
+
+
+
+Fielding, et al. Standards Track [Page 104]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ SHOULD assume that all languages are equally acceptable. If an
+ Accept-Language header is present, then all languages which are
+ assigned a quality factor greater than 0 are acceptable.
+
+ It might be contrary to the privacy expectations of the user to send
+ an Accept-Language header with the complete linguistic preferences of
+ the user in every request. For a discussion of this issue, see
+ section 15.1.4.
+
+ As intelligibility is highly dependent on the individual user, it is
+ recommended that client applications make the choice of linguistic
+ preference available to the user. If the choice is not made
+ available, then the Accept-Language header field MUST NOT be given in
+ the request.
+
+ Note: When making the choice of linguistic preference available to
+ the user, we remind implementors of the fact that users are not
+ familiar with the details of language matching as described above,
+ and should provide appropriate guidance. As an example, users
+ might assume that on selecting "en-gb", they will be served any
+ kind of English document if British English is not available. A
+ user agent might suggest in such a case to add "en" to get the
+ best matching behavior.
+
+14.5 Accept-Ranges
+
+ The Accept-Ranges response-header field allows the server to
+ indicate its acceptance of range requests for a resource:
+
+ Accept-Ranges = "Accept-Ranges" ":" acceptable-ranges
+ acceptable-ranges = 1#range-unit | "none"
+
+ Origin servers that accept byte-range requests MAY send
+
+ Accept-Ranges: bytes
+
+ but are not required to do so. Clients MAY generate byte-range
+ requests without having received this header for the resource
+ involved. Range units are defined in section 3.12.
+
+ Servers that do not accept any kind of range request for a
+ resource MAY send
+
+ Accept-Ranges: none
+
+ to advise the client not to attempt a range request.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 105]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.6 Age
+
+ The Age response-header field conveys the sender's estimate of the
+ amount of time since the response (or its revalidation) was
+ generated at the origin server. A cached response is "fresh" if
+ its age does not exceed its freshness lifetime. Age values are
+ calculated as specified in section 13.2.3.
+
+ Age = "Age" ":" age-value
+ age-value = delta-seconds
+
+ Age values are non-negative decimal integers, representing time in
+ seconds.
+
+ If a cache receives a value larger than the largest positive
+ integer it can represent, or if any of its age calculations
+ overflows, it MUST transmit an Age header with a value of
+ 2147483648 (2^31). An HTTP/1.1 server that includes a cache MUST
+ include an Age header field in every response generated from its
+ own cache. Caches SHOULD use an arithmetic type of at least 31
+ bits of range.
+
+14.7 Allow
+
+ The Allow entity-header field lists the set of methods supported
+ by the resource identified by the Request-URI. The purpose of this
+ field is strictly to inform the recipient of valid methods
+ associated with the resource. An Allow header field MUST be
+ present in a 405 (Method Not Allowed) response.
+
+ Allow = "Allow" ":" #Method
+
+ Example of use:
+
+ Allow: GET, HEAD, PUT
+
+ This field cannot prevent a client from trying other methods.
+ However, the indications given by the Allow header field value
+ SHOULD be followed. The actual set of allowed methods is defined
+ by the origin server at the time of each request.
+
+ The Allow header field MAY be provided with a PUT request to
+ recommend the methods to be supported by the new or modified
+ resource. The server is not required to support these methods and
+ SHOULD include an Allow header in the response giving the actual
+ supported methods.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 106]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A proxy MUST NOT modify the Allow header field even if it does not
+ understand all the methods specified, since the user agent might
+ have other means of communicating with the origin server.
+
+14.8 Authorization
+
+ A user agent that wishes to authenticate itself with a server--
+ usually, but not necessarily, after receiving a 401 response--does
+ so by including an Authorization request-header field with the
+ request. The Authorization field value consists of credentials
+ containing the authentication information of the user agent for
+ the realm of the resource being requested.
+
+ Authorization = "Authorization" ":" credentials
+
+ HTTP access authentication is described in "HTTP Authentication:
+ Basic and Digest Access Authentication" [43]. If a request is
+ authenticated and a realm specified, the same credentials SHOULD
+ be valid for all other requests within this realm (assuming that
+ the authentication scheme itself does not require otherwise, such
+ as credentials that vary according to a challenge value or using
+ synchronized clocks).
+
+ When a shared cache (see section 13.7) receives a request
+ containing an Authorization field, it MUST NOT return the
+ corresponding response as a reply to any other request, unless one
+ of the following specific exceptions holds:
+
+ 1. If the response includes the "s-maxage" cache-control
+ directive, the cache MAY use that response in replying to a
+ subsequent request. But (if the specified maximum age has
+ passed) a proxy cache MUST first revalidate it with the origin
+ server, using the request-headers from the new request to allow
+ the origin server to authenticate the new request. (This is the
+ defined behavior for s-maxage.) If the response includes "s-
+ maxage=0", the proxy MUST always revalidate it before re-using
+ it.
+
+ 2. If the response includes the "must-revalidate" cache-control
+ directive, the cache MAY use that response in replying to a
+ subsequent request. But if the response is stale, all caches
+ MUST first revalidate it with the origin server, using the
+ request-headers from the new request to allow the origin server
+ to authenticate the new request.
+
+ 3. If the response includes the "public" cache-control directive,
+ it MAY be returned in reply to any subsequent request.
+
+
+
+
+Fielding, et al. Standards Track [Page 107]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.9 Cache-Control
+
+ The Cache-Control general-header field is used to specify directives
+ that MUST be obeyed by all caching mechanisms along the
+ request/response chain. The directives specify behavior intended to
+ prevent caches from adversely interfering with the request or
+ response. These directives typically override the default caching
+ algorithms. Cache directives are unidirectional in that the presence
+ of a directive in a request does not imply that the same directive is
+ to be given in the response.
+
+ Note that HTTP/1.0 caches might not implement Cache-Control and
+ might only implement Pragma: no-cache (see section 14.32).
+
+ Cache directives MUST be passed through by a proxy or gateway
+ application, regardless of their significance to that application,
+ since the directives might be applicable to all recipients along the
+ request/response chain. It is not possible to specify a cache-
+ directive for a specific cache.
+
+ Cache-Control = "Cache-Control" ":" 1#cache-directive
+
+ cache-directive = cache-request-directive
+ | cache-response-directive
+
+ cache-request-directive =
+ "no-cache" ; Section 14.9.1
+ | "no-store" ; Section 14.9.2
+ | "max-age" "=" delta-seconds ; Section 14.9.3, 14.9.4
+ | "max-stale" [ "=" delta-seconds ] ; Section 14.9.3
+ | "min-fresh" "=" delta-seconds ; Section 14.9.3
+ | "no-transform" ; Section 14.9.5
+ | "only-if-cached" ; Section 14.9.4
+ | cache-extension ; Section 14.9.6
+
+ cache-response-directive =
+ "public" ; Section 14.9.1
+ | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
+ | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
+ | "no-store" ; Section 14.9.2
+ | "no-transform" ; Section 14.9.5
+ | "must-revalidate" ; Section 14.9.4
+ | "proxy-revalidate" ; Section 14.9.4
+ | "max-age" "=" delta-seconds ; Section 14.9.3
+ | "s-maxage" "=" delta-seconds ; Section 14.9.3
+ | cache-extension ; Section 14.9.6
+
+ cache-extension = token [ "=" ( token | quoted-string ) ]
+
+
+
+Fielding, et al. Standards Track [Page 108]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ When a directive appears without any 1#field-name parameter, the
+ directive applies to the entire request or response. When such a
+ directive appears with a 1#field-name parameter, it applies only to
+ the named field or fields, and not to the rest of the request or
+ response. This mechanism supports extensibility; implementations of
+ future versions of the HTTP protocol might apply these directives to
+ header fields not defined in HTTP/1.1.
+
+ The cache-control directives can be broken down into these general
+ categories:
+
+ - Restrictions on what are cacheable; these may only be imposed by
+ the origin server.
+
+ - Restrictions on what may be stored by a cache; these may be
+ imposed by either the origin server or the user agent.
+
+ - Modifications of the basic expiration mechanism; these may be
+ imposed by either the origin server or the user agent.
+
+ - Controls over cache revalidation and reload; these may only be
+ imposed by a user agent.
+
+ - Control over transformation of entities.
+
+ - Extensions to the caching system.
+
+14.9.1 What is Cacheable
+
+ By default, a response is cacheable if the requirements of the
+ request method, request header fields, and the response status
+ indicate that it is cacheable. Section 13.4 summarizes these defaults
+ for cacheability. The following Cache-Control response directives
+ allow an origin server to override the default cacheability of a
+ response:
+
+ public
+ Indicates that the response MAY be cached by any cache, even if it
+ would normally be non-cacheable or cacheable only within a non-
+ shared cache. (See also Authorization, section 14.8, for
+ additional details.)
+
+ private
+ Indicates that all or part of the response message is intended for
+ a single user and MUST NOT be cached by a shared cache. This
+ allows an origin server to state that the specified parts of the
+
+
+
+
+
+Fielding, et al. Standards Track [Page 109]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ response are intended for only one user and are not a valid
+ response for requests by other users. A private (non-shared) cache
+ MAY cache the response.
+
+ Note: This usage of the word private only controls where the
+ response may be cached, and cannot ensure the privacy of the
+ message content.
+
+ no-cache
+ If the no-cache directive does not specify a field-name, then a
+ cache MUST NOT use the response to satisfy a subsequent request
+ without successful revalidation with the origin server. This
+ allows an origin server to prevent caching even by caches that
+ have been configured to return stale responses to client requests.
+
+ If the no-cache directive does specify one or more field-names,
+ then a cache MAY use the response to satisfy a subsequent request,
+ subject to any other restrictions on caching. However, the
+ specified field-name(s) MUST NOT be sent in the response to a
+ subsequent request without successful revalidation with the origin
+ server. This allows an origin server to prevent the re-use of
+ certain header fields in a response, while still allowing caching
+ of the rest of the response.
+
+ Note: Most HTTP/1.0 caches will not recognize or obey this
+ directive.
+
+14.9.2 What May be Stored by Caches
+
+ no-store
+ The purpose of the no-store directive is to prevent the
+ inadvertent release or retention of sensitive information (for
+ example, on backup tapes). The no-store directive applies to the
+ entire message, and MAY be sent either in a response or in a
+ request. If sent in a request, a cache MUST NOT store any part of
+ either this request or any response to it. If sent in a response,
+ a cache MUST NOT store any part of either this response or the
+ request that elicited it. This directive applies to both non-
+ shared and shared caches. "MUST NOT store" in this context means
+ that the cache MUST NOT intentionally store the information in
+ non-volatile storage, and MUST make a best-effort attempt to
+ remove the information from volatile storage as promptly as
+ possible after forwarding it.
+
+ Even when this directive is associated with a response, users
+ might explicitly store such a response outside of the caching
+ system (e.g., with a "Save As" dialog). History buffers MAY store
+ such responses as part of their normal operation.
+
+
+
+Fielding, et al. Standards Track [Page 110]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The purpose of this directive is to meet the stated requirements
+ of certain users and service authors who are concerned about
+ accidental releases of information via unanticipated accesses to
+ cache data structures. While the use of this directive might
+ improve privacy in some cases, we caution that it is NOT in any
+ way a reliable or sufficient mechanism for ensuring privacy. In
+ particular, malicious or compromised caches might not recognize or
+ obey this directive, and communications networks might be
+ vulnerable to eavesdropping.
+
+14.9.3 Modifications of the Basic Expiration Mechanism
+
+ The expiration time of an entity MAY be specified by the origin
+ server using the Expires header (see section 14.21). Alternatively,
+ it MAY be specified using the max-age directive in a response. When
+ the max-age cache-control directive is present in a cached response,
+ the response is stale if its current age is greater than the age
+ value given (in seconds) at the time of a new request for that
+ resource. The max-age directive on a response implies that the
+ response is cacheable (i.e., "public") unless some other, more
+ restrictive cache directive is also present.
+
+ If a response includes both an Expires header and a max-age
+ directive, the max-age directive overrides the Expires header, even
+ if the Expires header is more restrictive. This rule allows an origin
+ server to provide, for a given response, a longer expiration time to
+ an HTTP/1.1 (or later) cache than to an HTTP/1.0 cache. This might be
+ useful if certain HTTP/1.0 caches improperly calculate ages or
+ expiration times, perhaps due to desynchronized clocks.
+
+ Many HTTP/1.0 cache implementations will treat an Expires value that
+ is less than or equal to the response Date value as being equivalent
+ to the Cache-Control response directive "no-cache". If an HTTP/1.1
+ cache receives such a response, and the response does not include a
+ Cache-Control header field, it SHOULD consider the response to be
+ non-cacheable in order to retain compatibility with HTTP/1.0 servers.
+
+ Note: An origin server might wish to use a relatively new HTTP
+ cache control feature, such as the "private" directive, on a
+ network including older caches that do not understand that
+ feature. The origin server will need to combine the new feature
+ with an Expires field whose value is less than or equal to the
+ Date value. This will prevent older caches from improperly
+ caching the response.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 111]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ s-maxage
+ If a response includes an s-maxage directive, then for a shared
+ cache (but not for a private cache), the maximum age specified by
+ this directive overrides the maximum age specified by either the
+ max-age directive or the Expires header. The s-maxage directive
+ also implies the semantics of the proxy-revalidate directive (see
+ section 14.9.4), i.e., that the shared cache must not use the
+ entry after it becomes stale to respond to a subsequent request
+ without first revalidating it with the origin server. The s-
+ maxage directive is always ignored by a private cache.
+
+ Note that most older caches, not compliant with this specification,
+ do not implement any cache-control directives. An origin server
+ wishing to use a cache-control directive that restricts, but does not
+ prevent, caching by an HTTP/1.1-compliant cache MAY exploit the
+ requirement that the max-age directive overrides the Expires header,
+ and the fact that pre-HTTP/1.1-compliant caches do not observe the
+ max-age directive.
+
+ Other directives allow a user agent to modify the basic expiration
+ mechanism. These directives MAY be specified on a request:
+
+ max-age
+ Indicates that the client is willing to accept a response whose
+ age is no greater than the specified time in seconds. Unless max-
+ stale directive is also included, the client is not willing to
+ accept a stale response.
+
+ min-fresh
+ Indicates that the client is willing to accept a response whose
+ freshness lifetime is no less than its current age plus the
+ specified time in seconds. That is, the client wants a response
+ that will still be fresh for at least the specified number of
+ seconds.
+
+ max-stale
+ Indicates that the client is willing to accept a response that has
+ exceeded its expiration time. If max-stale is assigned a value,
+ then the client is willing to accept a response that has exceeded
+ its expiration time by no more than the specified number of
+ seconds. If no value is assigned to max-stale, then the client is
+ willing to accept a stale response of any age.
+
+ If a cache returns a stale response, either because of a max-stale
+ directive on a request, or because the cache is configured to
+ override the expiration time of a response, the cache MUST attach a
+ Warning header to the stale response, using Warning 110 (Response is
+ stale).
+
+
+
+Fielding, et al. Standards Track [Page 112]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A cache MAY be configured to return stale responses without
+ validation, but only if this does not conflict with any "MUST"-level
+ requirements concerning cache validation (e.g., a "must-revalidate"
+ cache-control directive).
+
+ If both the new request and the cached entry include "max-age"
+ directives, then the lesser of the two values is used for determining
+ the freshness of the cached entry for that request.
+
+14.9.4 Cache Revalidation and Reload Controls
+
+ Sometimes a user agent might want or need to insist that a cache
+ revalidate its cache entry with the origin server (and not just with
+ the next cache along the path to the origin server), or to reload its
+ cache entry from the origin server. End-to-end revalidation might be
+ necessary if either the cache or the origin server has overestimated
+ the expiration time of the cached response. End-to-end reload may be
+ necessary if the cache entry has become corrupted for some reason.
+
+ End-to-end revalidation may be requested either when the client does
+ not have its own local cached copy, in which case we call it
+ "unspecified end-to-end revalidation", or when the client does have a
+ local cached copy, in which case we call it "specific end-to-end
+ revalidation."
+
+ The client can specify these three kinds of action using Cache-
+ Control request directives:
+
+ End-to-end reload
+ The request includes a "no-cache" cache-control directive or, for
+ compatibility with HTTP/1.0 clients, "Pragma: no-cache". Field
+ names MUST NOT be included with the no-cache directive in a
+ request. The server MUST NOT use a cached copy when responding to
+ such a request.
+
+ Specific end-to-end revalidation
+ The request includes a "max-age=0" cache-control directive, which
+ forces each cache along the path to the origin server to
+ revalidate its own entry, if any, with the next cache or server.
+ The initial request includes a cache-validating conditional with
+ the client's current validator.
+
+ Unspecified end-to-end revalidation
+ The request includes "max-age=0" cache-control directive, which
+ forces each cache along the path to the origin server to
+ revalidate its own entry, if any, with the next cache or server.
+ The initial request does not include a cache-validating
+
+
+
+
+Fielding, et al. Standards Track [Page 113]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ conditional; the first cache along the path (if any) that holds a
+ cache entry for this resource includes a cache-validating
+ conditional with its current validator.
+
+ max-age
+ When an intermediate cache is forced, by means of a max-age=0
+ directive, to revalidate its own cache entry, and the client has
+ supplied its own validator in the request, the supplied validator
+ might differ from the validator currently stored with the cache
+ entry. In this case, the cache MAY use either validator in making
+ its own request without affecting semantic transparency.
+
+ However, the choice of validator might affect performance. The
+ best approach is for the intermediate cache to use its own
+ validator when making its request. If the server replies with 304
+ (Not Modified), then the cache can return its now validated copy
+ to the client with a 200 (OK) response. If the server replies with
+ a new entity and cache validator, however, the intermediate cache
+ can compare the returned validator with the one provided in the
+ client's request, using the strong comparison function. If the
+ client's validator is equal to the origin server's, then the
+ intermediate cache simply returns 304 (Not Modified). Otherwise,
+ it returns the new entity with a 200 (OK) response.
+
+ If a request includes the no-cache directive, it SHOULD NOT
+ include min-fresh, max-stale, or max-age.
+
+ only-if-cached
+ In some cases, such as times of extremely poor network
+ connectivity, a client may want a cache to return only those
+ responses that it currently has stored, and not to reload or
+ revalidate with the origin server. To do this, the client may
+ include the only-if-cached directive in a request. If it receives
+ this directive, a cache SHOULD either respond using a cached entry
+ that is consistent with the other constraints of the request, or
+ respond with a 504 (Gateway Timeout) status. However, if a group
+ of caches is being operated as a unified system with good internal
+ connectivity, such a request MAY be forwarded within that group of
+ caches.
+
+ must-revalidate
+ Because a cache MAY be configured to ignore a server's specified
+ expiration time, and because a client request MAY include a max-
+ stale directive (which has a similar effect), the protocol also
+ includes a mechanism for the origin server to require revalidation
+ of a cache entry on any subsequent use. When the must-revalidate
+ directive is present in a response received by a cache, that cache
+ MUST NOT use the entry after it becomes stale to respond to a
+
+
+
+Fielding, et al. Standards Track [Page 114]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ subsequent request without first revalidating it with the origin
+ server. (I.e., the cache MUST do an end-to-end revalidation every
+ time, if, based solely on the origin server's Expires or max-age
+ value, the cached response is stale.)
+
+ The must-revalidate directive is necessary to support reliable
+ operation for certain protocol features. In all circumstances an
+ HTTP/1.1 cache MUST obey the must-revalidate directive; in
+ particular, if the cache cannot reach the origin server for any
+ reason, it MUST generate a 504 (Gateway Timeout) response.
+
+ Servers SHOULD send the must-revalidate directive if and only if
+ failure to revalidate a request on the entity could result in
+ incorrect operation, such as a silently unexecuted financial
+ transaction. Recipients MUST NOT take any automated action that
+ violates this directive, and MUST NOT automatically provide an
+ unvalidated copy of the entity if revalidation fails.
+
+ Although this is not recommended, user agents operating under
+ severe connectivity constraints MAY violate this directive but, if
+ so, MUST explicitly warn the user that an unvalidated response has
+ been provided. The warning MUST be provided on each unvalidated
+ access, and SHOULD require explicit user confirmation.
+
+ proxy-revalidate
+ The proxy-revalidate directive has the same meaning as the must-
+ revalidate directive, except that it does not apply to non-shared
+ user agent caches. It can be used on a response to an
+ authenticated request to permit the user's cache to store and
+ later return the response without needing to revalidate it (since
+ it has already been authenticated once by that user), while still
+ requiring proxies that service many users to revalidate each time
+ (in order to make sure that each user has been authenticated).
+ Note that such authenticated responses also need the public cache
+ control directive in order to allow them to be cached at all.
+
+14.9.5 No-Transform Directive
+
+ no-transform
+ Implementors of intermediate caches (proxies) have found it useful
+ to convert the media type of certain entity bodies. A non-
+ transparent proxy might, for example, convert between image
+ formats in order to save cache space or to reduce the amount of
+ traffic on a slow link.
+
+ Serious operational problems occur, however, when these
+ transformations are applied to entity bodies intended for certain
+ kinds of applications. For example, applications for medical
+
+
+
+Fielding, et al. Standards Track [Page 115]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ imaging, scientific data analysis and those using end-to-end
+ authentication, all depend on receiving an entity body that is bit
+ for bit identical to the original entity-body.
+
+ Therefore, if a message includes the no-transform directive, an
+ intermediate cache or proxy MUST NOT change those headers that are
+ listed in section 13.5.2 as being subject to the no-transform
+ directive. This implies that the cache or proxy MUST NOT change
+ any aspect of the entity-body that is specified by these headers,
+ including the value of the entity-body itself.
+
+14.9.6 Cache Control Extensions
+
+ The Cache-Control header field can be extended through the use of one
+ or more cache-extension tokens, each with an optional assigned value.
+ Informational extensions (those which do not require a change in
+ cache behavior) MAY be added without changing the semantics of other
+ directives. Behavioral extensions are designed to work by acting as
+ modifiers to the existing base of cache directives. Both the new
+ directive and the standard directive are supplied, such that
+ applications which do not understand the new directive will default
+ to the behavior specified by the standard directive, and those that
+ understand the new directive will recognize it as modifying the
+ requirements associated with the standard directive. In this way,
+ extensions to the cache-control directives can be made without
+ requiring changes to the base protocol.
+
+ This extension mechanism depends on an HTTP cache obeying all of the
+ cache-control directives defined for its native HTTP-version, obeying
+ certain extensions, and ignoring all directives that it does not
+ understand.
+
+ For example, consider a hypothetical new response directive called
+ community which acts as a modifier to the private directive. We
+ define this new directive to mean that, in addition to any non-shared
+ cache, any cache which is shared only by members of the community
+ named within its value may cache the response. An origin server
+ wishing to allow the UCI community to use an otherwise private
+ response in their shared cache(s) could do so by including
+
+ Cache-Control: private, community="UCI"
+
+ A cache seeing this header field will act correctly even if the cache
+ does not understand the community cache-extension, since it will also
+ see and understand the private directive and thus default to the safe
+ behavior.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 116]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Unrecognized cache-directives MUST be ignored; it is assumed that any
+ cache-directive likely to be unrecognized by an HTTP/1.1 cache will
+ be combined with standard directives (or the response's default
+ cacheability) such that the cache behavior will remain minimally
+ correct even if the cache does not understand the extension(s).
+
+14.10 Connection
+
+ The Connection general-header field allows the sender to specify
+ options that are desired for that particular connection and MUST NOT
+ be communicated by proxies over further connections.
+
+ The Connection header has the following grammar:
+
+ Connection = "Connection" ":" 1#(connection-token)
+ connection-token = token
+
+ HTTP/1.1 proxies MUST parse the Connection header field before a
+ message is forwarded and, for each connection-token in this field,
+ remove any header field(s) from the message with the same name as the
+ connection-token. Connection options are signaled by the presence of
+ a connection-token in the Connection header field, not by any
+ corresponding additional header field(s), since the additional header
+ field may not be sent if there are no parameters associated with that
+ connection option.
+
+ Message headers listed in the Connection header MUST NOT include
+ end-to-end headers, such as Cache-Control.
+
+ HTTP/1.1 defines the "close" connection option for the sender to
+ signal that the connection will be closed after completion of the
+ response. For example,
+
+ Connection: close
+
+ in either the request or the response header fields indicates that
+ the connection SHOULD NOT be considered `persistent' (section 8.1)
+ after the current request/response is complete.
+
+ HTTP/1.1 applications that do not support persistent connections MUST
+ include the "close" connection option in every message.
+
+[[ Should say: ]]
+[[ An HTTP/1.1 client that does not support persistent connections ]]
+[[ MUST include the "close" connection option in every request message. ]]
+[[ ]]
+[[ An HTTP/1.1 server that does not support persistent connections ]]
+[[ MUST include the "close" connection option in every response ]]
+[[ message that does not have a 1xx (informational) status code. ]]
+
+ A system receiving an HTTP/1.0 (or lower-version) message that
+ includes a Connection header MUST, for each connection-token in this
+ field, remove and ignore any header field(s) from the message with
+ the same name as the connection-token. This protects against mistaken
+ forwarding of such header fields by pre-HTTP/1.1 proxies. See section
+ 19.6.2.
+
+
+
+Fielding, et al. Standards Track [Page 117]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.11 Content-Encoding
+
+ The Content-Encoding entity-header field is used as a modifier to the
+ media-type. When present, its value indicates what additional content
+ codings have been applied to the entity-body, and thus what decoding
+ mechanisms must be applied in order to obtain the media-type
+ referenced by the Content-Type header field. Content-Encoding is
+ primarily used to allow a document to be compressed without losing
+ the identity of its underlying media type.
+
+ Content-Encoding = "Content-Encoding" ":" 1#content-coding
+
+ Content codings are defined in section 3.5. An example of its use is
+
+ Content-Encoding: gzip
+
+ The content-coding is a characteristic of the entity identified by
+ the Request-URI. Typically, the entity-body is stored with this
+ encoding and is only decoded before rendering or analogous usage.
+ However, a non-transparent proxy MAY modify the content-coding if the
+ new coding is known to be acceptable to the recipient, unless the
+ "no-transform" cache-control directive is present in the message.
+
+ If the content-coding of an entity is not "identity", then the
+ response MUST include a Content-Encoding entity-header (section
+ 14.11) that lists the non-identity content-coding(s) used.
+
+ If the content-coding of an entity in a request message is not
+ acceptable to the origin server, the server SHOULD respond with a
+ status code of 415 (Unsupported Media Type).
+
+ If multiple encodings have been applied to an entity, the content
+ codings MUST be listed in the order in which they were applied.
+ Additional information about the encoding parameters MAY be provided
+ by other entity-header fields not defined by this specification.
+
+14.12 Content-Language
+
+ The Content-Language entity-header field describes the natural
+ language(s) of the intended audience for the enclosed entity. Note
+ that this might not be equivalent to all the languages used within
+ the entity-body.
+
+ Content-Language = "Content-Language" ":" 1#language-tag
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 118]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Language tags are defined in section 3.10. The primary purpose of
+ Content-Language is to allow a user to identify and differentiate
+ entities according to the user's own preferred language. Thus, if the
+ body content is intended only for a Danish-literate audience, the
+ appropriate field is
+
+ Content-Language: da
+
+ If no Content-Language is specified, the default is that the content
+ is intended for all language audiences. This might mean that the
+ sender does not consider it to be specific to any natural language,
+ or that the sender does not know for which language it is intended.
+
+ Multiple languages MAY be listed for content that is intended for
+ multiple audiences. For example, a rendition of the "Treaty of
+ Waitangi," presented simultaneously in the original Maori and English
+ versions, would call for
+
+ Content-Language: mi, en
+
+ However, just because multiple languages are present within an entity
+ does not mean that it is intended for multiple linguistic audiences.
+ An example would be a beginner's language primer, such as "A First
+ Lesson in Latin," which is clearly intended to be used by an
+ English-literate audience. In this case, the Content-Language would
+ properly only include "en".
+
+ Content-Language MAY be applied to any media type -- it is not
+ limited to textual documents.
+
+14.13 Content-Length
+
+ The Content-Length entity-header field indicates the size of the
+ entity-body, in decimal number of OCTETs, sent to the recipient or,
+ in the case of the HEAD method, the size of the entity-body that
+ would have been sent had the request been a GET.
+
+ Content-Length = "Content-Length" ":" 1*DIGIT
+
+ An example is
+
+ Content-Length: 3495
+
+ Applications SHOULD use this field to indicate the transfer-length of
+ the message-body, unless this is prohibited by the rules in section
+ 4.4.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 119]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Any Content-Length greater than or equal to zero is a valid value.
+ Section 4.4 describes how to determine the length of a message-body
+ if a Content-Length is not given.
+
+ Note that the meaning of this field is significantly different from
+ the corresponding definition in MIME, where it is an optional field
+ used within the "message/external-body" content-type. In HTTP, it
+ SHOULD be sent whenever the message's length can be determined prior
+ to being transferred, unless this is prohibited by the rules in
+ section 4.4.
+
+14.14 Content-Location
+
+ The Content-Location entity-header field MAY be used to supply the
+ resource location for the entity enclosed in the message when that
+ entity is accessible from a location separate from the requested
+ resource's URI. A server SHOULD provide a Content-Location for the
+ variant corresponding to the response entity; especially in the case
+ where a resource has multiple entities associated with it, and those
+ entities actually have separate locations by which they might be
+ individually accessed, the server SHOULD provide a Content-Location
+ for the particular variant which is returned.
+
+ Content-Location = "Content-Location" ":"
+ ( absoluteURI | relativeURI )
+
+ The value of Content-Location also defines the base URI for the
+ entity.
+
+ The Content-Location value is not a replacement for the original
+ requested URI; it is only a statement of the location of the resource
+ corresponding to this particular entity at the time of the request.
+ Future requests MAY specify the Content-Location URI as the request-
+ URI if the desire is to identify the source of that particular
+ entity.
+
+ A cache cannot assume that an entity with a Content-Location
+ different from the URI used to retrieve it can be used to respond to
+ later requests on that Content-Location URI. However, the Content-
+ Location can be used to differentiate between multiple entities
+ retrieved from a single requested resource, as described in section
+ 13.6.
+
+ If the Content-Location is a relative URI, the relative URI is
+ interpreted relative to the Request-URI.
+
+ The meaning of the Content-Location header in PUT or POST requests is
+ undefined; servers are free to ignore it in those cases.
+
+
+
+Fielding, et al. Standards Track [Page 120]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.15 Content-MD5
+
+ The Content-MD5 entity-header field, as defined in RFC 1864 [23], is
+ an MD5 digest of the entity-body for the purpose of providing an
+ end-to-end message integrity check (MIC) of the entity-body. (Note: a
+ MIC is good for detecting accidental modification of the entity-body
+ in transit, but is not proof against malicious attacks.)
+
+ Content-MD5 = "Content-MD5" ":" md5-digest
+ md5-digest = <base64 of 128 bit MD5 digest as per RFC 1864>
+
+ The Content-MD5 header field MAY be generated by an origin server or
+ client to function as an integrity check of the entity-body. Only
+ origin servers or clients MAY generate the Content-MD5 header field;
+ proxies and gateways MUST NOT generate it, as this would defeat its
+ value as an end-to-end integrity check. Any recipient of the entity-
+ body, including gateways and proxies, MAY check that the digest value
+ in this header field matches that of the entity-body as received.
+
+ The MD5 digest is computed based on the content of the entity-body,
+ including any content-coding that has been applied, but not including
+ any transfer-encoding applied to the message-body. If the message is
+ received with a transfer-encoding, that encoding MUST be removed
+ prior to checking the Content-MD5 value against the received entity.
+
+ This has the result that the digest is computed on the octets of the
+ entity-body exactly as, and in the order that, they would be sent if
+ no transfer-encoding were being applied.
+
+ HTTP extends RFC 1864 to permit the digest to be computed for MIME
+ composite media-types (e.g., multipart/* and message/rfc822), but
+ this does not change how the digest is computed as defined in the
+ preceding paragraph.
+
+ There are several consequences of this. The entity-body for composite
+ types MAY contain many body-parts, each with its own MIME and HTTP
+ headers (including Content-MD5, Content-Transfer-Encoding, and
+ Content-Encoding headers). If a body-part has a Content-Transfer-
+ Encoding or Content-Encoding header, it is assumed that the content
+ of the body-part has had the encoding applied, and the body-part is
+ included in the Content-MD5 digest as is -- i.e., after the
+ application. The Transfer-Encoding header field is not allowed within
+ body-parts.
+
+ Conversion of all line breaks to CRLF MUST NOT be done before
+ computing or checking the digest: the line break convention used in
+ the text actually transmitted MUST be left unaltered when computing
+ the digest.
+
+
+
+Fielding, et al. Standards Track [Page 121]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Note: while the definition of Content-MD5 is exactly the same for
+ HTTP as in RFC 1864 for MIME entity-bodies, there are several ways
+ in which the application of Content-MD5 to HTTP entity-bodies
+ differs from its application to MIME entity-bodies. One is that
+ HTTP, unlike MIME, does not use Content-Transfer-Encoding, and
+ does use Transfer-Encoding and Content-Encoding. Another is that
+ HTTP more frequently uses binary content types than MIME, so it is
+ worth noting that, in such cases, the byte order used to compute
+ the digest is the transmission byte order defined for the type.
+ Lastly, HTTP allows transmission of text types with any of several
+ line break conventions and not just the canonical form using CRLF.
+
+14.16 Content-Range
+
+ The Content-Range entity-header is sent with a partial entity-body to
+ specify where in the full entity-body the partial body should be
+ applied. Range units are defined in section 3.12.
+
+ Content-Range = "Content-Range" ":" content-range-spec
+
+ content-range-spec = byte-content-range-spec
+ byte-content-range-spec = bytes-unit SP
+ byte-range-resp-spec "/"
+ ( instance-length | "*" )
+
+ byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
+ | "*"
+ instance-length = 1*DIGIT
+
+ The header SHOULD indicate the total length of the full entity-body,
+ unless this length is unknown or difficult to determine. The asterisk
+ "*" character means that the instance-length is unknown at the time
+ when the response was generated.
+
+ Unlike byte-ranges-specifier values (see section 14.35.1), a byte-
+ range-resp-spec MUST only specify one range, and MUST contain
+ absolute byte positions for both the first and last byte of the
+ range.
+
+ A byte-content-range-spec with a byte-range-resp-spec whose last-
+ byte-pos value is less than its first-byte-pos value, or whose
+ instance-length value is less than or equal to its last-byte-pos
+ value, is invalid. The recipient of an invalid byte-content-range-
+ spec MUST ignore it and any content transferred along with it.
+
+ A server sending a response with status code 416 (Requested range not
+ satisfiable) SHOULD include a Content-Range field with a byte-range-
+ resp-spec of "*". The instance-length specifies the current length of
+
+
+
+Fielding, et al. Standards Track [Page 122]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ the selected resource. A response with status code 206 (Partial
+ Content) MUST NOT include a Content-Range field with a byte-range-
+ resp-spec of "*".
+
+ Examples of byte-content-range-spec values, assuming that the entity
+ contains a total of 1234 bytes:
+
+ . The first 500 bytes:
+ bytes 0-499/1234
+
+ . The second 500 bytes:
+ bytes 500-999/1234
+
+ . All except for the first 500 bytes:
+ bytes 500-1233/1234
+
+ . The last 500 bytes:
+ bytes 734-1233/1234
+
+ When an HTTP message includes the content of a single range (for
+ example, a response to a request for a single range, or to a request
+ for a set of ranges that overlap without any holes), this content is
+ transmitted with a Content-Range header, and a Content-Length header
+ showing the number of bytes actually transferred. For example,
+
+ HTTP/1.1 206 Partial content
+ Date: Wed, 15 Nov 1995 06:25:24 GMT
+ Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
+ Content-Range: bytes 21010-47021/47022
+ Content-Length: 26012
+ Content-Type: image/gif
+
+ When an HTTP message includes the content of multiple ranges (for
+ example, a response to a request for multiple non-overlapping
+ ranges), these are transmitted as a multipart message. The multipart
+ media type used for this purpose is "multipart/byteranges" as defined
+ in appendix 19.2. See appendix 19.6.3 for a compatibility issue.
+
+ A response to a request for a single range MUST NOT be sent using the
+ multipart/byteranges media type. A response to a request for
+ multiple ranges, whose result is a single range, MAY be sent as a
+ multipart/byteranges media type with one part. A client that cannot
+ decode a multipart/byteranges message MUST NOT ask for multiple
+ byte-ranges in a single request.
+
+ When a client requests multiple byte-ranges in one request, the
+ server SHOULD return them in the order that they appeared in the
+ request.
+
+
+
+Fielding, et al. Standards Track [Page 123]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the server ignores a byte-range-spec because it is syntactically
+ invalid, the server SHOULD treat the request as if the invalid Range
+ header field did not exist. (Normally, this means return a 200
+ response containing the full entity).
+
+ If the server receives a request (other than one including an If-
+ Range request-header field) with an unsatisfiable Range request-
+ header field (that is, all of whose byte-range-spec values have a
+ first-byte-pos value greater than the current length of the selected
+ resource), it SHOULD return a response code of 416 (Requested range
+ not satisfiable) (section 10.4.17).
+
+ Note: clients cannot depend on servers to send a 416 (Requested
+ range not satisfiable) response instead of a 200 (OK) response for
+ an unsatisfiable Range request-header, since not all servers
+ implement this request-header.
+
+14.17 Content-Type
+
+ The Content-Type entity-header field indicates the media type of the
+ entity-body sent to the recipient or, in the case of the HEAD method,
+ the media type that would have been sent had the request been a GET.
+
+ Content-Type = "Content-Type" ":" media-type
+
+ Media types are defined in section 3.7. An example of the field is
+
+ Content-Type: text/html; charset=ISO-8859-4
+
+ Further discussion of methods for identifying the media type of an
+ entity is provided in section 7.2.1.
+
+14.18 Date
+
+ The Date general-header field represents the date and time at which
+ the message was originated, having the same semantics as orig-date in
+ RFC 822. The field value is an HTTP-date, as described in section
+ 3.3.1; it MUST be sent in RFC 1123 [8]-date format.
+
+ Date = "Date" ":" HTTP-date
+
+ An example is
+
+ Date: Tue, 15 Nov 1994 08:12:31 GMT
+
+ Origin servers MUST include a Date header field in all responses,
+ except in these cases:
+
+
+
+
+Fielding, et al. Standards Track [Page 124]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 1. If the response status code is 100 (Continue) or 101 (Switching
+ Protocols), the response MAY include a Date header field, at
+ the server's option.
+
+ 2. If the response status code conveys a server error, e.g. 500
+ (Internal Server Error) or 503 (Service Unavailable), and it is
+ inconvenient or impossible to generate a valid Date.
+
+ 3. If the server does not have a clock that can provide a
+ reasonable approximation of the current time, its responses
+ MUST NOT include a Date header field. In this case, the rules
+ in section 14.18.1 MUST be followed.
+
+ A received message that does not have a Date header field MUST be
+ assigned one by the recipient if the message will be cached by that
+ recipient or gatewayed via a protocol which requires a Date. An HTTP
+ implementation without a clock MUST NOT cache responses without
+ revalidating them on every use. An HTTP cache, especially a shared
+ cache, SHOULD use a mechanism, such as NTP [28], to synchronize its
+ clock with a reliable external standard.
+
+ Clients SHOULD only send a Date header field in messages that include
+ an entity-body, as in the case of the PUT and POST requests, and even
+ then it is optional. A client without a clock MUST NOT send a Date
+ header field in a request.
+
+ The HTTP-date sent in a Date header SHOULD NOT represent a date and
+ time subsequent to the generation of the message. It SHOULD represent
+ the best available approximation of the date and time of message
+ generation, unless the implementation has no means of generating a
+ reasonably accurate date and time. In theory, the date ought to
+ represent the moment just before the entity is generated. In
+ practice, the date can be generated at any time during the message
+ origination without affecting its semantic value.
+
+14.18.1 Clockless Origin Server Operation
+
+ Some origin server implementations might not have a clock available.
+ An origin server without a clock MUST NOT assign Expires or Last-
+ Modified values to a response, unless these values were associated
+ with the resource by a system or user with a reliable clock. It MAY
+ assign an Expires value that is known, at or before server
+ configuration time, to be in the past (this allows "pre-expiration"
+ of responses without storing separate Expires values for each
+ resource).
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 125]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.19 ETag
+
+ The ETag response-header field provides the current value of the
+ entity tag for the requested variant. The headers used with entity
+ tags are described in sections 14.24, 14.26 and 14.44. The entity tag
+ MAY be used for comparison with other entities from the same resource
+ (see section 13.3.3).
+
+ ETag = "ETag" ":" entity-tag
+
+ Examples:
+
+ ETag: "xyzzy"
+ ETag: W/"xyzzy"
+ ETag: ""
+
+14.20 Expect
+
+ The Expect request-header field is used to indicate that particular
+ server behaviors are required by the client.
+
+ Expect = "Expect" ":" 1#expectation
+
+ expectation = "100-continue" | expectation-extension
+ expectation-extension = token [ "=" ( token | quoted-string )
+ *expect-params ]
+ expect-params = ";" token [ "=" ( token | quoted-string ) ]
+
+
+ A server that does not understand or is unable to comply with any of
+ the expectation values in the Expect field of a request MUST respond
+ with appropriate error status. The server MUST respond with a 417
+ (Expectation Failed) status if any of the expectations cannot be met
+ or, if there are other problems with the request, some other 4xx
+ status.
+
+ This header field is defined with extensible syntax to allow for
+ future extensions. If a server receives a request containing an
+ Expect field that includes an expectation-extension that it does not
+ support, it MUST respond with a 417 (Expectation Failed) status.
+
+ Comparison of expectation values is case-insensitive for unquoted
+ tokens (including the 100-continue token), and is case-sensitive for
+ quoted-string expectation-extensions.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 126]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The Expect mechanism is hop-by-hop: that is, an HTTP/1.1 proxy MUST
+ return a 417 (Expectation Failed) status if it receives a request
+ with an expectation that it cannot meet. However, the Expect
+ request-header itself is end-to-end; it MUST be forwarded if the
+ request is forwarded.
+
+ Many older HTTP/1.0 and HTTP/1.1 applications do not understand the
+ Expect header.
+
+ See section 8.2.3 for the use of the 100 (continue) status.
+
+14.21 Expires
+
+ The Expires entity-header field gives the date/time after which the
+ response is considered stale. A stale cache entry may not normally be
+ returned by a cache (either a proxy cache or a user agent cache)
+ unless it is first validated with the origin server (or with an
+ intermediate cache that has a fresh copy of the entity). See section
+ 13.2 for further discussion of the expiration model.
+
+ The presence of an Expires field does not imply that the original
+ resource will change or cease to exist at, before, or after that
+ time.
+
+ The format is an absolute date and time as defined by HTTP-date in
+ section 3.3.1; it MUST be in RFC 1123 date format:
+
+ Expires = "Expires" ":" HTTP-date
+
+ An example of its use is
+
+ Expires: Thu, 01 Dec 1994 16:00:00 GMT
+
+ Note: if a response includes a Cache-Control field with the max-
+ age directive (see section 14.9.3), that directive overrides the
+ Expires field.
+
+ HTTP/1.1 clients and caches MUST treat other invalid date formats,
+ especially including the value "0", as in the past (i.e., "already
+ expired").
+
+ To mark a response as "already expired," an origin server sends an
+ Expires date that is equal to the Date header value. (See the rules
+ for expiration calculations in section 13.2.4.)
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 127]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ To mark a response as "never expires," an origin server sends an
+ Expires date approximately one year from the time the response is
+ sent. HTTP/1.1 servers SHOULD NOT send Expires dates more than one
+ year in the future.
+
+ The presence of an Expires header field with a date value of some
+ time in the future on a response that otherwise would by default be
+ non-cacheable indicates that the response is cacheable, unless
+ indicated otherwise by a Cache-Control header field (section 14.9).
+
+14.22 From
+
+ The From request-header field, if given, SHOULD contain an Internet
+ e-mail address for the human user who controls the requesting user
+ agent. The address SHOULD be machine-usable, as defined by "mailbox"
+ in RFC 822 [9] as updated by RFC 1123 [8]:
+
+ From = "From" ":" mailbox
+
+ An example is:
+
+ From: webmaster@w3.org
+
+ This header field MAY be used for logging purposes and as a means for
+ identifying the source of invalid or unwanted requests. It SHOULD NOT
+ be used as an insecure form of access protection. The interpretation
+ of this field is that the request is being performed on behalf of the
+ person given, who accepts responsibility for the method performed. In
+ particular, robot agents SHOULD include this header so that the
+ person responsible for running the robot can be contacted if problems
+ occur on the receiving end.
+
+ The Internet e-mail address in this field MAY be separate from the
+ Internet host which issued the request. For example, when a request
+ is passed through a proxy the original issuer's address SHOULD be
+ used.
+
+ The client SHOULD NOT send the From header field without the user's
+ approval, as it might conflict with the user's privacy interests or
+ their site's security policy. It is strongly recommended that the
+ user be able to disable, enable, and modify the value of this field
+ at any time prior to a request.
+
+14.23 Host
+
+ The Host request-header field specifies the Internet host and port
+ number of the resource being requested, as obtained from the original
+ URI given by the user or referring resource (generally an HTTP URL,
+
+
+
+Fielding, et al. Standards Track [Page 128]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ as described in section 3.2.2). The Host field value MUST represent
+ the naming authority of the origin server or gateway given by the
+ original URL. This allows the origin server or gateway to
+ differentiate between internally-ambiguous URLs, such as the root "/"
+ URL of a server for multiple host names on a single IP address.
+
+ Host = "Host" ":" host [ ":" port ] ; Section 3.2.2
+
+ A "host" without any trailing port information implies the default
+ port for the service requested (e.g., "80" for an HTTP URL). For
+ example, a request on the origin server for
+ <http://www.w3.org/pub/WWW/> would properly include:
+
+ GET /pub/WWW/ HTTP/1.1
+ Host: www.w3.org
+
+ A client MUST include a Host header field in all HTTP/1.1 request
+ messages . If the requested URI does not include an Internet host
+ name for the service being requested, then the Host header field MUST
+ be given with an empty value. An HTTP/1.1 proxy MUST ensure that any
+ request message it forwards does contain an appropriate Host header
+ field that identifies the service being requested by the proxy. All
+ Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad Request)
+ status code to any HTTP/1.1 request message which lacks a Host header
+ field.
+
+ See sections 5.2 and 19.6.1.1 for other requirements relating to
+ Host.
+
+14.24 If-Match
+
+ The If-Match request-header field is used with a method to make it
+ conditional. A client that has one or more entities previously
+ obtained from the resource can verify that one of those entities is
+ current by including a list of their associated entity tags in the
+ If-Match header field. Entity tags are defined in section 3.11. The
+ purpose of this feature is to allow efficient updates of cached
+ information with a minimum amount of transaction overhead. It is also
+ used, on updating requests, to prevent inadvertent modification of
+ the wrong version of a resource. As a special case, the value "*"
+ matches any current entity of the resource.
+
+ If-Match = "If-Match" ":" ( "*" | 1#entity-tag )
+
+ If any of the entity tags match the entity tag of the entity that
+ would have been returned in the response to a similar GET request
+ (without the If-Match header) on that resource, or if "*" is given
+
+
+
+
+Fielding, et al. Standards Track [Page 129]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ and any current entity exists for that resource, then the server MAY
+ perform the requested method as if the If-Match header field did not
+ exist.
+
+ A server MUST use the strong comparison function (see section 13.3.3)
+ to compare the entity tags in If-Match.
+
+ If none of the entity tags match, or if "*" is given and no current
+ entity exists, the server MUST NOT perform the requested method, and
+ MUST return a 412 (Precondition Failed) response. This behavior is
+ most useful when the client wants to prevent an updating method, such
+ as PUT, from modifying a resource that has changed since the client
+ last retrieved it.
+
+ If the request would, without the If-Match header field, result in
+ anything other than a 2xx or 412 status, then the If-Match header
+ MUST be ignored.
+
+ The meaning of "If-Match: *" is that the method SHOULD be performed
+ if the representation selected by the origin server (or by a cache,
+ possibly using the Vary mechanism, see section 14.44) exists, and
+ MUST NOT be performed if the representation does not exist.
+
+ A request intended to update a resource (e.g., a PUT) MAY include an
+ If-Match header field to signal that the request method MUST NOT be
+ applied if the entity corresponding to the If-Match value (a single
+ entity tag) is no longer a representation of that resource. This
+ allows the user to indicate that they do not wish the request to be
+ successful if the resource has been changed without their knowledge.
+ Examples:
+
+ If-Match: "xyzzy"
+ If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
+ If-Match: *
+
+ The result of a request having both an If-Match header field and
+ either an If-None-Match or an If-Modified-Since header fields is
+ undefined by this specification.
+
+14.25 If-Modified-Since
+
+ The If-Modified-Since request-header field is used with a method to
+ make it conditional: if the requested variant has not been modified
+ since the time specified in this field, an entity will not be
+ returned from the server; instead, a 304 (not modified) response will
+ be returned without any message-body.
+
+ If-Modified-Since = "If-Modified-Since" ":" HTTP-date
+
+
+
+Fielding, et al. Standards Track [Page 130]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ An example of the field is:
+
+ If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+
+ A GET method with an If-Modified-Since header and no Range header
+ requests that the identified entity be transferred only if it has
+ been modified since the date given by the If-Modified-Since header.
+ The algorithm for determining this includes the following cases:
+
+ a) If the request would normally result in anything other than a
+ 200 (OK) status, or if the passed If-Modified-Since date is
+ invalid, the response is exactly the same as for a normal GET.
+ A date which is later than the server's current time is
+ invalid.
+
+ b) If the variant has been modified since the If-Modified-Since
+ date, the response is exactly the same as for a normal GET.
+
+ c) If the variant has not been modified since a valid If-
+ Modified-Since date, the server SHOULD return a 304 (Not
+ Modified) response.
+
+ The purpose of this feature is to allow efficient updates of cached
+ information with a minimum amount of transaction overhead.
+
+ Note: The Range request-header field modifies the meaning of If-
+ Modified-Since; see section 14.35 for full details.
+
+ Note: If-Modified-Since times are interpreted by the server, whose
+ clock might not be synchronized with the client.
+
+ Note: When handling an If-Modified-Since header field, some
+ servers will use an exact date comparison function, rather than a
+ less-than function, for deciding whether to send a 304 (Not
+ Modified) response. To get best results when sending an If-
+ Modified-Since header field for cache validation, clients are
+ advised to use the exact date string received in a previous Last-
+ Modified header field whenever possible.
+
+ Note: If a client uses an arbitrary date in the If-Modified-Since
+ header instead of a date taken from the Last-Modified header for
+ the same request, the client should be aware of the fact that this
+ date is interpreted in the server's understanding of time. The
+ client should consider unsynchronized clocks and rounding problems
+ due to the different encodings of time between the client and
+ server. This includes the possibility of race conditions if the
+ document has changed between the time it was first requested and
+ the If-Modified-Since date of a subsequent request, and the
+
+
+
+Fielding, et al. Standards Track [Page 131]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ possibility of clock-skew-related problems if the If-Modified-
+ Since date is derived from the client's clock without correction
+ to the server's clock. Corrections for different time bases
+ between client and server are at best approximate due to network
+ latency.
+
+ The result of a request having both an If-Modified-Since header field
+ and either an If-Match or an If-Unmodified-Since header fields is
+ undefined by this specification.
+
+14.26 If-None-Match
+
+ The If-None-Match request-header field is used with a method to make
+ it conditional. A client that has one or more entities previously
+ obtained from the resource can verify that none of those entities is
+ current by including a list of their associated entity tags in the
+ If-None-Match header field. The purpose of this feature is to allow
+ efficient updates of cached information with a minimum amount of
+ transaction overhead. It is also used to prevent a method (e.g. PUT)
+ from inadvertently modifying an existing resource when the client
+ believes that the resource does not exist.
+
+ As a special case, the value "*" matches any current entity of the
+ resource.
+
+ If-None-Match = "If-None-Match" ":" ( "*" | 1#entity-tag )
+
+ If any of the entity tags match the entity tag of the entity that
+ would have been returned in the response to a similar GET request
+ (without the If-None-Match header) on that resource, or if "*" is
+ given and any current entity exists for that resource, then the
+ server MUST NOT perform the requested method, unless required to do
+ so because the resource's modification date fails to match that
+ supplied in an If-Modified-Since header field in the request.
+ Instead, if the request method was GET or HEAD, the server SHOULD
+ respond with a 304 (Not Modified) response, including the cache-
+ related header fields (particularly ETag) of one of the entities that
+ matched. For all other request methods, the server MUST respond with
+ a status of 412 (Precondition Failed).
+
+ See section 13.3.3 for rules on how to determine if two entities tags
+ match. The weak comparison function can only be used with GET or HEAD
+ requests.
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 132]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If none of the entity tags match, then the server MAY perform the
+ requested method as if the If-None-Match header field did not exist,
+ but MUST also ignore any If-Modified-Since header field(s) in the
+ request. That is, if no entity tags match, then the server MUST NOT
+ return a 304 (Not Modified) response.
+
+ If the request would, without the If-None-Match header field, result
+ in anything other than a 2xx or 304 status, then the If-None-Match
+ header MUST be ignored. (See section 13.3.4 for a discussion of
+ server behavior when both If-Modified-Since and If-None-Match appear
+ in the same request.)
+
+ The meaning of "If-None-Match: *" is that the method MUST NOT be
+ performed if the representation selected by the origin server (or by
+ a cache, possibly using the Vary mechanism, see section 14.44)
+ exists, and SHOULD be performed if the representation does not exist.
+ This feature is intended to be useful in preventing races between PUT
+ operations.
+
+ Examples:
+
+ If-None-Match: "xyzzy"
+ If-None-Match: W/"xyzzy"
+ If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
+ If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
+ If-None-Match: *
+
+ The result of a request having both an If-None-Match header field and
+ either an If-Match or an If-Unmodified-Since header fields is
+ undefined by this specification.
+
+14.27 If-Range
+
+ If a client has a partial copy of an entity in its cache, and wishes
+ to have an up-to-date copy of the entire entity in its cache, it
+ could use the Range request-header with a conditional GET (using
+ either or both of If-Unmodified-Since and If-Match.) However, if the
+ condition fails because the entity has been modified, the client
+ would then have to make a second request to obtain the entire current
+ entity-body.
+
+ The If-Range header allows a client to "short-circuit" the second
+ request. Informally, its meaning is `if the entity is unchanged, send
+ me the part(s) that I am missing; otherwise, send me the entire new
+ entity'.
+
+ If-Range = "If-Range" ":" ( entity-tag | HTTP-date )
+
+
+
+
+Fielding, et al. Standards Track [Page 133]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the client has no entity tag for an entity, but does have a Last-
+ Modified date, it MAY use that date in an If-Range header. (The
+ server can distinguish between a valid HTTP-date and any form of
+ entity-tag by examining no more than two characters.) The If-Range
+ header SHOULD only be used together with a Range header, and MUST be
+ ignored if the request does not include a Range header, or if the
+ server does not support the sub-range operation.
+
+ If the entity tag given in the If-Range header matches the current
+ entity tag for the entity, then the server SHOULD provide the
+ specified sub-range of the entity using a 206 (Partial content)
+ response. If the entity tag does not match, then the server SHOULD
+ return the entire entity using a 200 (OK) response.
+
+14.28 If-Unmodified-Since
+
+ The If-Unmodified-Since request-header field is used with a method to
+ make it conditional. If the requested resource has not been modified
+ since the time specified in this field, the server SHOULD perform the
+ requested operation as if the If-Unmodified-Since header were not
+ present.
+
+ If the requested variant has been modified since the specified time,
+ the server MUST NOT perform the requested operation, and MUST return
+ a 412 (Precondition Failed).
+
+ If-Unmodified-Since = "If-Unmodified-Since" ":" HTTP-date
+
+ An example of the field is:
+
+ If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+
+ If the request normally (i.e., without the If-Unmodified-Since
+ header) would result in anything other than a 2xx or 412 status, the
+ If-Unmodified-Since header SHOULD be ignored.
+
+ If the specified date is invalid, the header is ignored.
+
+ The result of a request having both an If-Unmodified-Since header
+ field and either an If-None-Match or an If-Modified-Since header
+ fields is undefined by this specification.
+
+14.29 Last-Modified
+
+ The Last-Modified entity-header field indicates the date and time at
+ which the origin server believes the variant was last modified.
+
+ Last-Modified = "Last-Modified" ":" HTTP-date
+
+
+
+Fielding, et al. Standards Track [Page 134]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ An example of its use is
+
+ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
+
+ The exact meaning of this header field depends on the implementation
+ of the origin server and the nature of the original resource. For
+ files, it may be just the file system last-modified time. For
+ entities with dynamically included parts, it may be the most recent
+ of the set of last-modify times for its component parts. For database
+ gateways, it may be the last-update time stamp of the record. For
+ virtual objects, it may be the last time the internal state changed.
+
+ An origin server MUST NOT send a Last-Modified date which is later
+ than the server's time of message origination. In such cases, where
+ the resource's last modification would indicate some time in the
+ future, the server MUST replace that date with the message
+ origination date.
+
+ An origin server SHOULD obtain the Last-Modified value of the entity
+ as close as possible to the time that it generates the Date value of
+ its response. This allows a recipient to make an accurate assessment
+ of the entity's modification time, especially if the entity changes
+ near the time that the response is generated.
+
+ HTTP/1.1 servers SHOULD send Last-Modified whenever feasible.
+
+14.30 Location
+
+ The Location response-header field is used to redirect the recipient
+ to a location other than the Request-URI for completion of the
+ request or identification of a new resource. For 201 (Created)
+ responses, the Location is that of the new resource which was created
+ by the request. For 3xx responses, the location SHOULD indicate the
+ server's preferred URI for automatic redirection to the resource. The
+ field value consists of a single absolute URI.
+
+ Location = "Location" ":" absoluteURI
+ [[ [ "#" fragment ] ]]
+
+ An example is:
+
+ Location: http://www.w3.org/pub/WWW/People.html
+
+ Note: The Content-Location header field (section 14.14) differs
+ from Location in that the Content-Location identifies the original
+ location of the entity enclosed in the request. It is therefore
+ possible for a response to contain header fields for both Location
+ and Content-Location. Also see section 13.10 for cache
+ requirements of some methods.
+
+
+
+Fielding, et al. Standards Track [Page 135]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.31 Max-Forwards
+
+ The Max-Forwards request-header field provides a mechanism with the
+ TRACE (section 9.8) and OPTIONS (section 9.2) methods to limit the
+ number of proxies or gateways that can forward the request to the
+ next inbound server. This can be useful when the client is attempting
+ to trace a request chain which appears to be failing or looping in
+ mid-chain.
+
+ Max-Forwards = "Max-Forwards" ":" 1*DIGIT
+
+ The Max-Forwards value is a decimal integer indicating the remaining
+ number of times this request message may be forwarded.
+
+ Each proxy or gateway recipient of a TRACE or OPTIONS request
+ containing a Max-Forwards header field MUST check and update its
+ value prior to forwarding the request. If the received value is zero
+ (0), the recipient MUST NOT forward the request; instead, it MUST
+ respond as the final recipient. If the received Max-Forwards value is
+ greater than zero, then the forwarded message MUST contain an updated
+ Max-Forwards field with a value decremented by one (1).
+
+ The Max-Forwards header field MAY be ignored for all other methods
+ defined by this specification and for any extension methods for which
+ it is not explicitly referred to as part of that method definition.
+
+14.32 Pragma
+
+ The Pragma general-header field is used to include implementation-
+ specific directives that might apply to any recipient along the
+ request/response chain. All pragma directives specify optional
+ behavior from the viewpoint of the protocol; however, some systems
+ MAY require that behavior be consistent with the directives.
+
+ Pragma = "Pragma" ":" 1#pragma-directive
+ pragma-directive = "no-cache" | extension-pragma
+ extension-pragma = token [ "=" ( token | quoted-string ) ]
+
+ When the no-cache directive is present in a request message, an
+ application SHOULD forward the request toward the origin server even
+ if it has a cached copy of what is being requested. This pragma
+ directive has the same semantics as the no-cache cache-directive (see
+ section 14.9) and is defined here for backward compatibility with
+ HTTP/1.0. Clients SHOULD include both header fields when a no-cache
+ request is sent to a server not known to be HTTP/1.1 compliant.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 136]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Pragma directives MUST be passed through by a proxy or gateway
+ application, regardless of their significance to that application,
+ since the directives might be applicable to all recipients along the
+ request/response chain. It is not possible to specify a pragma for a
+ specific recipient; however, any pragma directive not relevant to a
+ recipient SHOULD be ignored by that recipient.
+
+ HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client had
+ sent "Cache-Control: no-cache". No new Pragma directives will be
+ defined in HTTP.
+
+ Note: because the meaning of "Pragma: no-cache as a response
+ header field is not actually specified, it does not provide a
+ reliable replacement for "Cache-Control: no-cache" in a response
+
+14.33 Proxy-Authenticate
+
+ The Proxy-Authenticate response-header field MUST be included as part
+ of a 407 (Proxy Authentication Required) response. The field value
+ consists of a challenge that indicates the authentication scheme and
+ parameters applicable to the proxy for this Request-URI.
+
+ Proxy-Authenticate = "Proxy-Authenticate" ":" 1#challenge
+
+ The HTTP access authentication process is described in "HTTP
+ Authentication: Basic and Digest Access Authentication" [43]. Unlike
+ WWW-Authenticate, the Proxy-Authenticate header field applies only to
+ the current connection and SHOULD NOT be passed on to downstream
+ clients. However, an intermediate proxy might need to obtain its own
+ credentials by requesting them from the downstream client, which in
+ some circumstances will appear as if the proxy is forwarding the
+ Proxy-Authenticate header field.
+
+14.34 Proxy-Authorization
+
+ The Proxy-Authorization request-header field allows the client to
+ identify itself (or its user) to a proxy which requires
+ authentication. The Proxy-Authorization field value consists of
+ credentials containing the authentication information of the user
+ agent for the proxy and/or realm of the resource being requested.
+
+ Proxy-Authorization = "Proxy-Authorization" ":" credentials
+
+ The HTTP access authentication process is described in "HTTP
+ Authentication: Basic and Digest Access Authentication" [43] . Unlike
+ Authorization, the Proxy-Authorization header field applies only to
+ the next outbound proxy that demanded authentication using the Proxy-
+ Authenticate field. When multiple proxies are used in a chain, the
+
+
+
+Fielding, et al. Standards Track [Page 137]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Proxy-Authorization header field is consumed by the first outbound
+ proxy that was expecting to receive credentials. A proxy MAY relay
+ the credentials from the client request to the next proxy if that is
+ the mechanism by which the proxies cooperatively authenticate a given
+ request.
+
+14.35 Range
+
+14.35.1 Byte Ranges
+
+ Since all HTTP entities are represented in HTTP messages as sequences
+ of bytes, the concept of a byte range is meaningful for any HTTP
+ entity. (However, not all clients and servers need to support byte-
+ range operations.)
+
+ Byte range specifications in HTTP apply to the sequence of bytes in
+ the entity-body (not necessarily the same as the message-body).
+
+ A byte range operation MAY specify a single range of bytes, or a set
+ of ranges within a single entity.
+
+ ranges-specifier = byte-ranges-specifier
+ byte-ranges-specifier = bytes-unit "=" byte-range-set
+ byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
+ byte-range-spec = first-byte-pos "-" [last-byte-pos]
+ first-byte-pos = 1*DIGIT
+ last-byte-pos = 1*DIGIT
+
+ The first-byte-pos value in a byte-range-spec gives the byte-offset
+ of the first byte in a range. The last-byte-pos value gives the
+ byte-offset of the last byte in the range; that is, the byte
+ positions specified are inclusive. Byte offsets start at zero.
+
+ If the last-byte-pos value is present, it MUST be greater than or
+ equal to the first-byte-pos in that byte-range-spec, or the byte-
+ range-spec is syntactically invalid. The recipient of a byte-range-
+ set that includes one or more syntactically invalid byte-range-spec
+ values MUST ignore the header field that includes that byte-range-
+ set.
+
+ If the last-byte-pos value is absent, or if the value is greater than
+ or equal to the current length of the entity-body, last-byte-pos is
+ taken to be equal to one less than the current length of the entity-
+ body in bytes.
+
+ By its choice of last-byte-pos, a client can limit the number of
+ bytes retrieved without knowing the size of the entity.
+
+
+
+
+Fielding, et al. Standards Track [Page 138]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ suffix-byte-range-spec = "-" suffix-length
+ suffix-length = 1*DIGIT
+
+ A suffix-byte-range-spec is used to specify the suffix of the
+ entity-body, of a length given by the suffix-length value. (That is,
+ this form specifies the last N bytes of an entity-body.) If the
+ entity is shorter than the specified suffix-length, the entire
+ entity-body is used.
+
+ If a syntactically valid byte-range-set includes at least one byte-
+ range-spec whose first-byte-pos is less than the current length of
+ the entity-body, or at least one suffix-byte-range-spec with a non-
+ zero suffix-length, then the byte-range-set is satisfiable.
+ Otherwise, the byte-range-set is unsatisfiable. If the byte-range-set
+ is unsatisfiable, the server SHOULD return a response with a status
+ of 416 (Requested range not satisfiable). Otherwise, the server
+ SHOULD return a response with a status of 206 (Partial Content)
+ containing the satisfiable ranges of the entity-body.
+
+ Examples of byte-ranges-specifier values (assuming an entity-body of
+ length 10000):
+
+ - The first 500 bytes (byte offsets 0-499, inclusive): bytes=0-
+ 499
+
+ - The second 500 bytes (byte offsets 500-999, inclusive):
+ bytes=500-999
+
+ - The final 500 bytes (byte offsets 9500-9999, inclusive):
+ bytes=-500
+
+ - Or bytes=9500-
+
+ - The first and last bytes only (bytes 0 and 9999): bytes=0-0,-1
+
+ - Several legal but not canonical specifications of the second 500
+ bytes (byte offsets 500-999, inclusive):
+ bytes=500-600,601-999
+ bytes=500-700,601-999
+
+14.35.2 Range Retrieval Requests
+
+ HTTP retrieval requests using conditional or unconditional GET
+ methods MAY request one or more sub-ranges of the entity, instead of
+ the entire entity, using the Range request header, which applies to
+ the entity returned as the result of the request:
+
+ Range = "Range" ":" ranges-specifier
+
+
+
+Fielding, et al. Standards Track [Page 139]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ A server MAY ignore the Range header. However, HTTP/1.1 origin
+ servers and intermediate caches ought to support byte ranges when
+ possible, since Range supports efficient recovery from partially
+ failed transfers, and supports efficient partial retrieval of large
+ entities.
+
+ If the server supports the Range header and the specified range or
+ ranges are appropriate for the entity:
+
+ - The presence of a Range header in an unconditional GET modifies
+ what is returned if the GET is otherwise successful. In other
+ words, the response carries a status code of 206 (Partial
+ Content) instead of 200 (OK).
+
+ - The presence of a Range header in a conditional GET (a request
+ using one or both of If-Modified-Since and If-None-Match, or
+ one or both of If-Unmodified-Since and If-Match) modifies what
+ is returned if the GET is otherwise successful and the
+ condition is true. It does not affect the 304 (Not Modified)
+ response returned if the conditional is false.
+
+ In some cases, it might be more appropriate to use the If-Range
+ header (see section 14.27) in addition to the Range header.
+
+ If a proxy that supports ranges receives a Range request, forwards
+ the request to an inbound server, and receives an entire entity in
+ reply, it SHOULD only return the requested range to its client. It
+ SHOULD store the entire received response in its cache if that is
+ consistent with its cache allocation policies.
+
+14.36 Referer
+
+ The Referer[sic] request-header field allows the client to specify,
+ for the server's benefit, the address (URI) of the resource from
+ which the Request-URI was obtained (the "referrer", although the
+ header field is misspelled.) The Referer request-header allows a
+ server to generate lists of back-links to resources for interest,
+ logging, optimized caching, etc. It also allows obsolete or mistyped
+ links to be traced for maintenance. The Referer field MUST NOT be
+ sent if the Request-URI was obtained from a source that does not have
+ its own URI, such as input from the user keyboard.
+
+ Referer = "Referer" ":" ( absoluteURI | relativeURI )
+
+ Example:
+
+ Referer: http://www.w3.org/hypertext/DataSources/Overview.html
+
+
+
+
+Fielding, et al. Standards Track [Page 140]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If the field value is a relative URI, it SHOULD be interpreted
+ relative to the Request-URI. The URI MUST NOT include a fragment. See
+ section 15.1.3 for security considerations.
+
+14.37 Retry-After
+
+ The Retry-After response-header field can be used with a 503 (Service
+ Unavailable) response to indicate how long the service is expected to
+ be unavailable to the requesting client. This field MAY also be used
+ with any 3xx (Redirection) response to indicate the minimum time the
+ user-agent is asked wait before issuing the redirected request. The
+ value of this field can be either an HTTP-date or an integer number
+ of seconds (in decimal) after the time of the response.
+
+ Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
+
+ Two examples of its use are
+
+ Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
+ Retry-After: 120
+
+ In the latter example, the delay is 2 minutes.
+
+14.38 Server
+
+ The Server response-header field contains information about the
+ software used by the origin server to handle the request. The field
+ can contain multiple product tokens (section 3.8) and comments
+ identifying the server and any significant subproducts. The product
+ tokens are listed in order of their significance for identifying the
+ application.
+
+ Server = "Server" ":" 1*( product | comment )
+
+ Example:
+
+ Server: CERN/3.0 libwww/2.17
+
+ If the response is being forwarded through a proxy, the proxy
+ application MUST NOT modify the Server response-header. Instead, it
+ SHOULD include a Via field (as described in section 14.45).
+ [[ Actually, it MUST ]]
+
+ Note: Revealing the specific software version of the server might
+ allow the server machine to become more vulnerable to attacks
+ against software that is known to contain security holes. Server
+ implementors are encouraged to make this field a configurable
+ option.
+
+
+
+
+Fielding, et al. Standards Track [Page 141]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+14.39 TE
+
+ The TE request-header field indicates what extension transfer-codings
+ it is willing to accept in the response and whether or not it is
+ willing to accept trailer fields in a chunked transfer-coding. Its
+ value may consist of the keyword "trailers" and/or a comma-separated
+ list of extension transfer-coding names with optional accept
+ parameters (as described in section 3.6).
+
+ TE = "TE" ":" #( t-codings )
+ t-codings = "trailers" | ( transfer-extension [ accept-params ] )
+
+ The presence of the keyword "trailers" indicates that the client is
+ willing to accept trailer fields in a chunked transfer-coding, as
+ defined in section 3.6.1. This keyword is reserved for use with
+ transfer-coding values even though it does not itself represent a
+ transfer-coding.
+
+ Examples of its use are:
+
+ TE: deflate
+ TE:
+ TE: trailers, deflate;q=0.5
+
+ The TE header field only applies to the immediate connection.
+ Therefore, the keyword MUST be supplied within a Connection header
+ field (section 14.10) whenever TE is present in an HTTP/1.1 message.
+
+ A server tests whether a transfer-coding is acceptable, according to
+ a TE field, using these rules:
+
+ 1. The "chunked" transfer-coding is always acceptable. If the
+ keyword "trailers" is listed, the client indicates that it is
+ willing to accept trailer fields in the chunked response on
+ behalf of itself and any downstream clients. The implication is
+ that, if given, the client is stating that either all
+ downstream clients are willing to accept trailer fields in the
+ forwarded response, or that it will attempt to buffer the
+ response on behalf of downstream recipients.
+
+ Note: HTTP/1.1 does not define any means to limit the size of a
+ chunked response such that a client can be assured of buffering
+ the entire response.
+
+ 2. If the transfer-coding being tested is one of the transfer-
+ codings listed in the TE field, then it is acceptable unless it
+ is accompanied by a qvalue of 0. (As defined in section 3.9, a
+ qvalue of 0 means "not acceptable.")
+
+
+
+Fielding, et al. Standards Track [Page 142]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 3. If multiple transfer-codings are acceptable, then the
+ acceptable transfer-coding with the highest non-zero qvalue is
+ preferred. The "chunked" transfer-coding always has a qvalue
+ of 1.
+
+ If the TE field-value is empty or if no TE field is present, the only
+ transfer-coding is "chunked". A message with no transfer-coding is
+ always acceptable.
+
+14.40 Trailer
+
+ The Trailer general field value indicates that the given set of
+ header fields is present in the trailer of a message encoded with
+ chunked transfer-coding.
+
+ Trailer = "Trailer" ":" 1#field-name
+
+ An HTTP/1.1 message SHOULD include a Trailer header field in a
+ message using chunked transfer-coding with a non-empty trailer. Doing
+ so allows the recipient to know which header fields to expect in the
+ trailer.
+
+ If no Trailer header field is present, the trailer SHOULD NOT include
+ any header fields. See section 3.6.1 for restrictions on the use of
+ trailer fields in a "chunked" transfer-coding.
+
+ Message header fields listed in the Trailer header field MUST NOT
+ include the following header fields:
+
+ . Transfer-Encoding
+
+ . Content-Length
+
+ . Trailer
+
+14.41 Transfer-Encoding
+
+ The Transfer-Encoding general-header field indicates what (if any)
+ type of transformation has been applied to the message body in order
+ to safely transfer it between the sender and the recipient. This
+ differs from the content-coding in that the transfer-coding is a
+ property of the message, not of the entity.
+
+ Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
+
+ Transfer-codings are defined in section 3.6. An example is:
+
+ Transfer-Encoding: chunked
+
+
+
+Fielding, et al. Standards Track [Page 143]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ If multiple encodings have been applied to an entity, the transfer-
+ codings MUST be listed in the order in which they were applied.
+ Additional information about the encoding parameters MAY be provided
+ by other entity-header fields not defined by this specification.
+
+ Many older HTTP/1.0 applications do not understand the Transfer-
+ Encoding header.
+
+14.42 Upgrade
+
+ The Upgrade general-header allows the client to specify what
+ additional communication protocols it supports and would like to use
+ if the server finds it appropriate to switch protocols. The server
+ MUST use the Upgrade header field within a 101 (Switching Protocols)
+ response to indicate which protocol(s) are being switched.
+
+ Upgrade = "Upgrade" ":" 1#product
+
+ For example,
+
+ Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
+
+ The Upgrade header field is intended to provide a simple mechanism
+ for transition from HTTP/1.1 to some other, incompatible protocol. It
+ does so by allowing the client to advertise its desire to use another
+ protocol, such as a later version of HTTP with a higher major version
+ number, even though the current request has been made using HTTP/1.1.
+ This eases the difficult transition between incompatible protocols by
+ allowing the client to initiate a request in the more commonly
+ supported protocol while indicating to the server that it would like
+ to use a "better" protocol if available (where "better" is determined
+ by the server, possibly according to the nature of the method and/or
+ resource being requested).
+
+ The Upgrade header field only applies to switching application-layer
+ protocols upon the existing transport-layer connection. Upgrade
+ cannot be used to insist on a protocol change; its acceptance and use
+ by the server is optional. The capabilities and nature of the
+ application-layer communication after the protocol change is entirely
+ dependent upon the new protocol chosen, although the first action
+ after changing the protocol MUST be a response to the initial HTTP
+ request containing the Upgrade header field.
+
+ The Upgrade header field only applies to the immediate connection.
+ Therefore, the upgrade keyword MUST be supplied within a Connection
+ header field (section 14.10) whenever Upgrade is present in an
+ HTTP/1.1 message.
+
+
+
+
+Fielding, et al. Standards Track [Page 144]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The Upgrade header field cannot be used to indicate a switch to a
+ protocol on a different connection. For that purpose, it is more
+ appropriate to use a 301, 302, 303, or 305 redirection response.
+
+ This specification only defines the protocol name "HTTP" for use by
+ the family of Hypertext Transfer Protocols, as defined by the HTTP
+ version rules of section 3.1 and future updates to this
+ specification. Any token can be used as a protocol name; however, it
+ will only be useful if both the client and server associate the name
+ with the same protocol.
+
+14.43 User-Agent
+
+ The User-Agent request-header field contains information about the
+ user agent originating the request. This is for statistical purposes,
+ the tracing of protocol violations, and automated recognition of user
+ agents for the sake of tailoring responses to avoid particular user
+ agent limitations. User agents SHOULD include this field with
+ requests. The field can contain multiple product tokens (section 3.8)
+ and comments identifying the agent and any subproducts which form a
+ significant part of the user agent. By convention, the product tokens
+ are listed in order of their significance for identifying the
+ application.
+
+ User-Agent = "User-Agent" ":" 1*( product | comment )
+
+ Example:
+
+ User-Agent: CERN-LineMode/2.15 libwww/2.17b3
+
+14.44 Vary
+
+ The Vary field value indicates the set of request-header fields that
+ fully determines, while the response is fresh, whether a cache is
+ permitted to use the response to reply to a subsequent request
+ without revalidation. For uncacheable or stale responses, the Vary
+ field value advises the user agent about the criteria that were used
+ to select the representation. A Vary field value of "*" implies that
+ a cache cannot determine from the request headers of a subsequent
+ request whether this response is the appropriate representation. See
+ section 13.6 for use of the Vary header field by caches.
+
+ Vary = "Vary" ":" ( "*" | 1#field-name )
+
+ An HTTP/1.1 server SHOULD include a Vary header field with any
+ cacheable response that is subject to server-driven negotiation.
+ Doing so allows a cache to properly interpret future requests on that
+ resource and informs the user agent about the presence of negotiation
+
+
+
+Fielding, et al. Standards Track [Page 145]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ on that resource. A server MAY include a Vary header field with a
+ non-cacheable response that is subject to server-driven negotiation,
+ since this might provide the user agent with useful information about
+ the dimensions over which the response varies at the time of the
+ response.
+
+ A Vary field value consisting of a list of field-names signals that
+ the representation selected for the response is based on a selection
+ algorithm which considers ONLY the listed request-header field values
+ in selecting the most appropriate representation. A cache MAY assume
+ that the same selection will be made for future requests with the
+ same values for the listed field names, for the duration of time for
+ which the response is fresh.
+
+ The field-names given are not limited to the set of standard
+ request-header fields defined by this specification. Field names are
+ case-insensitive.
+
+ A Vary field value of "*" signals that unspecified parameters not
+ limited to the request-headers (e.g., the network address of the
+ client), play a role in the selection of the response representation.
+ The "*" value MUST NOT be generated by a proxy server; it may only be
+ generated by an origin server.
+
+14.45 Via
+
+ The Via general-header field MUST be used by gateways and proxies to
+ indicate the intermediate protocols and recipients between the user
+ agent and the server on requests, and between the origin server and
+ the client on responses. It is analogous to the "Received" field of
+ RFC 822 [9] and is intended to be used for tracking message forwards,
+ avoiding request loops, and identifying the protocol capabilities of
+ all senders along the request/response chain.
+
+ Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
+ received-protocol = [ protocol-name "/" ] protocol-version
+ protocol-name = token
+ protocol-version = token
+ received-by = ( host [ ":" port ] ) | pseudonym
+ pseudonym = token
+
+ The received-protocol indicates the protocol version of the message
+ received by the server or client along each segment of the
+ request/response chain. The received-protocol version is appended to
+ the Via field value when the message is forwarded so that information
+ about the protocol capabilities of upstream applications remains
+ visible to all recipients.
+
+
+
+
+Fielding, et al. Standards Track [Page 146]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The protocol-name is optional if and only if it would be "HTTP". The
+ received-by field is normally the host and optional port number of a
+ recipient server or client that subsequently forwarded the message.
+ However, if the real host is considered to be sensitive information,
+ it MAY be replaced by a pseudonym. If the port is not given, it MAY
+ be assumed to be the default port of the received-protocol.
+
+ Multiple Via field values represents each proxy or gateway that has
+ forwarded the message. Each recipient MUST append its information
+ such that the end result is ordered according to the sequence of
+ forwarding applications.
+
+ Comments MAY be used in the Via header field to identify the software
+ of the recipient proxy or gateway, analogous to the User-Agent and
+ Server header fields. However, all comments in the Via field are
+ optional and MAY be removed by any recipient prior to forwarding the
+ message.
+
+ For example, a request message could be sent from an HTTP/1.0 user
+ agent to an internal proxy code-named "fred", which uses HTTP/1.1 to
+ forward the request to a public proxy at nowhere.com, which completes
+ the request by forwarding it to the origin server at www.ics.uci.edu.
+ The request received by www.ics.uci.edu would then have the following
+ Via header field:
+
+ Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
+
+ Proxies and gateways used as a portal through a network firewall
+ SHOULD NOT, by default, forward the names and ports of hosts within
+ the firewall region. This information SHOULD only be propagated if
+ explicitly enabled. If not enabled, the received-by host of any host
+ behind the firewall SHOULD be replaced by an appropriate pseudonym
+ for that host.
+
+ For organizations that have strong privacy requirements for hiding
+ internal structures, a proxy MAY combine an ordered subsequence of
+ Via header field entries with identical received-protocol values into
+ a single such entry. For example,
+
+ Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy
+
+ could be collapsed to
+
+ Via: 1.0 ricky, 1.1 mertz, 1.0 lucy
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 147]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Applications SHOULD NOT combine multiple entries unless they are all
+ under the same organizational control and the hosts have already been
+ replaced by pseudonyms. Applications MUST NOT combine entries which
+ have different received-protocol values.
+
+14.46 Warning
+
+ The Warning general-header field is used to carry additional
+ information about the status or transformation of a message which
+ might not be reflected in the message. This information is typically
+ used to warn about a possible lack of semantic transparency from
+ caching operations or transformations applied to the entity body of
+ the message.
+
+ Warning headers are sent with responses using:
+
+ Warning = "Warning" ":" 1#warning-value
+
+ warning-value = warn-code SP warn-agent SP warn-text
+ [SP warn-date]
+
+ warn-code = 3DIGIT
+ warn-agent = ( host [ ":" port ] ) | pseudonym
+ ; the name or pseudonym of the server adding
+ ; the Warning header, for use in debugging
+ warn-text = quoted-string
+ warn-date = <"> HTTP-date <">
+
+ A response MAY carry more than one Warning header.
+
+ The warn-text SHOULD be in a natural language and character set that
+ is most likely to be intelligible to the human user receiving the
+ response. This decision MAY be based on any available knowledge, such
+ as the location of the cache or user, the Accept-Language field in a
+ request, the Content-Language field in a response, etc. The default
+ language is English and the default character set is ISO-8859-1.
+
+ If a character set other than ISO-8859-1 is used, it MUST be encoded
+ in the warn-text using the method described in RFC 2047 [14].
+
+ Warning headers can in general be applied to any message, however
+ some specific warn-codes are specific to caches and can only be
+ applied to response messages. New Warning headers SHOULD be added
+ after any existing Warning headers. A cache MUST NOT delete any
+ Warning header that it received with a message. However, if a cache
+ successfully validates a cache entry, it SHOULD remove any Warning
+ headers previously attached to that entry except as specified for
+
+
+
+
+Fielding, et al. Standards Track [Page 148]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ specific Warning codes. It MUST then add any Warning headers received
+ in the validating response. In other words, Warning headers are those
+ that would be attached to the most recent relevant response.
+
+ When multiple Warning headers are attached to a response, the user
+ agent ought to inform the user of as many of them as possible, in the
+ order that they appear in the response. If it is not possible to
+ inform the user of all of the warnings, the user agent SHOULD follow
+ these heuristics:
+
+ - Warnings that appear early in the response take priority over
+ those appearing later in the response.
+
+ - Warnings in the user's preferred character set take priority
+ over warnings in other character sets but with identical warn-
+ codes and warn-agents.
+
+ Systems that generate multiple Warning headers SHOULD order them with
+ this user agent behavior in mind.
+
+ Requirements for the behavior of caches with respect to Warnings are
+ stated in section 13.1.2.
+
+ This is a list of the currently-defined warn-codes, each with a
+ recommended warn-text in English, and a description of its meaning.
+
+ 110 Response is stale
+ MUST be included whenever the returned response is stale.
+
+ 111 Revalidation failed
+ MUST be included if a cache returns a stale response because an
+ attempt to revalidate the response failed, due to an inability to
+ reach the server.
+
+ 112 Disconnected operation
+ SHOULD be included if the cache is intentionally disconnected from
+ the rest of the network for a period of time.
+
+ 113 Heuristic expiration
+ MUST be included if the cache heuristically chose a freshness
+ lifetime greater than 24 hours and the response's age is greater
+ than 24 hours.
+
+ 199 Miscellaneous warning
+ The warning text MAY include arbitrary information to be presented
+ to a human user, or logged. A system receiving this warning MUST
+ NOT take any automated action, besides presenting the warning to
+ the user.
+
+
+
+Fielding, et al. Standards Track [Page 149]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 214 Transformation applied
+ MUST be added by an intermediate cache or proxy if it applies any
+ transformation changing the content-coding (as specified in the
+ Content-Encoding header) or media-type (as specified in the
+ Content-Type header) of the response, or the entity-body of the
+ response, unless this Warning code already appears in the response.
+
+ 299 Miscellaneous persistent warning
+ The warning text MAY include arbitrary information to be presented
+ to a human user, or logged. A system receiving this warning MUST
+ NOT take any automated action.
+
+ If an implementation sends a message with one or more Warning headers
+ whose version is HTTP/1.0 or lower, then the sender MUST include in
+ each warning-value a warn-date that matches the date in the response.
+
+ If an implementation receives a message with a warning-value that
+ includes a warn-date, and that warn-date is different from the Date
+ value in the response, then that warning-value MUST be deleted from
+ the message before storing, forwarding, or using it. (This prevents
+ bad consequences of naive caching of Warning header fields.) If all
+ of the warning-values are deleted for this reason, the Warning header
+ MUST be deleted as well.
+
+14.47 WWW-Authenticate
+
+ The WWW-Authenticate response-header field MUST be included in 401
+ (Unauthorized) response messages. The field value consists of at
+ least one challenge that indicates the authentication scheme(s) and
+ parameters applicable to the Request-URI.
+
+ WWW-Authenticate = "WWW-Authenticate" ":" 1#challenge
+
+ The HTTP access authentication process is described in "HTTP
+ Authentication: Basic and Digest Access Authentication" [43]. User
+ agents are advised to take special care in parsing the WWW-
+ Authenticate field value as it might contain more than one challenge,
+ or if more than one WWW-Authenticate header field is provided, the
+ contents of a challenge itself can contain a comma-separated list of
+ authentication parameters.
+
+15 Security Considerations
+
+ This section is meant to inform application developers, information
+ providers, and users of the security limitations in HTTP/1.1 as
+ described by this document. The discussion does not include
+ definitive solutions to the problems revealed, though it does make
+ some suggestions for reducing security risks.
+
+
+
+Fielding, et al. Standards Track [Page 150]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+15.1 Personal Information
+
+ HTTP clients are often privy to large amounts of personal information
+ (e.g. the user's name, location, mail address, passwords, encryption
+ keys, etc.), and SHOULD be very careful to prevent unintentional
+ leakage of this information via the HTTP protocol to other sources.
+ We very strongly recommend that a convenient interface be provided
+ for the user to control dissemination of such information, and that
+ designers and implementors be particularly careful in this area.
+ History shows that errors in this area often create serious security
+ and/or privacy problems and generate highly adverse publicity for the
+ implementor's company.
+
+15.1.1 Abuse of Server Log Information
+
+ A server is in the position to save personal data about a user's
+ requests which might identify their reading patterns or subjects of
+ interest. This information is clearly confidential in nature and its
+ handling can be constrained by law in certain countries. People using
+ the HTTP protocol to provide data are responsible for ensuring that
+ such material is not distributed without the permission of any
+ individuals that are identifiable by the published results.
+
+15.1.2 Transfer of Sensitive Information
+
+ Like any generic data transfer protocol, HTTP cannot regulate the
+ content of the data that is transferred, nor is there any a priori
+ method of determining the sensitivity of any particular piece of
+ information within the context of any given request. Therefore,
+ applications SHOULD supply as much control over this information as
+ possible to the provider of that information. Four header fields are
+ worth special mention in this context: Server, Via, Referer and From.
+
+ Revealing the specific software version of the server might allow the
+ server machine to become more vulnerable to attacks against software
+ that is known to contain security holes. Implementors SHOULD make the
+ Server header field a configurable option.
+
+ Proxies which serve as a portal through a network firewall SHOULD
+ take special precautions regarding the transfer of header information
+ that identifies the hosts behind the firewall. In particular, they
+ SHOULD remove, or replace with sanitized versions, any Via fields
+ generated behind the firewall.
+
+ The Referer header allows reading patterns to be studied and reverse
+ links drawn. Although it can be very useful, its power can be abused
+ if user details are not separated from the information contained in
+
+
+
+
+Fielding, et al. Standards Track [Page 151]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ the Referer. Even when the personal information has been removed, the
+ Referer header might indicate a private document's URI whose
+ publication would be inappropriate.
+
+ The information sent in the From field might conflict with the user's
+ privacy interests or their site's security policy, and hence it
+ SHOULD NOT be transmitted without the user being able to disable,
+ enable, and modify the contents of the field. The user MUST be able
+ to set the contents of this field within a user preference or
+ application defaults configuration.
+
+ We suggest, though do not require, that a convenient toggle interface
+ be provided for the user to enable or disable the sending of From and
+ Referer information.
+
+ The User-Agent (section 14.43) or Server (section 14.38) header
+ fields can sometimes be used to determine that a specific client or
+ server have a particular security hole which might be exploited.
+ Unfortunately, this same information is often used for other valuable
+ purposes for which HTTP currently has no better mechanism.
+
+15.1.3 Encoding Sensitive Information in URI's
+
+ Because the source of a link might be private information or might
+ reveal an otherwise private information source, it is strongly
+ recommended that the user be able to select whether or not the
+ Referer field is sent. For example, a browser client could have a
+ toggle switch for browsing openly/anonymously, which would
+ respectively enable/disable the sending of Referer and From
+ information.
+
+ Clients SHOULD NOT include a Referer header field in a (non-secure)
+ HTTP request if the referring page was transferred with a secure
+ protocol.
+
+ Authors of services which use the HTTP protocol SHOULD NOT use GET
+ based forms for the submission of sensitive data, because this will
+ cause this data to be encoded in the Request-URI. Many existing
+ servers, proxies, and user agents will log the request URI in some
+ place where it might be visible to third parties. Servers can use
+ POST-based form submission instead
+
+15.1.4 Privacy Issues Connected to Accept Headers
+
+ Accept request-headers can reveal information about the user to all
+ servers which are accessed. The Accept-Language header in particular
+ can reveal information the user would consider to be of a private
+ nature, because the understanding of particular languages is often
+
+
+
+Fielding, et al. Standards Track [Page 152]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ strongly correlated to the membership of a particular ethnic group.
+ User agents which offer the option to configure the contents of an
+ Accept-Language header to be sent in every request are strongly
+ encouraged to let the configuration process include a message which
+ makes the user aware of the loss of privacy involved.
+
+ An approach that limits the loss of privacy would be for a user agent
+ to omit the sending of Accept-Language headers by default, and to ask
+ the user whether or not to start sending Accept-Language headers to a
+ server if it detects, by looking for any Vary response-header fields
+ generated by the server, that such sending could improve the quality
+ of service.
+
+ Elaborate user-customized accept header fields sent in every request,
+ in particular if these include quality values, can be used by servers
+ as relatively reliable and long-lived user identifiers. Such user
+ identifiers would allow content providers to do click-trail tracking,
+ and would allow collaborating content providers to match cross-server
+ click-trails or form submissions of individual users. Note that for
+ many users not behind a proxy, the network address of the host
+ running the user agent will also serve as a long-lived user
+ identifier. In environments where proxies are used to enhance
+ privacy, user agents ought to be conservative in offering accept
+ header configuration options to end users. As an extreme privacy
+ measure, proxies could filter the accept headers in relayed requests.
+ General purpose user agents which provide a high degree of header
+ configurability SHOULD warn users about the loss of privacy which can
+ be involved.
+
+15.2 Attacks Based On File and Path Names
+
+ Implementations of HTTP origin servers SHOULD be careful to restrict
+ the documents returned by HTTP requests to be only those that were
+ intended by the server administrators. If an HTTP server translates
+ HTTP URIs directly into file system calls, the server MUST take
+ special care not to serve files that were not intended to be
+ delivered to HTTP clients. For example, UNIX, Microsoft Windows, and
+ other operating systems use ".." as a path component to indicate a
+ directory level above the current one. On such a system, an HTTP
+ server MUST disallow any such construct in the Request-URI if it
+ would otherwise allow access to a resource outside those intended to
+ be accessible via the HTTP server. Similarly, files intended for
+ reference only internally to the server (such as access control
+ files, configuration files, and script code) MUST be protected from
+ inappropriate retrieval, since they might contain sensitive
+ information. Experience has shown that minor bugs in such HTTP server
+ implementations have turned into security risks.
+
+
+
+
+Fielding, et al. Standards Track [Page 153]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+15.3 DNS Spoofing
+
+ Clients using HTTP rely heavily on the Domain Name Service, and are
+ thus generally prone to security attacks based on the deliberate
+ mis-association of IP addresses and DNS names. Clients need to be
+ cautious in assuming the continuing validity of an IP number/DNS name
+ association.
+
+ In particular, HTTP clients SHOULD rely on their name resolver for
+ confirmation of an IP number/DNS name association, rather than
+ caching the result of previous host name lookups. Many platforms
+ already can cache host name lookups locally when appropriate, and
+ they SHOULD be configured to do so. It is proper for these lookups to
+ be cached, however, only when the TTL (Time To Live) information
+ reported by the name server makes it likely that the cached
+ information will remain useful.
+
+ If HTTP clients cache the results of host name lookups in order to
+ achieve a performance improvement, they MUST observe the TTL
+ information reported by DNS.
+
+ If HTTP clients do not observe this rule, they could be spoofed when
+ a previously-accessed server's IP address changes. As network
+ renumbering is expected to become increasingly common [24], the
+ possibility of this form of attack will grow. Observing this
+ requirement thus reduces this potential security vulnerability.
+
+ This requirement also improves the load-balancing behavior of clients
+ for replicated servers using the same DNS name and reduces the
+ likelihood of a user's experiencing failure in accessing sites which
+ use that strategy.
+
+15.4 Location Headers and Spoofing
+
+ If a single server supports multiple organizations that do not trust
+ one another, then it MUST check the values of Location and Content-
+ Location headers in responses that are generated under control of
+ said organizations to make sure that they do not attempt to
+ invalidate resources over which they have no authority.
+
+15.5 Content-Disposition Issues
+
+ RFC 1806 [35], from which the often implemented Content-Disposition
+ (see section 19.5.1) header in HTTP is derived, has a number of very
+ serious security considerations. Content-Disposition is not part of
+ the HTTP standard, but since it is widely implemented, we are
+ documenting its use and risks for implementors. See RFC 2183 [49]
+ (which updates RFC 1806) for details.
+
+
+
+Fielding, et al. Standards Track [Page 154]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+15.6 Authentication Credentials and Idle Clients
+
+ Existing HTTP clients and user agents typically retain authentication
+ information indefinitely. HTTP/1.1. does not provide a method for a
+ server to direct clients to discard these cached credentials. This is
+ a significant defect that requires further extensions to HTTP.
+ Circumstances under which credential caching can interfere with the
+ application's security model include but are not limited to:
+
+ - Clients which have been idle for an extended period following
+ which the server might wish to cause the client to reprompt the
+ user for credentials.
+
+ - Applications which include a session termination indication
+ (such as a `logout' or `commit' button on a page) after which
+ the server side of the application `knows' that there is no
+ further reason for the client to retain the credentials.
+
+ This is currently under separate study. There are a number of work-
+ arounds to parts of this problem, and we encourage the use of
+ password protection in screen savers, idle time-outs, and other
+ methods which mitigate the security problems inherent in this
+ problem. In particular, user agents which cache credentials are
+ encouraged to provide a readily accessible mechanism for discarding
+ cached credentials under user control.
+
+15.7 Proxies and Caching
+
+ By their very nature, HTTP proxies are men-in-the-middle, and
+ represent an opportunity for man-in-the-middle attacks. Compromise of
+ the systems on which the proxies run can result in serious security
+ and privacy problems. Proxies have access to security-related
+ information, personal information about individual users and
+ organizations, and proprietary information belonging to users and
+ content providers. A compromised proxy, or a proxy implemented or
+ configured without regard to security and privacy considerations,
+ might be used in the commission of a wide range of potential attacks.
+
+ Proxy operators should protect the systems on which proxies run as
+ they would protect any system that contains or transports sensitive
+ information. In particular, log information gathered at proxies often
+ contains highly sensitive personal information, and/or information
+ about organizations. Log information should be carefully guarded, and
+ appropriate guidelines for use developed and followed. (Section
+ 15.1.1).
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 155]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Caching proxies provide additional potential vulnerabilities, since
+ the contents of the cache represent an attractive target for
+ malicious exploitation. Because cache contents persist after an HTTP
+ request is complete, an attack on the cache can reveal information
+ long after a user believes that the information has been removed from
+ the network. Therefore, cache contents should be protected as
+ sensitive information.
+
+ Proxy implementors should consider the privacy and security
+ implications of their design and coding decisions, and of the
+ configuration options they provide to proxy operators (especially the
+ default configuration).
+
+ Users of a proxy need to be aware that they are no trustworthier than
+ the people who run the proxy; HTTP itself cannot solve this problem.
+
+ The judicious use of cryptography, when appropriate, may suffice to
+ protect against a broad range of security and privacy attacks. Such
+ cryptography is beyond the scope of the HTTP/1.1 specification.
+
+15.7.1 Denial of Service Attacks on Proxies
+
+ They exist. They are hard to defend against. Research continues.
+ Beware.
+
+16 Acknowledgments
+
+ This specification makes heavy use of the augmented BNF and generic
+ constructs defined by David H. Crocker for RFC 822 [9]. Similarly, it
+ reuses many of the definitions provided by Nathaniel Borenstein and
+ Ned Freed for MIME [7]. We hope that their inclusion in this
+ specification will help reduce past confusion over the relationship
+ between HTTP and Internet mail message formats.
+
+ The HTTP protocol has evolved considerably over the years. It has
+ benefited from a large and active developer community--the many
+ people who have participated on the www-talk mailing list--and it is
+ that community which has been most responsible for the success of
+ HTTP and of the World-Wide Web in general. Marc Andreessen, Robert
+ Cailliau, Daniel W. Connolly, Bob Denny, John Franks, Jean-Francois
+ Groff, Phillip M. Hallam-Baker, Hakon W. Lie, Ari Luotonen, Rob
+ McCool, Lou Montulli, Dave Raggett, Tony Sanders, and Marc
+ VanHeyningen deserve special recognition for their efforts in
+ defining early aspects of the protocol.
+
+ This document has benefited greatly from the comments of all those
+ participating in the HTTP-WG. In addition to those already mentioned,
+ the following individuals have contributed to this specification:
+
+
+
+Fielding, et al. Standards Track [Page 156]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Gary Adams Ross Patterson
+ Harald Tveit Alvestrand Albert Lunde
+ Keith Ball John C. Mallery
+ Brian Behlendorf Jean-Philippe Martin-Flatin
+ Paul Burchard Mitra
+ Maurizio Codogno David Morris
+ Mike Cowlishaw Gavin Nicol
+ Roman Czyborra Bill Perry
+ Michael A. Dolan Jeffrey Perry
+ David J. Fiander Scott Powers
+ Alan Freier Owen Rees
+ Marc Hedlund Luigi Rizzo
+ Greg Herlihy David Robinson
+ Koen Holtman Marc Salomon
+ Alex Hopmann Rich Salz
+ Bob Jernigan Allan M. Schiffman
+ Shel Kaphan Jim Seidman
+ Rohit Khare Chuck Shotton
+ John Klensin Eric W. Sink
+ Martijn Koster Simon E. Spero
+ Alexei Kosut Richard N. Taylor
+ David M. Kristol Robert S. Thau
+ Daniel LaLiberte Bill (BearHeart) Weinman
+ Ben Laurie Francois Yergeau
+ Paul J. Leach Mary Ellen Zurko
+ Daniel DuBois Josh Cohen
+
+
+ Much of the content and presentation of the caching design is due to
+ suggestions and comments from individuals including: Shel Kaphan,
+ Paul Leach, Koen Holtman, David Morris, and Larry Masinter.
+
+ Most of the specification of ranges is based on work originally done
+ by Ari Luotonen and John Franks, with additional input from Steve
+ Zilles.
+
+ Thanks to the "cave men" of Palo Alto. You know who you are.
+
+ Jim Gettys (the current editor of this document) wishes particularly
+ to thank Roy Fielding, the previous editor of this document, along
+ with John Klensin, Jeff Mogul, Paul Leach, Dave Kristol, Koen
+ Holtman, John Franks, Josh Cohen, Alex Hopmann, Scott Lawrence, and
+ Larry Masinter for their help. And thanks go particularly to Jeff
+ Mogul and Scott Lawrence for performing the "MUST/MAY/SHOULD" audit.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 157]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ The Apache Group, Anselm Baird-Smith, author of Jigsaw, and Henrik
+ Frystyk implemented RFC 2068 early, and we wish to thank them for the
+ discovery of many of the problems that this document attempts to
+ rectify.
+
+17 References
+
+ [1] Alvestrand, H., "Tags for the Identification of Languages", RFC
+ 1766, March 1995.
+
+ [2] Anklesaria, F., McCahill, M., Lindner, P., Johnson, D., Torrey,
+ D. and B. Alberti, "The Internet Gopher Protocol (a distributed
+ document search and retrieval protocol)", RFC 1436, March 1993.
+
+ [3] Berners-Lee, T., "Universal Resource Identifiers in WWW", RFC
+ 1630, June 1994.
+
+ [4] Berners-Lee, T., Masinter, L. and M. McCahill, "Uniform Resource
+ Locators (URL)", RFC 1738, December 1994.
+
+ [5] Berners-Lee, T. and D. Connolly, "Hypertext Markup Language -
+ 2.0", RFC 1866, November 1995.
+
+ [6] Berners-Lee, T., Fielding, R. and H. Frystyk, "Hypertext Transfer
+ Protocol -- HTTP/1.0", RFC 1945, May 1996.
+
+ [7] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies",
+ RFC 2045, November 1996.
+
+ [8] Braden, R., "Requirements for Internet Hosts -- Communication
+ Layers", STD 3, RFC 1123, October 1989.
+
+ [9] Crocker, D., "Standard for The Format of ARPA Internet Text
+ Messages", STD 11, RFC 822, August 1982.
+
+ [10] Davis, F., Kahle, B., Morris, H., Salem, J., Shen, T., Wang, R.,
+ Sui, J., and M. Grinbaum, "WAIS Interface Protocol Prototype
+ Functional Specification," (v1.5), Thinking Machines
+ Corporation, April 1990.
+
+ [11] Fielding, R., "Relative Uniform Resource Locators", RFC 1808,
+ June 1995.
+
+ [12] Horton, M. and R. Adams, "Standard for Interchange of USENET
+ Messages", RFC 1036, December 1987.
+
+
+
+
+
+Fielding, et al. Standards Track [Page 158]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ [13] Kantor, B. and P. Lapsley, "Network News Transfer Protocol", RFC
+ 977, February 1986.
+
+ [14] Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part
+ Three: Message Header Extensions for Non-ASCII Text", RFC 2047,
+ November 1996.
+
+ [15] Nebel, E. and L. Masinter, "Form-based File Upload in HTML", RFC
+ 1867, November 1995.
+
+ [16] Postel, J., "Simple Mail Transfer Protocol", STD 10, RFC 821,
+ August 1982.
+
+ [17] Postel, J., "Media Type Registration Procedure", RFC 1590,
+ November 1996.
+
+[[ Should be: ]]
+[[ [17] Freed, N., Klensin, J., and Postel, J., "Multipurpose Internet ]]
+[[ Mail Extensions (MIME) Part Four: "Registration Procedure", ]]
+[[ RFC 2048, November 1996. ]]
+
+ [18] Postel, J. and J. Reynolds, "File Transfer Protocol", STD 9, RFC
+ 959, October 1985.
+
+ [19] Reynolds, J. and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
+ October 1994.
+
+ [20] Sollins, K. and L. Masinter, "Functional Requirements for
+ Uniform Resource Names", RFC 1737, December 1994.
+
+ [21] US-ASCII. Coded Character Set - 7-Bit American Standard Code for
+ Information Interchange. Standard ANSI X3.4-1986, ANSI, 1986.
+
+ [22] ISO-8859. International Standard -- Information Processing --
+ 8-bit Single-Byte Coded Graphic Character Sets --
+ Part 1: Latin alphabet No. 1, ISO-8859-1:1987.
+ Part 2: Latin alphabet No. 2, ISO-8859-2, 1987.
+ Part 3: Latin alphabet No. 3, ISO-8859-3, 1988.
+ Part 4: Latin alphabet No. 4, ISO-8859-4, 1988.
+ Part 5: Latin/Cyrillic alphabet, ISO-8859-5, 1988.
+ Part 6: Latin/Arabic alphabet, ISO-8859-6, 1987.
+ Part 7: Latin/Greek alphabet, ISO-8859-7, 1987.
+ Part 8: Latin/Hebrew alphabet, ISO-8859-8, 1988.
+ Part 9: Latin alphabet No. 5, ISO-8859-9, 1990.
+
+ [23] Meyers, J. and M. Rose, "The Content-MD5 Header Field", RFC
+ 1864, October 1995.
+
+ [24] Carpenter, B. and Y. Rekhter, "Renumbering Needs Work", RFC
+ 1900, February 1996.
+
+ [25] Deutsch, P., "GZIP file format specification version 4.3", RFC
+ 1952, May 1996.
+
+
+
+Fielding, et al. Standards Track [Page 159]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ [26] Venkata N. Padmanabhan, and Jeffrey C. Mogul. "Improving HTTP
+ Latency", Computer Networks and ISDN Systems, v. 28, pp. 25-35,
+ Dec. 1995. Slightly revised version of paper in Proc. 2nd
+ International WWW Conference '94: Mosaic and the Web, Oct. 1994,
+ which is available at
+ http://www.ncsa.uiuc.edu/SDG/IT94/Proceedings/DDay/mogul/HTTPLat
+ ency.html.
+
+ [27] Joe Touch, John Heidemann, and Katia Obraczka. "Analysis of HTTP
+ Performance", <URL: http://www.isi.edu/touch/pubs/http-perf96/>,
+ ISI Research Report ISI/RR-98-463, (original report dated Aug.
+ 1996), USC/Information Sciences Institute, August 1998.
+
+ [28] Mills, D., "Network Time Protocol (Version 3) Specification,
+ Implementation and Analysis", RFC 1305, March 1992.
+
+ [29] Deutsch, P., "DEFLATE Compressed Data Format Specification
+ version 1.3", RFC 1951, May 1996.
+
+ [30] S. Spero, "Analysis of HTTP Performance Problems,"
+ http://sunsite.unc.edu/mdma-release/http-prob.html.
+
+ [31] Deutsch, P. and J. Gailly, "ZLIB Compressed Data Format
+ Specification version 3.3", RFC 1950, May 1996.
+
+ [32] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P.,
+ Luotonen, A., Sink, E. and L. Stewart, "An Extension to HTTP:
+ Digest Access Authentication", RFC 2069, January 1997.
+
+ [33] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and T.
+ Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC
+ 2068, January 1997.
+
+ [34] Bradner, S., "Key words for use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+ [35] Troost, R. and Dorner, S., "Communicating Presentation
+ Information in Internet Messages: The Content-Disposition
+ Header", RFC 1806, June 1995.
+
+ [36] Mogul, J., Fielding, R., Gettys, J. and H. Frystyk, "Use and
+ Interpretation of HTTP Version Numbers", RFC 2145, May 1997.
+ [jg639]
+
+ [37] Palme, J., "Common Internet Message Headers", RFC 2076, February
+ 1997. [jg640]
+
+
+
+
+
+Fielding, et al. Standards Track [Page 160]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ [38] Yergeau, F., "UTF-8, a transformation format of Unicode and
+ ISO-10646", RFC 2279, January 1998. [jg641]
+
+ [39] Nielsen, H.F., Gettys, J., Baird-Smith, A., Prud'hommeaux, E.,
+ Lie, H., and C. Lilley. "Network Performance Effects of
+ HTTP/1.1, CSS1, and PNG," Proceedings of ACM SIGCOMM '97, Cannes
+ France, September 1997.[jg642]
+
+ [40] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part Two: Media Types", RFC 2046, November
+ 1996. [jg643]
+
+ [41] Alvestrand, H., "IETF Policy on Character Sets and Languages",
+ BCP 18, RFC 2277, January 1998. [jg644]
+
+ [42] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform Resource
+ Identifiers (URI): Generic Syntax and Semantics", RFC 2396,
+ August 1998. [jg645]
+
+ [43] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S.,
+ Leach, P., Luotonen, A., Sink, E. and L. Stewart, "HTTP
+ Authentication: Basic and Digest Access Authentication", RFC
+ 2617, June 1999. [jg646]
+
+ [44] Luotonen, A., "Tunneling TCP based protocols through Web proxy
+ servers," Work in Progress. [jg647]
+
+ [45] Palme, J. and A. Hopmann, "MIME E-mail Encapsulation of
+ Aggregate Documents, such as HTML (MHTML)", RFC 2110, March
+ 1997.
+
+ [46] Bradner, S., "The Internet Standards Process -- Revision 3", BCP
+ 9, RFC 2026, October 1996.
+
+ [47] Masinter, L., "Hyper Text Coffee Pot Control Protocol
+ (HTCPCP/1.0)", RFC 2324, 1 April 1998.
+
+ [48] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part Five: Conformance Criteria and Examples",
+ RFC 2049, November 1996.
+
+ [49] Troost, R., Dorner, S. and K. Moore, "Communicating Presentation
+ Information in Internet Messages: The Content-Disposition Header
+ Field", RFC 2183, August 1997.
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 161]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+18 Authors' Addresses
+
+ Roy T. Fielding
+ Information and Computer Science
+ University of California, Irvine
+ Irvine, CA 92697-3425, USA
+
+ Fax: +1 (949) 824-1715
+ EMail: fielding@ics.uci.edu
+
+
+ James Gettys
+ World Wide Web Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: jg@w3.org
+
+
+ Jeffrey C. Mogul
+ Western Research Laboratory
+ Compaq Computer Corporation
+ 250 University Avenue
+ Palo Alto, California, 94305, USA
+
+ EMail: mogul@wrl.dec.com
+
+
+ Henrik Frystyk Nielsen
+ World Wide Web Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: frystyk@w3.org
+
+
+ Larry Masinter
+ Xerox Corporation
+ 3333 Coyote Hill Road
+ Palo Alto, CA 94034, USA
+
+ EMail: masinter@parc.xerox.com
+
+
+
+
+
+Fielding, et al. Standards Track [Page 162]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Paul J. Leach
+ Microsoft Corporation
+ 1 Microsoft Way
+ Redmond, WA 98052, USA
+
+ EMail: paulle@microsoft.com
+
+
+ Tim Berners-Lee
+ Director, World Wide Web Consortium
+ MIT Laboratory for Computer Science
+ 545 Technology Square
+ Cambridge, MA 02139, USA
+
+ Fax: +1 (617) 258 8682
+ EMail: timbl@w3.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 163]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+19 Appendices
+
+19.1 Internet Media Type message/http and application/http
+
+ In addition to defining the HTTP/1.1 protocol, this document serves
+ as the specification for the Internet media type "message/http" and
+ "application/http". The message/http type can be used to enclose a
+ single HTTP request or response message, provided that it obeys the
+ MIME restrictions for all "message" types regarding line length and
+ encodings. The application/http type can be used to enclose a
+ pipeline of one or more HTTP request or response messages (not
+ intermixed). The following is to be registered with IANA [17].
+
+ Media Type name: message
+ Media subtype name: http
+ Required parameters: none
+ Optional parameters: version, msgtype
+ version: The HTTP-Version number of the enclosed message
+ (e.g., "1.1"). If not present, the version can be
+ determined from the first line of the body.
+ msgtype: The message type -- "request" or "response". If not
+ present, the type can be determined from the first
+ line of the body.
+ Encoding considerations: only "7bit", "8bit", or "binary" are
+ permitted
+ Security considerations: none
+
+ Media Type name: application
+ Media subtype name: http
+ Required parameters: none
+ Optional parameters: version, msgtype
+ version: The HTTP-Version number of the enclosed messages
+ (e.g., "1.1"). If not present, the version can be
+ determined from the first line of the body.
+ msgtype: The message type -- "request" or "response". If not
+ present, the type can be determined from the first
+ line of the body.
+ Encoding considerations: HTTP messages enclosed by this type
+ are in "binary" format; use of an appropriate
+ Content-Transfer-Encoding is required when
+ transmitted via E-mail.
+ Security considerations: none
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 164]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+19.2 Internet Media Type multipart/byteranges
+
+ When an HTTP 206 (Partial Content) response message includes the
+ content of multiple ranges (a response to a request for multiple
+ non-overlapping ranges), these are transmitted as a multipart
+ message-body. The media type for this purpose is called
+ "multipart/byteranges".
+
+ The multipart/byteranges media type includes two or more parts, each
+ with its own Content-Type and Content-Range fields. The required
+ boundary parameter specifies the boundary string used to separate
+ each body-part.
+
+ Media Type name: multipart
+ Media subtype name: byteranges
+ Required parameters: boundary
+ Optional parameters: none
+ Encoding considerations: only "7bit", "8bit", or "binary" are
+ permitted
+ Security considerations: none
+
+
+ For example:
+
+ HTTP/1.1 206 Partial Content
+ Date: Wed, 15 Nov 1995 06:25:24 GMT
+ Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
+ Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
+
+ --THIS_STRING_SEPARATES
+ Content-type: application/pdf
+ Content-range: bytes 500-999/8000
+
+ ...the first range...
+ --THIS_STRING_SEPARATES
+ Content-type: application/pdf
+ Content-range: bytes 7000-7999/8000
+
+ ...the second range
+ --THIS_STRING_SEPARATES--
+
+ Notes:
+
+ 1) Additional CRLFs may precede the first boundary string in the
+ entity.
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 165]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ 2) Although RFC 2046 [40] permits the boundary string to be
+ quoted, some existing implementations handle a quoted boundary
+ string incorrectly.
+
+ 3) A number of browsers and servers were coded to an early draft
+ of the byteranges specification to use a media type of
+ multipart/x-byteranges, which is almost, but not quite
+ compatible with the version documented in HTTP/1.1.
+
+19.3 Tolerant Applications
+
+ Although this document specifies the requirements for the generation
+ of HTTP/1.1 messages, not all applications will be correct in their
+ implementation. We therefore recommend that operational applications
+ be tolerant of deviations whenever those deviations can be
+ interpreted unambiguously.
+
+ Clients SHOULD be tolerant in parsing the Status-Line and servers
+ tolerant when parsing the Request-Line. In particular, they SHOULD
+ accept any amount of SP or HT characters between fields, even though
+ only a single SP is required.
+
+ The line terminator for message-header fields is the sequence CRLF.
+ However, we recommend that applications, when parsing such headers,
+ recognize a single LF as a line terminator and ignore the leading CR.
+
+ The character set of an entity-body SHOULD be labeled as the lowest
+ common denominator of the character codes used within that body, with
+ the exception that not labeling the entity is preferred over labeling
+ the entity with the labels US-ASCII or ISO-8859-1. See section 3.7.1
+ and 3.4.1.
+
+ Additional rules for requirements on parsing and encoding of dates
+ and other potential problems with date encodings include:
+
+ - HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
+ which appears to be more than 50 years in the future is in fact
+ in the past (this helps solve the "year 2000" problem).
+
+ - An HTTP/1.1 implementation MAY internally represent a parsed
+ Expires date as earlier than the proper value, but MUST NOT
+ internally represent a parsed Expires date as later than the
+ proper value.
+
+ - All expiration-related calculations MUST be done in GMT. The
+ local time zone MUST NOT influence the calculation or comparison
+ of an age or expiration time.
+
+
+
+
+Fielding, et al. Standards Track [Page 166]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - If an HTTP header incorrectly carries a date value with a time
+ zone other than GMT, it MUST be converted into GMT using the
+ most conservative possible conversion.
+
+19.4 Differences Between HTTP Entities and RFC 2045 Entities
+
+ HTTP/1.1 uses many of the constructs defined for Internet Mail (RFC
+ 822 [9]) and the Multipurpose Internet Mail Extensions (MIME [7]) to
+ allow entities to be transmitted in an open variety of
+ representations and with extensible mechanisms. However, RFC 2045
+ discusses mail, and HTTP has a few features that are different from
+ those described in RFC 2045. These differences were carefully chosen
+ to optimize performance over binary connections, to allow greater
+ freedom in the use of new media types, to make date comparisons
+ easier, and to acknowledge the practice of some early HTTP servers
+ and clients.
+
+ This appendix describes specific areas where HTTP differs from RFC
+ 2045. Proxies and gateways to strict MIME environments SHOULD be
+ aware of these differences and provide the appropriate conversions
+ where necessary. Proxies and gateways from MIME environments to HTTP
+ also need to be aware of the differences because some conversions
+ might be required.
+
+19.4.1 MIME-Version
+
+ HTTP is not a MIME-compliant protocol. However, HTTP/1.1 messages MAY
+ include a single MIME-Version general-header field to indicate what
+ version of the MIME protocol was used to construct the message. Use
+ of the MIME-Version header field indicates that the message is in
+ full compliance with the MIME protocol (as defined in RFC 2045[7]).
+ Proxies/gateways are responsible for ensuring full compliance (where
+ possible) when exporting HTTP messages to strict MIME environments.
+
+ MIME-Version = "MIME-Version" ":" 1*DIGIT "." 1*DIGIT
+
+ MIME version "1.0" is the default for use in HTTP/1.1. However,
+ HTTP/1.1 message parsing and semantics are defined by this document
+ and not the MIME specification.
+
+19.4.2 Conversion to Canonical Form
+
+ RFC 2045 [7] requires that an Internet mail entity be converted to
+ canonical form prior to being transferred, as described in section 4
+ of RFC 2049 [48]. Section 3.7.1 of this document describes the forms
+ allowed for subtypes of the "text" media type when transmitted over
+ HTTP. RFC 2046 requires that content with a type of "text" represent
+ line breaks as CRLF and forbids the use of CR or LF outside of line
+
+
+
+Fielding, et al. Standards Track [Page 167]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ break sequences. HTTP allows CRLF, bare CR, and bare LF to indicate a
+ line break within text content when a message is transmitted over
+ HTTP.
+
+ Where it is possible, a proxy or gateway from HTTP to a strict MIME
+ environment SHOULD translate all line breaks within the text media
+ types described in section 3.7.1 of this document to the RFC 2049
+ canonical form of CRLF. Note, however, that this might be complicated
+ by the presence of a Content-Encoding and by the fact that HTTP
+ allows the use of some character sets which do not use octets 13 and
+ 10 to represent CR and LF, as is the case for some multi-byte
+ character sets.
+
+ Implementors should note that conversion will break any cryptographic
+ checksums applied to the original content unless the original content
+ is already in canonical form. Therefore, the canonical form is
+ recommended for any content that uses such checksums in HTTP.
+
+19.4.3 Conversion of Date Formats
+
+ HTTP/1.1 uses a restricted set of date formats (section 3.3.1) to
+ simplify the process of date comparison. Proxies and gateways from
+ other protocols SHOULD ensure that any Date header field present in a
+ message conforms to one of the HTTP/1.1 formats and rewrite the date
+ if necessary.
+
+19.4.4 Introduction of Content-Encoding
+
+ RFC 2045 does not include any concept equivalent to HTTP/1.1's
+ Content-Encoding header field. Since this acts as a modifier on the
+ media type, proxies and gateways from HTTP to MIME-compliant
+ protocols MUST either change the value of the Content-Type header
+ field or decode the entity-body before forwarding the message. (Some
+ experimental applications of Content-Type for Internet mail have used
+ a media-type parameter of ";conversions=<content-coding>" to perform
+ a function equivalent to Content-Encoding. However, this parameter is
+ not part of RFC 2045.)
+
+19.4.5 No Content-Transfer-Encoding
+
+ HTTP does not use the Content-Transfer-Encoding (CTE) field of RFC
+ 2045. Proxies and gateways from MIME-compliant protocols to HTTP MUST
+ remove any non-identity CTE ("quoted-printable" or "base64") encoding
+ prior to delivering the response message to an HTTP client.
+
+ [[ "MUST remove any CTE encoding prior to delivering the response ]]
+ [[ message to an HTTP client." ]]
+
+ Proxies and gateways from HTTP to MIME-compliant protocols are
+ responsible for ensuring that the message is in the correct format
+ and encoding for safe transport on that protocol, where "safe
+
+
+
+Fielding, et al. Standards Track [Page 168]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ transport" is defined by the limitations of the protocol being used.
+ Such a proxy or gateway SHOULD label the data with an appropriate
+ Content-Transfer-Encoding if doing so will improve the likelihood of
+ safe transport over the destination protocol.
+
+19.4.6 Introduction of Transfer-Encoding
+
+ HTTP/1.1 introduces the Transfer-Encoding header field (section
+ 14.41). Proxies/gateways MUST remove any transfer-coding prior to
+ forwarding a message via a MIME-compliant protocol.
+
+ A process for decoding the "chunked" transfer-coding (section 3.6)
+ can be represented in pseudo-code as:
+
+ length := 0
+ read chunk-size, chunk-extension (if any) and CRLF
+ while (chunk-size > 0) {
+ read chunk-data and CRLF
+ append chunk-data to entity-body
+ length := length + chunk-size
+ read chunk-size and CRLF
+ }
+ read entity-header
+ while (entity-header not empty) {
+ append entity-header to existing header fields
+ read entity-header
+ }
+ Content-Length := length
+ Remove "chunked" from Transfer-Encoding
+
+19.4.7 MHTML and Line Length Limitations
+
+ HTTP implementations which share code with MHTML [45] implementations
+ need to be aware of MIME line length limitations. Since HTTP does not
+ have this limitation, HTTP does not fold long lines. MHTML messages
+ being transported by HTTP follow all conventions of MHTML, including
+ line length limitations and folding, canonicalization, etc., since
+ HTTP transports all message-bodies as payload (see section 3.7.2) and
+ does not interpret the content or any MIME header lines that might be
+ contained therein.
+
+19.5 Additional Features
+
+ RFC 1945 and RFC 2068 document protocol elements used by some
+ existing HTTP implementations, but not consistently and correctly
+ across most HTTP/1.1 applications. Implementors are advised to be
+ aware of these features, but cannot rely upon their presence in, or
+ interoperability with, other HTTP/1.1 applications. Some of these
+
+
+
+Fielding, et al. Standards Track [Page 169]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ describe proposed experimental features, and some describe features
+ that experimental deployment found lacking that are now addressed in
+ the base HTTP/1.1 specification.
+
+ A number of other headers, such as Content-Disposition and Title,
+ from SMTP and MIME are also often implemented (see RFC 2076 [37]).
+
+19.5.1 Content-Disposition
+
+ The Content-Disposition response-header field has been proposed as a
+ means for the origin server to suggest a default filename if the user
+ requests that the content is saved to a file. This usage is derived
+ from the definition of Content-Disposition in RFC 1806 [35].
+
+ content-disposition = "Content-Disposition" ":"
+ disposition-type *( ";" disposition-parm )
+ disposition-type = "attachment" | disp-extension-token
+ disposition-parm = filename-parm | disp-extension-parm
+ filename-parm = "filename" "=" quoted-string
+ disp-extension-token = token
+ disp-extension-parm = token "=" ( token | quoted-string )
+
+ An example is
+
+ Content-Disposition: attachment; filename="fname.ext"
+
+ The receiving user agent SHOULD NOT respect any directory path
+ information present in the filename-parm parameter, which is the only
+ parameter believed to apply to HTTP implementations at this time. The
+ filename SHOULD be treated as a terminal component only.
+
+ If this header is used in a response with the application/octet-
+ stream content-type, the implied suggestion is that the user agent
+ should not display the response, but directly enter a `save response
+ as...' dialog.
+
+ See section 15.5 for Content-Disposition security issues.
+
+19.6 Compatibility with Previous Versions
+
+ It is beyond the scope of a protocol specification to mandate
+ compliance with previous versions. HTTP/1.1 was deliberately
+ designed, however, to make supporting previous versions easy. It is
+ worth noting that, at the time of composing this specification
+ (1996), we would expect commercial HTTP/1.1 servers to:
+
+ - recognize the format of the Request-Line for HTTP/0.9, 1.0, and
+ 1.1 requests;
+
+
+
+Fielding, et al. Standards Track [Page 170]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ - understand any valid request in the format of HTTP/0.9, 1.0, or
+ 1.1;
+
+ - respond appropriately with a message in the same major version
+ used by the client.
+
+ And we would expect HTTP/1.1 clients to:
+
+ - recognize the format of the Status-Line for HTTP/1.0 and 1.1
+ responses;
+
+ - understand any valid response in the format of HTTP/0.9, 1.0, or
+ 1.1.
+
+ For most implementations of HTTP/1.0, each connection is established
+ by the client prior to the request and closed by the server after
+ sending the response. Some implementations implement the Keep-Alive
+ version of persistent connections described in section 19.7.1 of RFC
+ 2068 [33].
+
+19.6.1 Changes from HTTP/1.0
+
+ This section summarizes major differences between versions HTTP/1.0
+ and HTTP/1.1.
+
+19.6.1.1 Changes to Simplify Multi-homed Web Servers and Conserve IP
+ Addresses
+
+ The requirements that clients and servers support the Host request-
+ header, report an error if the Host request-header (section 14.23) is
+ missing from an HTTP/1.1 request, and accept absolute URIs (section
+ 5.1.2) are among the most important changes defined by this
+ specification.
+
+ Older HTTP/1.0 clients assumed a one-to-one relationship of IP
+ addresses and servers; there was no other established mechanism for
+ distinguishing the intended server of a request than the IP address
+ to which that request was directed. The changes outlined above will
+ allow the Internet, once older HTTP clients are no longer common, to
+ support multiple Web sites from a single IP address, greatly
+ simplifying large operational Web servers, where allocation of many
+ IP addresses to a single host has created serious problems. The
+ Internet will also be able to recover the IP addresses that have been
+ allocated for the sole purpose of allowing special-purpose domain
+ names to be used in root-level HTTP URLs. Given the rate of growth of
+ the Web, and the number of servers already deployed, it is extremely
+
+
+
+
+
+Fielding, et al. Standards Track [Page 171]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ important that all implementations of HTTP (including updates to
+ existing HTTP/1.0 applications) correctly implement these
+ requirements:
+
+ - Both clients and servers MUST support the Host request-header.
+
+ - A client that sends an HTTP/1.1 request MUST send a Host header.
+
+ - Servers MUST report a 400 (Bad Request) error if an HTTP/1.1
+ request does not include a Host request-header.
+
+ - Servers MUST accept absolute URIs.
+
+19.6.2 Compatibility with HTTP/1.0 Persistent Connections
+
+ Some clients and servers might wish to be compatible with some
+ previous implementations of persistent connections in HTTP/1.0
+ clients and servers. Persistent connections in HTTP/1.0 are
+ explicitly negotiated as they are not the default behavior. HTTP/1.0
+ experimental implementations of persistent connections are faulty,
+ and the new facilities in HTTP/1.1 are designed to rectify these
+ problems. The problem was that some existing 1.0 clients may be
+ sending Keep-Alive to a proxy server that doesn't understand
+ Connection, which would then erroneously forward it to the next
+ inbound server, which would establish the Keep-Alive connection and
+ result in a hung HTTP/1.0 proxy waiting for the close on the
+ response. The result is that HTTP/1.0 clients must be prevented from
+ using Keep-Alive when talking to proxies.
+
+ However, talking to proxies is the most important use of persistent
+ connections, so that prohibition is clearly unacceptable. Therefore,
+ we need some other mechanism for indicating a persistent connection
+ is desired, which is safe to use even when talking to an old proxy
+ that ignores Connection. Persistent connections are the default for
+ HTTP/1.1 messages; we introduce a new keyword (Connection: close) for
+ declaring non-persistence. See section 14.10.
+
+ The original HTTP/1.0 form of persistent connections (the Connection:
+ Keep-Alive and Keep-Alive header) is documented in RFC 2068. [33]
+
+19.6.3 Changes from RFC 2068
+
+ This specification has been carefully audited to correct and
+ disambiguate key word usage; RFC 2068 had many problems in respect to
+ the conventions laid out in RFC 2119 [34].
+
+ Clarified which error code should be used for inbound server failures
+ (e.g. DNS failures). (Section 10.5.5).
+
+
+
+Fielding, et al. Standards Track [Page 172]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ CREATE had a race that required an Etag be sent when a resource is
+ first created. (Section 10.2.2).
+
+ Content-Base was deleted from the specification: it was not
+ implemented widely, and there is no simple, safe way to introduce it
+ without a robust extension mechanism. In addition, it is used in a
+ similar, but not identical fashion in MHTML [45].
+
+ Transfer-coding and message lengths all interact in ways that
+ required fixing exactly when chunked encoding is used (to allow for
+ transfer encoding that may not be self delimiting); it was important
+ to straighten out exactly how message lengths are computed. (Sections
+ 3.6, 4.4, 7.2.2, 13.5.2, 14.13, 14.16)
+
+ A content-coding of "identity" was introduced, to solve problems
+ discovered in caching. (section 3.5)
+
+ Quality Values of zero should indicate that "I don't want something"
+ to allow clients to refuse a representation. (Section 3.9)
+
+ The use and interpretation of HTTP version numbers has been clarified
+ by RFC 2145. Require proxies to upgrade requests to highest protocol
+ version they support to deal with problems discovered in HTTP/1.0
+ implementations (Section 3.1)
+
+ Charset wildcarding is introduced to avoid explosion of character set
+ names in accept headers. (Section 14.2)
+
+ A case was missed in the Cache-Control model of HTTP/1.1; s-maxage
+ was introduced to add this missing case. (Sections 13.4, 14.8, 14.9,
+ 14.9.3)
+
+ The Cache-Control: max-age directive was not properly defined for
+ responses. (Section 14.9.3)
+
+ There are situations where a server (especially a proxy) does not
+ know the full length of a response but is capable of serving a
+ byterange request. We therefore need a mechanism to allow byteranges
+ with a content-range not indicating the full length of the message.
+ (Section 14.16)
+
+ Range request responses would become very verbose if all meta-data
+ were always returned; by allowing the server to only send needed
+ headers in a 206 response, this problem can be avoided. (Section
+ 10.2.7, 13.5.3, and 14.27)
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 173]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Fix problem with unsatisfiable range requests; there are two cases:
+ syntactic problems, and range doesn't exist in the document. The 416
+ status code was needed to resolve this ambiguity needed to indicate
+ an error for a byte range request that falls outside of the actual
+ contents of a document. (Section 10.4.17, 14.16)
+
+ Rewrite of message transmission requirements to make it much harder
+ for implementors to get it wrong, as the consequences of errors here
+ can have significant impact on the Internet, and to deal with the
+ following problems:
+
+ 1. Changing "HTTP/1.1 or later" to "HTTP/1.1", in contexts where
+ this was incorrectly placing a requirement on the behavior of
+ an implementation of a future version of HTTP/1.x
+
+ 2. Made it clear that user-agents should retry requests, not
+ "clients" in general.
+
+ 3. Converted requirements for clients to ignore unexpected 100
+ (Continue) responses, and for proxies to forward 100 responses,
+ into a general requirement for 1xx responses.
+
+ 4. Modified some TCP-specific language, to make it clearer that
+ non-TCP transports are possible for HTTP.
+
+ 5. Require that the origin server MUST NOT wait for the request
+ body before it sends a required 100 (Continue) response.
+
+ 6. Allow, rather than require, a server to omit 100 (Continue) if
+ it has already seen some of the request body.
+
+ 7. Allow servers to defend against denial-of-service attacks and
+ broken clients.
+
+ This change adds the Expect header and 417 status code. The message
+ transmission requirements fixes are in sections 8.2, 10.4.18,
+ 8.1.2.2, 13.11, and 14.20.
+
+ Proxies should be able to add Content-Length when appropriate.
+ (Section 13.5.2)
+
+ Clean up confusion between 403 and 404 responses. (Section 10.4.4,
+ 10.4.5, and 10.4.11)
+
+ Warnings could be cached incorrectly, or not updated appropriately.
+ (Section 13.1.2, 13.2.4, 13.5.2, 13.5.3, 14.9.3, and 14.46) Warning
+ also needed to be a general header, as PUT or other methods may have
+ need for it in requests.
+
+
+
+Fielding, et al. Standards Track [Page 174]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+ Transfer-coding had significant problems, particularly with
+ interactions with chunked encoding. The solution is that transfer-
+ codings become as full fledged as content-codings. This involves
+ adding an IANA registry for transfer-codings (separate from content
+ codings), a new header field (TE) and enabling trailer headers in the
+ future. Transfer encoding is a major performance benefit, so it was
+ worth fixing [39]. TE also solves another, obscure, downward
+ interoperability problem that could have occurred due to interactions
+ between authentication trailers, chunked encoding and HTTP/1.0
+ clients.(Section 3.6, 3.6.1, and 14.39)
+
+ The PATCH, LINK, UNLINK methods were defined but not commonly
+ implemented in previous versions of this specification. See RFC 2068
+ [33].
+
+ The Alternates, Content-Version, Derived-From, Link, URI, Public and
+ Content-Base header fields were defined in previous versions of this
+ specification, but not commonly implemented. See RFC 2068 [33].
+
+20 Index
+
+ Please see the PostScript version of this RFC for the INDEX.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 175]
+\f
+RFC 2616 HTTP/1.1 June 1999
+
+
+21. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Fielding, et al. Standards Track [Page 176]
+\f
--- /dev/null
+
+[[ Text in double brackets is from the unofficial errata at ]]
+[[ http://skrb.org/ietf/http_errata.html ]]
+
+Network Working Group J. Franks
+Request for Comments: 2617 Northwestern University
+Obsoletes: 2069 P. Hallam-Baker
+Category: Standards Track Verisign, Inc.
+ J. Hostetler
+ AbiSource, Inc.
+ S. Lawrence
+ Agranat Systems, Inc.
+ P. Leach
+ Microsoft Corporation
+ A. Luotonen
+ Netscape Communications Corporation
+ L. Stewart
+ Open Market, Inc.
+ June 1999
+
+
+ HTTP Authentication: Basic and Digest Access Authentication
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+Abstract
+
+ "HTTP/1.0", includes the specification for a Basic Access
+ Authentication scheme. This scheme is not considered to be a secure
+ method of user authentication (unless used in conjunction with some
+ external secure system such as SSL [5]), as the user name and
+ password are passed over the network as cleartext.
+
+ This document also provides the specification for HTTP's
+ authentication framework, the original Basic authentication scheme
+ and a scheme based on cryptographic hashes, referred to as "Digest
+ Access Authentication". It is therefore also intended to serve as a
+ replacement for RFC 2069 [6]. Some optional elements specified by
+ RFC 2069 have been removed from this specification due to problems
+ found since its publication; other new elements have been added for
+ compatibility, those new elements have been made optional, but are
+ strongly recommended.
+
+
+
+Franks, et al. Standards Track [Page 1]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ Like Basic, Digest access authentication verifies that both parties
+ to a communication know a shared secret (a password); unlike Basic,
+ this verification can be done without sending the password in the
+ clear, which is Basic's biggest weakness. As with most other
+ authentication protocols, the greatest sources of risks are usually
+ found not in the core protocol itself but in policies and procedures
+ surrounding its use.
+
+Table of Contents
+
+ 1 Access Authentication................................ 3
+ 1.1 Reliance on the HTTP/1.1 Specification............ 3
+ 1.2 Access Authentication Framework................... 3
+ 2 Basic Authentication Scheme.......................... 5
+ 3 Digest Access Authentication Scheme.................. 6
+ 3.1 Introduction...................................... 6
+ 3.1.1 Purpose......................................... 6
+ 3.1.2 Overall Operation............................... 6
+ 3.1.3 Representation of digest values................. 7
+ 3.1.4 Limitations..................................... 7
+ 3.2 Specification of Digest Headers................... 7
+ 3.2.1 The WWW-Authenticate Response Header............ 8
+ 3.2.2 The Authorization Request Header................ 11
+ 3.2.3 The Authentication-Info Header.................. 15
+ 3.3 Digest Operation.................................. 17
+ 3.4 Security Protocol Negotiation..................... 18
+ 3.5 Example........................................... 18
+ 3.6 Proxy-Authentication and Proxy-Authorization...... 19
+ 4 Security Considerations.............................. 19
+ 4.1 Authentication of Clients using Basic
+ Authentication.................................... 19
+ 4.2 Authentication of Clients using Digest
+ Authentication.................................... 20
+ 4.3 Limited Use Nonce Values.......................... 21
+ 4.4 Comparison of Digest with Basic Authentication.... 22
+ 4.5 Replay Attacks.................................... 22
+ 4.6 Weakness Created by Multiple Authentication
+ Schemes........................................... 23
+ 4.7 Online dictionary attacks......................... 23
+ 4.8 Man in the Middle................................. 24
+ 4.9 Chosen plaintext attacks.......................... 24
+ 4.10 Precomputed dictionary attacks.................... 25
+ 4.11 Batch brute force attacks......................... 25
+ 4.12 Spoofing by Counterfeit Servers................... 25
+ 4.13 Storing passwords................................. 26
+ 4.14 Summary........................................... 26
+ 5 Sample implementation................................ 27
+ 6 Acknowledgments...................................... 31
+
+
+
+Franks, et al. Standards Track [Page 2]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ 7 References........................................... 31
+ 8 Authors' Addresses................................... 32
+ 9 Full Copyright Statement............................. 34
+
+1 Access Authentication
+
+1.1 Reliance on the HTTP/1.1 Specification
+
+ This specification is a companion to the HTTP/1.1 specification [2].
+ It uses the augmented BNF section 2.1 of that document, and relies on
+ both the non-terminals defined in that document and other aspects of
+ the HTTP/1.1 specification.
+
+1.2 Access Authentication Framework
+
+ HTTP provides a simple challenge-response authentication mechanism
+ that MAY be used by a server to challenge a client request and by a
+ client to provide authentication information. It uses an extensible,
+ case-insensitive token to identify the authentication scheme,
+ followed by a comma-separated list of attribute-value pairs which
+ carry the parameters necessary for achieving authentication via that
+ scheme.
+
+ auth-scheme = token
+ auth-param = token "=" ( token | quoted-string )
+
+ The 401 (Unauthorized) response message is used by an origin server
+ to challenge the authorization of a user agent. This response MUST
+ include a WWW-Authenticate header field containing at least one
+ challenge applicable to the requested resource. The 407 (Proxy
+ Authentication Required) response message is used by a proxy to
+ challenge the authorization of a client and MUST include a Proxy-
+ Authenticate header field containing at least one challenge
+ applicable to the proxy for the requested resource.
+
+ challenge = auth-scheme 1*SP 1#auth-param
+
+ Note: User agents will need to take special care in parsing the WWW-
+ Authenticate or Proxy-Authenticate header field value if it contains
+ more than one challenge, or if more than one WWW-Authenticate header
+ field is provided, since the contents of a challenge may itself
+ contain a comma-separated list of authentication parameters.
+
+ The authentication parameter realm is defined for all authentication
+ schemes:
+
+ realm = "realm" "=" realm-value
+ realm-value = quoted-string
+
+
+
+Franks, et al. Standards Track [Page 3]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ The realm directive (case-insensitive) is required for all
+ authentication schemes that issue a challenge. The realm value
+ (case-sensitive), in combination with the canonical root URL (the
+ absoluteURI for the server whose abs_path is empty; see section 5.1.2
+ of [2]) of the server being accessed, defines the protection space.
+ These realms allow the protected resources on a server to be
+ partitioned into a set of protection spaces, each with its own
+ authentication scheme and/or authorization database. The realm value
+ is a string, generally assigned by the origin server, which may have
+ additional semantics specific to the authentication scheme. Note that
+ there may be multiple challenges with the same auth-scheme but
+ different realms.
+
+ A user agent that wishes to authenticate itself with an origin
+ server--usually, but not necessarily, after receiving a 401
+ (Unauthorized)--MAY do so by including an Authorization header field
+ with the request. A client that wishes to authenticate itself with a
+ proxy--usually, but not necessarily, after receiving a 407 (Proxy
+ Authentication Required)--MAY do so by including a Proxy-
+ Authorization header field with the request. Both the Authorization
+ field value and the Proxy-Authorization field value consist of
+ credentials containing the authentication information of the client
+ for the realm of the resource being requested. The user agent MUST
+ choose to use one of the challenges with the strongest auth-scheme it
+ understands and request credentials from the user based upon that
+ challenge.
+
+ credentials = auth-scheme #auth-param
+
+ Note that many browsers will only recognize Basic and will require
+ that it be the first auth-scheme presented. Servers should only
+ include Basic if it is minimally acceptable.
+
+ The protection space determines the domain over which credentials can
+ be automatically applied. If a prior request has been authorized, the
+ same credentials MAY be reused for all other requests within that
+ protection space for a period of time determined by the
+ authentication scheme, parameters, and/or user preference. Unless
+ otherwise defined by the authentication scheme, a single protection
+ space cannot extend outside the scope of its server.
+
+ If the origin server does not wish to accept the credentials sent
+ with a request, it SHOULD return a 401 (Unauthorized) response. The
+ response MUST include a WWW-Authenticate header field containing at
+ least one (possibly new) challenge applicable to the requested
+ resource. If a proxy does not accept the credentials sent with a
+ request, it SHOULD return a 407 (Proxy Authentication Required). The
+ response MUST include a Proxy-Authenticate header field containing a
+
+
+
+Franks, et al. Standards Track [Page 4]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ (possibly new) challenge applicable to the proxy for the requested
+ resource.
+
+ The HTTP protocol does not restrict applications to this simple
+ challenge-response mechanism for access authentication. Additional
+ mechanisms MAY be used, such as encryption at the transport level or
+ via message encapsulation, and with additional header fields
+ specifying authentication information. However, these additional
+ mechanisms are not defined by this specification.
+
+ Proxies MUST be completely transparent regarding user agent
+ authentication by origin servers. That is, they must forward the
+ WWW-Authenticate and Authorization headers untouched, and follow the
+ rules found in section 14.8 of [2]. Both the Proxy-Authenticate and
+ the Proxy-Authorization header fields are hop-by-hop headers (see
+ section 13.5.1 of [2]).
+
+2 Basic Authentication Scheme
+
+ The "basic" authentication scheme is based on the model that the
+ client must authenticate itself with a user-ID and a password for
+ each realm. The realm value should be considered an opaque string
+ which can only be compared for equality with other realms on that
+ server. The server will service the request only if it can validate
+ the user-ID and password for the protection space of the Request-URI.
+ There are no optional authentication parameters.
+
+ For Basic, the framework above is utilized as follows:
+
+ challenge = "Basic" realm
+ credentials = "Basic" basic-credentials
+
+ Upon receipt of an unauthorized request for a URI within the
+ protection space, the origin server MAY respond with a challenge like
+ the following:
+
+ WWW-Authenticate: Basic realm="WallyWorld"
+
+ where "WallyWorld" is the string assigned by the server to identify
+ the protection space of the Request-URI. A proxy may respond with the
+ same challenge using the Proxy-Authenticate header field.
+
+ To receive authorization, the client sends the userid and password,
+ separated by a single colon (":") character, within a base64 [7]
+ encoded string in the credentials.
+
+ basic-credentials = base64-user-pass
+ base64-user-pass = <base64 [4] encoding of user-pass,
+
+
+
+Franks, et al. Standards Track [Page 5]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ except not limited to 76 char/line>
+ user-pass = userid ":" password
+ userid = *<TEXT excluding ":">
+ password = *TEXT
+
+ Userids might be case sensitive.
+
+ If the user agent wishes to send the userid "Aladdin" and password
+ "open sesame", it would use the following header field:
+
+ Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+
+ A client SHOULD assume that all paths at or deeper than the depth of
+ the last symbolic element in the path field of the Request-URI also
+ are within the protection space specified by the Basic realm value of
+ the current challenge. A client MAY preemptively send the
+ corresponding Authorization header with requests for resources in
+ that space without receipt of another challenge from the server.
+ Similarly, when a client sends a request to a proxy, it may reuse a
+ userid and password in the Proxy-Authorization header field without
+ receiving another challenge from the proxy server. See section 4 for
+ security considerations associated with Basic authentication.
+
+3 Digest Access Authentication Scheme
+
+3.1 Introduction
+
+3.1.1 Purpose
+
+ The protocol referred to as "HTTP/1.0" includes the specification for
+ a Basic Access Authentication scheme[1]. That scheme is not
+ considered to be a secure method of user authentication, as the user
+ name and password are passed over the network in an unencrypted form.
+ This section provides the specification for a scheme that does not
+ send the password in cleartext, referred to as "Digest Access
+ Authentication".
+
+ The Digest Access Authentication scheme is not intended to be a
+ complete answer to the need for security in the World Wide Web. This
+ scheme provides no encryption of message content. The intent is
+ simply to create an access authentication method that avoids the most
+ serious flaws of Basic authentication.
+
+3.1.2 Overall Operation
+
+ Like Basic Access Authentication, the Digest scheme is based on a
+ simple challenge-response paradigm. The Digest scheme challenges
+ using a nonce value. A valid response contains a checksum (by
+
+
+
+Franks, et al. Standards Track [Page 6]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ default, the MD5 checksum) of the username, the password, the given
+ nonce value, the HTTP method, and the requested URI. In this way, the
+ password is never sent in the clear. Just as with the Basic scheme,
+ the username and password must be prearranged in some fashion not
+ addressed by this document.
+
+3.1.3 Representation of digest values
+
+ An optional header allows the server to specify the algorithm used to
+ create the checksum or digest. By default the MD5 algorithm is used
+ and that is the only algorithm described in this document.
+
+ For the purposes of this document, an MD5 digest of 128 bits is
+ represented as 32 ASCII printable characters. The bits in the 128 bit
+ digest are converted from most significant to least significant bit,
+ four bits at a time to their ASCII presentation as follows. Each four
+ bits is represented by its familiar hexadecimal notation from the
+ characters 0123456789abcdef. That is, binary 0000 gets represented by
+ the character '0', 0001, by '1', and so on up to the representation
+ of 1111 as 'f'.
+
+3.1.4 Limitations
+
+ The Digest authentication scheme described in this document suffers
+ from many known limitations. It is intended as a replacement for
+ Basic authentication and nothing more. It is a password-based system
+ and (on the server side) suffers from all the same problems of any
+ password system. In particular, no provision is made in this protocol
+ for the initial secure arrangement between user and server to
+ establish the user's password.
+
+ Users and implementors should be aware that this protocol is not as
+ secure as Kerberos, and not as secure as any client-side private-key
+ scheme. Nevertheless it is better than nothing, better than what is
+ commonly used with telnet and ftp, and better than Basic
+ authentication.
+
+3.2 Specification of Digest Headers
+
+ The Digest Access Authentication scheme is conceptually similar to
+ the Basic scheme. The formats of the modified WWW-Authenticate header
+ line and the Authorization header line are specified below. In
+ addition, a new header, Authentication-Info, is specified.
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 7]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+3.2.1 The WWW-Authenticate Response Header
+
+ If a server receives a request for an access-protected object, and an
+ acceptable Authorization header is not sent, the server responds with
+ a "401 Unauthorized" status code, and a WWW-Authenticate header as
+ per the framework defined above, which for the digest scheme is
+ utilized as follows:
+
+ challenge = "Digest" digest-challenge
+
+ digest-challenge = 1#( realm | [ domain ] | nonce |
+ [ opaque ] |[ stale ] | [ algorithm ] |
+ [ qop-options ] | [auth-param] )
+
+
+ domain = "domain" "=" <"> URI ( 1*SP URI ) <">
+ [[ Should be: ]]
+ [[ domain = "domain" "=" <"> URI *( 1*SP URI ) <"> ]]
+ URI = absoluteURI | abs_path
+ nonce = "nonce" "=" nonce-value
+ nonce-value = quoted-string
+ opaque = "opaque" "=" quoted-string
+ stale = "stale" "=" ( "true" | "false" )
+ algorithm = "algorithm" "=" ( "MD5" | "MD5-sess" |
+ token )
+ qop-options = "qop" "=" <"> 1#qop-value <">
+ qop-value = "auth" | "auth-int" | token
+
+ The meanings of the values of the directives used above are as
+ follows:
+
+ realm
+ A string to be displayed to users so they know which username and
+ password to use. This string should contain at least the name of
+ the host performing the authentication and might additionally
+ indicate the collection of users who might have access. An example
+ might be "registered_users@gotham.news.com".
+
+ domain
+ A quoted, space-separated list of URIs, as specified in RFC XURI
+ [7], that define the protection space. If a URI is an abs_path, it
+ is relative to the canonical root URL (see section 1.2 above) of
+ the server being accessed. An absoluteURI in this list may refer to
+ a different server than the one being accessed. The client can use
+ this list to determine the set of URIs for which the same
+ authentication information may be sent: any URI that has a URI in
+ this list as a prefix (after both have been made absolute) may be
+ assumed to be in the same protection space. If this directive is
+ omitted or its value is empty, the client should assume that the
+ protection space consists of all URIs on the responding server.
+
+
+
+Franks, et al. Standards Track [Page 8]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ This directive is not meaningful in Proxy-Authenticate headers, for
+ which the protection space is always the entire proxy; if present
+ it should be ignored.
+
+ nonce
+ A server-specified data string which should be uniquely generated
+ each time a 401 response is made. It is recommended that this
+ string be base64 or hexadecimal data. Specifically, since the
+ string is passed in the header lines as a quoted string, the
+ double-quote character is not allowed.
+
+ The contents of the nonce are implementation dependent. The quality
+ of the implementation depends on a good choice. A nonce might, for
+ example, be constructed as the base 64 encoding of
+
+ time-stamp H(time-stamp ":" ETag ":" private-key)
+
+ where time-stamp is a server-generated time or other non-repeating
+ value, ETag is the value of the HTTP ETag header associated with
+ the requested entity, and private-key is data known only to the
+ server. With a nonce of this form a server would recalculate the
+ hash portion after receiving the client authentication header and
+ reject the request if it did not match the nonce from that header
+ or if the time-stamp value is not recent enough. In this way the
+ server can limit the time of the nonce's validity. The inclusion of
+ the ETag prevents a replay request for an updated version of the
+ resource. (Note: including the IP address of the client in the
+ nonce would appear to offer the server the ability to limit the
+ reuse of the nonce to the same client that originally got it.
+ However, that would break proxy farms, where requests from a single
+ user often go through different proxies in the farm. Also, IP
+ address spoofing is not that hard.)
+
+ An implementation might choose not to accept a previously used
+ nonce or a previously used digest, in order to protect against a
+ replay attack. Or, an implementation might choose to use one-time
+ nonces or digests for POST or PUT requests and a time-stamp for GET
+ requests. For more details on the issues involved see section 4.
+ of this document.
+
+ The nonce is opaque to the client.
+
+ opaque
+ A string of data, specified by the server, which should be returned
+ by the client unchanged in the Authorization header of subsequent
+ requests with URIs in the same protection space. It is recommended
+ that this string be base64 or hexadecimal data.
+
+
+
+
+Franks, et al. Standards Track [Page 9]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ stale
+ A flag, indicating that the previous request from the client was
+ rejected because the nonce value was stale. If stale is TRUE
+ (case-insensitive), the client may wish to simply retry the request
+ with a new encrypted response, without reprompting the user for a
+ new username and password. The server should only set stale to TRUE
+ if it receives a request for which the nonce is invalid but with a
+ valid digest for that nonce (indicating that the client knows the
+ correct username/password). If stale is FALSE, or anything other
+ than TRUE, or the stale directive is not present, the username
+ and/or password are invalid, and new values must be obtained.
+
+ algorithm
+ A string indicating a pair of algorithms used to produce the digest
+ and a checksum. If this is not present it is assumed to be "MD5".
+ If the algorithm is not understood, the challenge should be ignored
+ (and a different one used, if there is more than one).
+
+ In this document the string obtained by applying the digest
+ algorithm to the data "data" with secret "secret" will be denoted
+ by KD(secret, data), and the string obtained by applying the
+ checksum algorithm to the data "data" will be denoted H(data). The
+ notation unq(X) means the value of the quoted-string X without the
+ surrounding quotes.
+
+ For the "MD5" and "MD5-sess" algorithms
+
+ H(data) = MD5(data)
+
+ and
+
+ KD(secret, data) = H(concat(secret, ":", data))
+
+ i.e., the digest is the MD5 of the secret concatenated with a colon
+ concatenated with the data. The "MD5-sess" algorithm is intended to
+ allow efficient 3rd party authentication servers; for the
+ difference in usage, see the description in section 3.2.2.2.
+
+ qop-options
+ This directive is optional, but is made so only for backward
+ compatibility with RFC 2069 [6]; it SHOULD be used by all
+ implementations compliant with this version of the Digest scheme.
+ If present, it is a quoted string of one or more tokens indicating
+ the "quality of protection" values supported by the server. The
+ value "auth" indicates authentication; the value "auth-int"
+ indicates authentication with integrity protection; see the
+
+
+
+
+
+Franks, et al. Standards Track [Page 10]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ descriptions below for calculating the response directive value for
+ the application of this choice. Unrecognized options MUST be
+ ignored.
+
+ auth-param
+ This directive allows for future extensions. Any unrecognized
+ directive MUST be ignored.
+
+3.2.2 The Authorization Request Header
+
+ The client is expected to retry the request, passing an Authorization
+ header line, which is defined according to the framework above,
+ utilized as follows.
+
+ credentials = "Digest" digest-response
+ digest-response = 1#( username | realm | nonce | digest-uri
+ | response | [ algorithm ] | [cnonce] |
+ [opaque] | [message-qop] |
+ [nonce-count] | [auth-param] )
+
+ username = "username" "=" username-value
+ username-value = quoted-string
+ digest-uri = "uri" "=" digest-uri-value
+ digest-uri-value = request-uri ; As specified by HTTP/1.1
+ message-qop = "qop" "=" qop-value
+ cnonce = "cnonce" "=" cnonce-value
+ cnonce-value = nonce-value
+ nonce-count = "nc" "=" nc-value
+ nc-value = 8LHEX
+ response = "response" "=" request-digest
+ request-digest = <"> 32LHEX <">
+ LHEX = "0" | "1" | "2" | "3" |
+ "4" | "5" | "6" | "7" |
+ "8" | "9" | "a" | "b" |
+ "c" | "d" | "e" | "f"
+
+ The values of the opaque and algorithm fields must be those supplied
+ in the WWW-Authenticate response header for the entity being
+ requested.
+
+ response
+ A string of 32 hex digits computed as defined below, which proves
+ that the user knows a password
+
+ username
+ The user's name in the specified realm.
+
+
+
+
+
+Franks, et al. Standards Track [Page 11]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ digest-uri
+ The URI from Request-URI of the Request-Line; duplicated here
+ because proxies are allowed to change the Request-Line in transit.
+
+ qop
+ Indicates what "quality of protection" the client has applied to
+ the message. If present, its value MUST be one of the alternatives
+ the server indicated it supports in the WWW-Authenticate header.
+ These values affect the computation of the request-digest. Note
+ that this is a single token, not a quoted list of alternatives as
+ in WWW- Authenticate. This directive is optional in order to
+ preserve backward compatibility with a minimal implementation of
+ RFC 2069 [6], but SHOULD be used if the server indicated that qop
+ is supported by providing a qop directive in the WWW-Authenticate
+ header field.
+
+ cnonce
+ This MUST be specified if a qop directive is sent (see above), and
+ MUST NOT be specified if the server did not send a qop directive in
+ the WWW-Authenticate header field. The cnonce-value is an opaque
+ quoted string value provided by the client and used by both client
+ and server to avoid chosen plaintext attacks, to provide mutual
+ authentication, and to provide some message integrity protection.
+ See the descriptions below of the calculation of the response-
+ digest and request-digest values.
+
+ nonce-count
+ This MUST be specified if a qop directive is sent (see above), and
+ MUST NOT be specified if the server did not send a qop directive in
+ the WWW-Authenticate header field. The nc-value is the hexadecimal
+ count of the number of requests (including the current request)
+ that the client has sent with the nonce value in this request. For
+ example, in the first request sent in response to a given nonce
+ value, the client sends "nc=00000001". The purpose of this
+ directive is to allow the server to detect request replays by
+ maintaining its own copy of this count - if the same nc-value is
+ seen twice, then the request is a replay. See the description
+ below of the construction of the request-digest value.
+
+ auth-param
+ This directive allows for future extensions. Any unrecognized
+ directive MUST be ignored.
+
+ If a directive or its value is improper, or required directives are
+ missing, the proper response is 400 Bad Request. If the request-
+ digest is invalid, then a login failure should be logged, since
+ repeated login failures from a single client may indicate an attacker
+ attempting to guess passwords.
+
+
+
+Franks, et al. Standards Track [Page 12]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ The definition of request-digest above indicates the encoding for its
+ value. The following definitions show how the value is computed.
+
+3.2.2.1 Request-Digest
+
+ If the "qop" value is "auth" or "auth-int":
+
+ request-digest = <"> < KD ( H(A1), unq(nonce-value)
+ ":" nc-value
+ ":" unq(cnonce-value)
+ ":" unq(qop-value)
+ ":" H(A2)
+ ) <">
+
+ If the "qop" directive is not present (this construction is for
+ compatibility with RFC 2069):
+
+ request-digest =
+ <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) >
+ <">
+
+ See below for the definitions for A1 and A2.
+
+3.2.2.2 A1
+
+ If the "algorithm" directive's value is "MD5" or is unspecified, then
+ A1 is:
+
+ A1 = unq(username-value) ":" unq(realm-value) ":" passwd
+
+ where
+
+ passwd = < user's password >
+
+ If the "algorithm" directive's value is "MD5-sess", then A1 is
+ calculated only once - on the first request by the client following
+ receipt of a WWW-Authenticate challenge from the server. It uses the
+ server nonce from that challenge, and the first client nonce value to
+ construct A1 as follows:
+
+ A1 = H( unq(username-value) ":" unq(realm-value)
+ ":" passwd )
+ ":" unq(nonce-value) ":" unq(cnonce-value)
+
+ This creates a 'session key' for the authentication of subsequent
+ requests and responses which is different for each "authentication
+ session", thus limiting the amount of material hashed with any one
+ key. (Note: see further discussion of the authentication session in
+
+
+
+Franks, et al. Standards Track [Page 13]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ section 3.3.) Because the server need only use the hash of the user
+ credentials in order to create the A1 value, this construction could
+ be used in conjunction with a third party authentication service so
+ that the web server would not need the actual password value. The
+ specification of such a protocol is beyond the scope of this
+ specification.
+
+3.2.2.3 A2
+
+ If the "qop" directive's value is "auth" or is unspecified, then A2
+ is:
+
+ A2 = Method ":" digest-uri-value
+
+ If the "qop" value is "auth-int", then A2 is:
+
+ A2 = Method ":" digest-uri-value ":" H(entity-body)
+
+3.2.2.4 Directive values and quoted-string
+
+ Note that the value of many of the directives, such as "username-
+ value", are defined as a "quoted-string". However, the "unq" notation
+ indicates that surrounding quotation marks are removed in forming the
+ string A1. Thus if the Authorization header includes the fields
+
+ username="Mufasa", realm=myhost@testrealm.com
+
+ and the user Mufasa has password "Circle Of Life" then H(A1) would be
+ H(Mufasa:myhost@testrealm.com:Circle Of Life) with no quotation marks
+ in the digested string.
+
+ No white space is allowed in any of the strings to which the digest
+ function H() is applied unless that white space exists in the quoted
+ strings or entity body whose contents make up the string to be
+ digested. For example, the string A1 illustrated above must be
+
+ Mufasa:myhost@testrealm.com:Circle Of Life
+
+ with no white space on either side of the colons, but with the white
+ space between the words used in the password value. Likewise, the
+ other strings digested by H() must not have white space on either
+ side of the colons which delimit their fields unless that white space
+ was in the quoted strings or entity body being digested.
+
+ Also note that if integrity protection is applied (qop=auth-int), the
+ H(entity-body) is the hash of the entity body, not the message body -
+ it is computed before any transfer encoding is applied by the sender
+
+
+
+
+Franks, et al. Standards Track [Page 14]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ and after it has been removed by the recipient. Note that this
+ includes multipart boundaries and embedded headers in each part of
+ any multipart content-type.
+
+3.2.2.5 Various considerations
+
+ The "Method" value is the HTTP request method as specified in section
+ 5.1.1 of [2]. The "request-uri" value is the Request-URI from the
+ request line as specified in section 5.1.2 of [2]. This may be "*",
+ an "absoluteURL" or an "abs_path" as specified in section 5.1.2 of
+ [2], but it MUST agree with the Request-URI. In particular, it MUST
+ be an "absoluteURL" if the Request-URI is an "absoluteURL". The
+ "cnonce-value" is an optional client-chosen value whose purpose is
+ to foil chosen plaintext attacks.
+
+ The authenticating server must assure that the resource designated by
+ the "uri" directive is the same as the resource specified in the
+ Request-Line; if they are not, the server SHOULD return a 400 Bad
+ Request error. (Since this may be a symptom of an attack, server
+ implementers may want to consider logging such errors.) The purpose
+ of duplicating information from the request URL in this field is to
+ deal with the possibility that an intermediate proxy may alter the
+ client's Request-Line. This altered (but presumably semantically
+ equivalent) request would not result in the same digest as that
+ calculated by the client.
+
+ Implementers should be aware of how authenticated transactions
+ interact with shared caches. The HTTP/1.1 protocol specifies that
+ when a shared cache (see section 13.7 of [2]) has received a request
+ containing an Authorization header and a response from relaying that
+ request, it MUST NOT return that response as a reply to any other
+ request, unless one of two Cache-Control (see section 14.9 of [2])
+ directives was present in the response. If the original response
+ included the "must-revalidate" Cache-Control directive, the cache MAY
+ use the entity of that response in replying to a subsequent request,
+ but MUST first revalidate it with the origin server, using the
+ request headers from the new request to allow the origin server to
+ authenticate the new request. Alternatively, if the original response
+ included the "public" Cache-Control directive, the response entity
+ MAY be returned in reply to any subsequent request.
+
+3.2.3 The Authentication-Info Header
+
+ The Authentication-Info header is used by the server to communicate
+ some information regarding the successful authentication in the
+ response.
+
+
+
+
+
+Franks, et al. Standards Track [Page 15]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ AuthenticationInfo = "Authentication-Info" ":" auth-info
+ auth-info = 1#(nextnonce | [ message-qop ]
+ | [ response-auth ] | [ cnonce ]
+ | [nonce-count] )
+ nextnonce = "nextnonce" "=" nonce-value
+ response-auth = "rspauth" "=" response-digest
+ response-digest = <"> *LHEX <">
+
+ The value of the nextnonce directive is the nonce the server wishes
+ the client to use for a future authentication response. The server
+ may send the Authentication-Info header with a nextnonce field as a
+ means of implementing one-time or otherwise changing nonces. If the
+ nextnonce field is present the client SHOULD use it when constructing
+ the Authorization header for its next request. Failure of the client
+ to do so may result in a request to re-authenticate from the server
+ with the "stale=TRUE".
+
+ Server implementations should carefully consider the performance
+ implications of the use of this mechanism; pipelined requests will
+ not be possible if every response includes a nextnonce directive
+ that must be used on the next request received by the server.
+ Consideration should be given to the performance vs. security
+ tradeoffs of allowing an old nonce value to be used for a limited
+ time to permit request pipelining. Use of the nonce-count can
+ retain most of the security advantages of a new server nonce
+ without the deleterious affects on pipelining.
+
+ message-qop
+ Indicates the "quality of protection" options applied to the
+ response by the server. The value "auth" indicates authentication;
+ the value "auth-int" indicates authentication with integrity
+ protection. The server SHOULD use the same value for the message-
+ qop directive in the response as was sent by the client in the
+ corresponding request.
+
+ The optional response digest in the "response-auth" directive
+ supports mutual authentication -- the server proves that it knows the
+ user's secret, and with qop=auth-int also provides limited integrity
+ protection of the response. The "response-digest" value is calculated
+ as for the "request-digest" in the Authorization header, except that
+ if "qop=auth" or is not specified in the Authorization header for the
+ request, A2 is
+
+ A2 = ":" digest-uri-value
+
+ and if "qop=auth-int", then A2 is
+
+ A2 = ":" digest-uri-value ":" H(entity-body)
+
+
+
+Franks, et al. Standards Track [Page 16]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ where "digest-uri-value" is the value of the "uri" directive on the
+ Authorization header in the request. The "cnonce-value" and "nc-
+ value" MUST be the ones for the client request to which this message
+ is the response. The "response-auth", "cnonce", and "nonce-count"
+ directives MUST BE present if "qop=auth" or "qop=auth-int" is
+ specified.
+
+ The Authentication-Info header is allowed in the trailer of an HTTP
+ message transferred via chunked transfer-coding.
+
+3.3 Digest Operation
+
+ Upon receiving the Authorization header, the server may check its
+ validity by looking up the password that corresponds to the submitted
+ username. Then, the server must perform the same digest operation
+ (e.g., MD5) performed by the client, and compare the result to the
+ given request-digest value.
+
+ Note that the HTTP server does not actually need to know the user's
+ cleartext password. As long as H(A1) is available to the server, the
+ validity of an Authorization header may be verified.
+
+ The client response to a WWW-Authenticate challenge for a protection
+ space starts an authentication session with that protection space.
+ The authentication session lasts until the client receives another
+ WWW-Authenticate challenge from any server in the protection space. A
+ client should remember the username, password, nonce, nonce count and
+ opaque values associated with an authentication session to use to
+ construct the Authorization header in future requests within that
+ protection space. The Authorization header may be included
+ preemptively; doing so improves server efficiency and avoids extra
+ round trips for authentication challenges. The server may choose to
+ accept the old Authorization header information, even though the
+ nonce value included might not be fresh. Alternatively, the server
+ may return a 401 response with a new nonce value, causing the client
+ to retry the request; by specifying stale=TRUE with this response,
+ the server tells the client to retry with the new nonce, but without
+ prompting for a new username and password.
+
+ Because the client is required to return the value of the opaque
+ directive given to it by the server for the duration of a session,
+ the opaque data may be used to transport authentication session state
+ information. (Note that any such use can also be accomplished more
+ easily and safely by including the state in the nonce.) For example,
+ a server could be responsible for authenticating content that
+ actually sits on another server. It would achieve this by having the
+ first 401 response include a domain directive whose value includes a
+ URI on the second server, and an opaque directive whose value
+
+
+
+Franks, et al. Standards Track [Page 17]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ contains the state information. The client will retry the request, at
+ which time the server might respond with a 301/302 redirection,
+ pointing to the URI on the second server. The client will follow the
+ redirection, and pass an Authorization header , including the
+ <opaque> data.
+
+ As with the basic scheme, proxies must be completely transparent in
+ the Digest access authentication scheme. That is, they must forward
+ the WWW-Authenticate, Authentication-Info and Authorization headers
+ untouched. If a proxy wants to authenticate a client before a request
+ is forwarded to the server, it can be done using the Proxy-
+ Authenticate and Proxy-Authorization headers described in section 3.6
+ below.
+
+3.4 Security Protocol Negotiation
+
+ It is useful for a server to be able to know which security schemes a
+ client is capable of handling.
+
+ It is possible that a server may want to require Digest as its
+ authentication method, even if the server does not know that the
+ client supports it. A client is encouraged to fail gracefully if the
+ server specifies only authentication schemes it cannot handle.
+
+3.5 Example
+
+ The following example assumes that an access-protected document is
+ being requested from the server via a GET request. The URI of the
+ document is "http://www.nowhere.org/dir/index.html". Both client and
+ server know that the username for this document is "Mufasa", and the
+ password is "Circle Of Life" (with one space between each of the
+ three words).
+
+ The first time the client requests the document, no Authorization
+ header is sent, so the server responds with:
+
+ HTTP/1.1 401 Unauthorized
+ WWW-Authenticate: Digest
+ realm="testrealm@host.com",
+ qop="auth,auth-int",
+ nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+ opaque="5ccc069c403ebaf9f0171e9517f40e41"
+
+ The client may prompt the user for the username and password, after
+ which it will respond with a new request, including the following
+ Authorization header:
+
+
+
+
+
+Franks, et al. Standards Track [Page 18]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ Authorization: Digest username="Mufasa",
+ realm="testrealm@host.com",
+ nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+ uri="/dir/index.html",
+ qop=auth,
+ nc=00000001,
+ cnonce="0a4f113b",
+ response="6629fae49393a05397450978507c4ef1",
+ opaque="5ccc069c403ebaf9f0171e9517f40e41"
+
+3.6 Proxy-Authentication and Proxy-Authorization
+
+ The digest authentication scheme may also be used for authenticating
+ users to proxies, proxies to proxies, or proxies to origin servers by
+ use of the Proxy-Authenticate and Proxy-Authorization headers. These
+ headers are instances of the Proxy-Authenticate and Proxy-
+ Authorization headers specified in sections 10.33 and 10.34 of the
+ HTTP/1.1 specification [2] and their behavior is subject to
+ restrictions described there. The transactions for proxy
+ authentication are very similar to those already described. Upon
+ receiving a request which requires authentication, the proxy/server
+ must issue the "407 Proxy Authentication Required" response with a
+ "Proxy-Authenticate" header. The digest-challenge used in the
+ Proxy-Authenticate header is the same as that for the WWW-
+ Authenticate header as defined above in section 3.2.1.
+
+ The client/proxy must then re-issue the request with a Proxy-
+ Authorization header, with directives as specified for the
+ Authorization header in section 3.2.2 above.
+
+ On subsequent responses, the server sends Proxy-Authentication-Info
+ with directives the same as those for the Authentication-Info header
+ field.
+
+ Note that in principle a client could be asked to authenticate itself
+ to both a proxy and an end-server, but never in the same response.
+
+4 Security Considerations
+
+4.1 Authentication of Clients using Basic Authentication
+
+ The Basic authentication scheme is not a secure method of user
+ authentication, nor does it in any way protect the entity, which is
+ transmitted in cleartext across the physical network used as the
+ carrier. HTTP does not prevent additional authentication schemes and
+ encryption mechanisms from being employed to increase security or the
+ addition of enhancements (such as schemes to use one-time passwords)
+ to Basic authentication.
+
+
+
+Franks, et al. Standards Track [Page 19]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ The most serious flaw in Basic authentication is that it results in
+ the essentially cleartext transmission of the user's password over
+ the physical network. It is this problem which Digest Authentication
+ attempts to address.
+
+ Because Basic authentication involves the cleartext transmission of
+ passwords it SHOULD NOT be used (without enhancements) to protect
+ sensitive or valuable information.
+
+ A common use of Basic authentication is for identification purposes
+ -- requiring the user to provide a user name and password as a means
+ of identification, for example, for purposes of gathering accurate
+ usage statistics on a server. When used in this way it is tempting to
+ think that there is no danger in its use if illicit access to the
+ protected documents is not a major concern. This is only correct if
+ the server issues both user name and password to the users and in
+ particular does not allow the user to choose his or her own password.
+ The danger arises because naive users frequently reuse a single
+ password to avoid the task of maintaining multiple passwords.
+
+ If a server permits users to select their own passwords, then the
+ threat is not only unauthorized access to documents on the server but
+ also unauthorized access to any other resources on other systems that
+ the user protects with the same password. Furthermore, in the
+ server's password database, many of the passwords may also be users'
+ passwords for other sites. The owner or administrator of such a
+ system could therefore expose all users of the system to the risk of
+ unauthorized access to all those sites if this information is not
+ maintained in a secure fashion.
+
+ Basic Authentication is also vulnerable to spoofing by counterfeit
+ servers. If a user can be led to believe that he is connecting to a
+ host containing information protected by Basic authentication when,
+ in fact, he is connecting to a hostile server or gateway, then the
+ attacker can request a password, store it for later use, and feign an
+ error. This type of attack is not possible with Digest
+ Authentication. Server implementers SHOULD guard against the
+ possibility of this sort of counterfeiting by gateways or CGI
+ scripts. In particular it is very dangerous for a server to simply
+ turn over a connection to a gateway. That gateway can then use the
+ persistent connection mechanism to engage in multiple transactions
+ with the client while impersonating the original server in a way that
+ is not detectable by the client.
+
+4.2 Authentication of Clients using Digest Authentication
+
+ Digest Authentication does not provide a strong authentication
+ mechanism, when compared to public key based mechanisms, for example.
+
+
+
+Franks, et al. Standards Track [Page 20]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ However, it is significantly stronger than (e.g.) CRAM-MD5, which has
+ been proposed for use with LDAP [10], POP and IMAP (see RFC 2195
+ [9]). It is intended to replace the much weaker and even more
+ dangerous Basic mechanism.
+
+ Digest Authentication offers no confidentiality protection beyond
+ protecting the actual password. All of the rest of the request and
+ response are available to an eavesdropper.
+
+ Digest Authentication offers only limited integrity protection for
+ the messages in either direction. If qop=auth-int mechanism is used,
+ those parts of the message used in the calculation of the WWW-
+ Authenticate and Authorization header field response directive values
+ (see section 3.2 above) are protected. Most header fields and their
+ values could be modified as a part of a man-in-the-middle attack.
+
+ Many needs for secure HTTP transactions cannot be met by Digest
+ Authentication. For those needs TLS or SHTTP are more appropriate
+ protocols. In particular Digest authentication cannot be used for any
+ transaction requiring confidentiality protection. Nevertheless many
+ functions remain for which Digest authentication is both useful and
+ appropriate. Any service in present use that uses Basic should be
+ switched to Digest as soon as practical.
+
+4.3 Limited Use Nonce Values
+
+ The Digest scheme uses a server-specified nonce to seed the
+ generation of the request-digest value (as specified in section
+ 3.2.2.1 above). As shown in the example nonce in section 3.2.1, the
+ server is free to construct the nonce such that it may only be used
+ from a particular client, for a particular resource, for a limited
+ period of time or number of uses, or any other restrictions. Doing
+ so strengthens the protection provided against, for example, replay
+ attacks (see 4.5). However, it should be noted that the method
+ chosen for generating and checking the nonce also has performance and
+ resource implications. For example, a server may choose to allow
+ each nonce value to be used only once by maintaining a record of
+ whether or not each recently issued nonce has been returned and
+ sending a next-nonce directive in the Authentication-Info header
+ field of every response. This protects against even an immediate
+ replay attack, but has a high cost checking nonce values, and perhaps
+ more important will cause authentication failures for any pipelined
+ requests (presumably returning a stale nonce indication). Similarly,
+ incorporating a request-specific element such as the Etag value for a
+ resource limits the use of the nonce to that version of the resource
+ and also defeats pipelining. Thus it may be useful to do so for
+ methods with side effects but have unacceptable performance for those
+ that do not.
+
+
+
+Franks, et al. Standards Track [Page 21]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+4.4 Comparison of Digest with Basic Authentication
+
+ Both Digest and Basic Authentication are very much on the weak end of
+ the security strength spectrum. But a comparison between the two
+ points out the utility, even necessity, of replacing Basic by Digest.
+
+ The greatest threat to the type of transactions for which these
+ protocols are used is network snooping. This kind of transaction
+ might involve, for example, online access to a database whose use is
+ restricted to paying subscribers. With Basic authentication an
+ eavesdropper can obtain the password of the user. This not only
+ permits him to access anything in the database, but, often worse,
+ will permit access to anything else the user protects with the same
+ password.
+
+ By contrast, with Digest Authentication the eavesdropper only gets
+ access to the transaction in question and not to the user's password.
+ The information gained by the eavesdropper would permit a replay
+ attack, but only with a request for the same document, and even that
+ may be limited by the server's choice of nonce.
+
+4.5 Replay Attacks
+
+ A replay attack against Digest authentication would usually be
+ pointless for a simple GET request since an eavesdropper would
+ already have seen the only document he could obtain with a replay.
+ This is because the URI of the requested document is digested in the
+ client request and the server will only deliver that document. By
+ contrast under Basic Authentication once the eavesdropper has the
+ user's password, any document protected by that password is open to
+ him.
+
+ Thus, for some purposes, it is necessary to protect against replay
+ attacks. A good Digest implementation can do this in various ways.
+ The server created "nonce" value is implementation dependent, but if
+ it contains a digest of the client IP, a time-stamp, the resource
+ ETag, and a private server key (as recommended above) then a replay
+ attack is not simple. An attacker must convince the server that the
+ request is coming from a false IP address and must cause the server
+ to deliver the document to an IP address different from the address
+ to which it believes it is sending the document. An attack can only
+ succeed in the period before the time-stamp expires. Digesting the
+ client IP and time-stamp in the nonce permits an implementation which
+ does not maintain state between transactions.
+
+ For applications where no possibility of replay attack can be
+ tolerated the server can use one-time nonce values which will not be
+ honored for a second use. This requires the overhead of the server
+
+
+
+Franks, et al. Standards Track [Page 22]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ remembering which nonce values have been used until the nonce time-
+ stamp (and hence the digest built with it) has expired, but it
+ effectively protects against replay attacks.
+
+ An implementation must give special attention to the possibility of
+ replay attacks with POST and PUT requests. Unless the server employs
+ one-time or otherwise limited-use nonces and/or insists on the use of
+ the integrity protection of qop=auth-int, an attacker could replay
+ valid credentials from a successful request with counterfeit form
+ data or other message body. Even with the use of integrity protection
+ most metadata in header fields is not protected. Proper nonce
+ generation and checking provides some protection against replay of
+ previously used valid credentials, but see 4.8.
+
+4.6 Weakness Created by Multiple Authentication Schemes
+
+ An HTTP/1.1 server may return multiple challenges with a 401
+ (Authenticate) response, and each challenge may use a different
+ auth-scheme. A user agent MUST choose to use the strongest auth-
+ scheme it understands and request credentials from the user based
+ upon that challenge.
+
+ Note that many browsers will only recognize Basic and will require
+ that it be the first auth-scheme presented. Servers should only
+ include Basic if it is minimally acceptable.
+
+ When the server offers choices of authentication schemes using the
+ WWW-Authenticate header, the strength of the resulting authentication
+ is only as good as that of the of the weakest of the authentication
+ schemes. See section 4.8 below for discussion of particular attack
+ scenarios that exploit multiple authentication schemes.
+
+4.7 Online dictionary attacks
+
+ If the attacker can eavesdrop, then it can test any overheard
+ nonce/response pairs against a list of common words. Such a list is
+ usually much smaller than the total number of possible passwords. The
+ cost of computing the response for each password on the list is paid
+ once for each challenge.
+
+ The server can mitigate this attack by not allowing users to select
+ passwords that are in a dictionary.
+
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 23]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+4.8 Man in the Middle
+
+ Both Basic and Digest authentication are vulnerable to "man in the
+ middle" (MITM) attacks, for example, from a hostile or compromised
+ proxy. Clearly, this would present all the problems of eavesdropping.
+ But it also offers some additional opportunities to the attacker.
+
+ A possible man-in-the-middle attack would be to add a weak
+ authentication scheme to the set of choices, hoping that the client
+ will use one that exposes the user's credentials (e.g. password). For
+ this reason, the client should always use the strongest scheme that
+ it understands from the choices offered.
+
+ An even better MITM attack would be to remove all offered choices,
+ replacing them with a challenge that requests only Basic
+ authentication, then uses the cleartext credentials from the Basic
+ authentication to authenticate to the origin server using the
+ stronger scheme it requested. A particularly insidious way to mount
+ such a MITM attack would be to offer a "free" proxy caching service
+ to gullible users.
+
+ User agents should consider measures such as presenting a visual
+ indication at the time of the credentials request of what
+ authentication scheme is to be used, or remembering the strongest
+ authentication scheme ever requested by a server and produce a
+ warning message before using a weaker one. It might also be a good
+ idea for the user agent to be configured to demand Digest
+ authentication in general, or from specific sites.
+
+ Or, a hostile proxy might spoof the client into making a request the
+ attacker wanted rather than one the client wanted. Of course, this is
+ still much harder than a comparable attack against Basic
+ Authentication.
+
+4.9 Chosen plaintext attacks
+
+ With Digest authentication, a MITM or a malicious server can
+ arbitrarily choose the nonce that the client will use to compute the
+ response. This is called a "chosen plaintext" attack. The ability to
+ choose the nonce is known to make cryptanalysis much easier [8].
+
+ However, no way to analyze the MD5 one-way function used by Digest
+ using chosen plaintext is currently known.
+
+ The countermeasure against this attack is for clients to be
+ configured to require the use of the optional "cnonce" directive;
+ this allows the client to vary the input to the hash in a way not
+ chosen by the attacker.
+
+
+
+Franks, et al. Standards Track [Page 24]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+4.10 Precomputed dictionary attacks
+
+ With Digest authentication, if the attacker can execute a chosen
+ plaintext attack, the attacker can precompute the response for many
+ common words to a nonce of its choice, and store a dictionary of
+ (response, password) pairs. Such precomputation can often be done in
+ parallel on many machines. It can then use the chosen plaintext
+ attack to acquire a response corresponding to that challenge, and
+ just look up the password in the dictionary. Even if most passwords
+ are not in the dictionary, some might be. Since the attacker gets to
+ pick the challenge, the cost of computing the response for each
+ password on the list can be amortized over finding many passwords. A
+ dictionary with 100 million password/response pairs would take about
+ 3.2 gigabytes of disk storage.
+
+ The countermeasure against this attack is to for clients to be
+ configured to require the use of the optional "cnonce" directive.
+
+4.11 Batch brute force attacks
+
+ With Digest authentication, a MITM can execute a chosen plaintext
+ attack, and can gather responses from many users to the same nonce.
+ It can then find all the passwords within any subset of password
+ space that would generate one of the nonce/response pairs in a single
+ pass over that space. It also reduces the time to find the first
+ password by a factor equal to the number of nonce/response pairs
+ gathered. This search of the password space can often be done in
+ parallel on many machines, and even a single machine can search large
+ subsets of the password space very quickly -- reports exist of
+ searching all passwords with six or fewer letters in a few hours.
+
+ The countermeasure against this attack is to for clients to be
+ configured to require the use of the optional "cnonce" directive.
+
+4.12 Spoofing by Counterfeit Servers
+
+ Basic Authentication is vulnerable to spoofing by counterfeit
+ servers. If a user can be led to believe that she is connecting to a
+ host containing information protected by a password she knows, when
+ in fact she is connecting to a hostile server, then the hostile
+ server can request a password, store it away for later use, and feign
+ an error. This type of attack is more difficult with Digest
+ Authentication -- but the client must know to demand that Digest
+ authentication be used, perhaps using some of the techniques
+ described above to counter "man-in-the-middle" attacks. Again, the
+ user can be helped in detecting this attack by a visual indication of
+ the authentication mechanism in use with appropriate guidance in
+ interpreting the implications of each scheme.
+
+
+
+Franks, et al. Standards Track [Page 25]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+4.13 Storing passwords
+
+ Digest authentication requires that the authenticating agent (usually
+ the server) store some data derived from the user's name and password
+ in a "password file" associated with a given realm. Normally this
+ might contain pairs consisting of username and H(A1), where H(A1) is
+ the digested value of the username, realm, and password as described
+ above.
+
+ The security implications of this are that if this password file is
+ compromised, then an attacker gains immediate access to documents on
+ the server using this realm. Unlike, say a standard UNIX password
+ file, this information need not be decrypted in order to access
+ documents in the server realm associated with this file. On the other
+ hand, decryption, or more likely a brute force attack, would be
+ necessary to obtain the user's password. This is the reason that the
+ realm is part of the digested data stored in the password file. It
+ means that if one Digest authentication password file is compromised,
+ it does not automatically compromise others with the same username
+ and password (though it does expose them to brute force attack).
+
+ There are two important security consequences of this. First the
+ password file must be protected as if it contained unencrypted
+ passwords, because for the purpose of accessing documents in its
+ realm, it effectively does.
+
+ A second consequence of this is that the realm string should be
+ unique among all realms which any single user is likely to use. In
+ particular a realm string should include the name of the host doing
+ the authentication. The inability of the client to authenticate the
+ server is a weakness of Digest Authentication.
+
+4.14 Summary
+
+ By modern cryptographic standards Digest Authentication is weak. But
+ for a large range of purposes it is valuable as a replacement for
+ Basic Authentication. It remedies some, but not all, weaknesses of
+ Basic Authentication. Its strength may vary depending on the
+ implementation. In particular the structure of the nonce (which is
+ dependent on the server implementation) may affect the ease of
+ mounting a replay attack. A range of server options is appropriate
+ since, for example, some implementations may be willing to accept the
+ server overhead of one-time nonces or digests to eliminate the
+ possibility of replay. Others may satisfied with a nonce like the one
+ recommended above restricted to a single IP address and a single ETag
+ or with a limited lifetime.
+
+
+
+
+
+Franks, et al. Standards Track [Page 26]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ The bottom line is that *any* compliant implementation will be
+ relatively weak by cryptographic standards, but *any* compliant
+ implementation will be far superior to Basic Authentication.
+
+5 Sample implementation
+
+ [[ WARNING: DigestCalcHA1 IS WRONG ]]
+
+ The following code implements the calculations of H(A1), H(A2),
+ request-digest and response-digest, and a test program which computes
+ the values used in the example of section 3.5. It uses the MD5
+ implementation from RFC 1321.
+
+ File "digcalc.h":
+
+#define HASHLEN 16
+typedef char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef char HASHHEX[HASHHEXLEN+1];
+#define IN
+#define OUT
+
+/* calculate H(A1) as per HTTP Digest spec */
+void DigestCalcHA1(
+ IN char * pszAlg,
+ IN char * pszUserName,
+ IN char * pszRealm,
+ IN char * pszPassword,
+ IN char * pszNonce,
+ IN char * pszCNonce,
+ OUT HASHHEX SessionKey
+ );
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+ IN HASHHEX HA1, /* H(A1) */
+ IN char * pszNonce, /* nonce from server */
+ IN char * pszNonceCount, /* 8 hex digits */
+ IN char * pszCNonce, /* client nonce */
+ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
+ IN char * pszMethod, /* method from the request */
+ IN char * pszDigestUri, /* requested URL */
+ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
+ OUT HASHHEX Response /* request-digest or response-digest */
+ );
+
+File "digcalc.c":
+
+#include <global.h>
+#include <md5.h>
+
+
+
+Franks, et al. Standards Track [Page 27]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+#include <string.h>
+#include "digcalc.h"
+
+void CvtHex(
+ IN HASH Bin,
+ OUT HASHHEX Hex
+ )
+{
+ unsigned short i;
+ unsigned char j;
+
+ for (i = 0; i < HASHLEN; i++) {
+ j = (Bin[i] >> 4) & 0xf;
+ if (j <= 9)
+ Hex[i*2] = (j + '0');
+ else
+ Hex[i*2] = (j + 'a' - 10);
+ j = Bin[i] & 0xf;
+ if (j <= 9)
+ Hex[i*2+1] = (j + '0');
+ else
+ Hex[i*2+1] = (j + 'a' - 10);
+ };
+ Hex[HASHHEXLEN] = '\0';
+};
+
+/* calculate H(A1) as per spec */
+void DigestCalcHA1(
+ IN char * pszAlg,
+ IN char * pszUserName,
+ IN char * pszRealm,
+ IN char * pszPassword,
+ IN char * pszNonce,
+ IN char * pszCNonce,
+ OUT HASHHEX SessionKey
+ )
+{
+ MD5_CTX Md5Ctx;
+ HASH HA1;
+
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
+ MD5Final(HA1, &Md5Ctx);
+ if (stricmp(pszAlg, "md5-sess") == 0) {
+
+
+
+Franks, et al. Standards Track [Page 28]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, HA1, HASHLEN);
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+ MD5Final(HA1, &Md5Ctx);
+ };
+ CvtHex(HA1, SessionKey);
+};
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+ IN HASHHEX HA1, /* H(A1) */
+ IN char * pszNonce, /* nonce from server */
+ IN char * pszNonceCount, /* 8 hex digits */
+ IN char * pszCNonce, /* client nonce */
+ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
+ IN char * pszMethod, /* method from the request */
+ IN char * pszDigestUri, /* requested URL */
+ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
+ OUT HASHHEX Response /* request-digest or response-digest */
+ )
+{
+ MD5_CTX Md5Ctx;
+ HASH HA2;
+ HASH RespHash;
+ HASHHEX HA2Hex;
+
+ // calculate H(A2)
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+ if (stricmp(pszQop, "auth-int") == 0) {
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
+ };
+ MD5Final(HA2, &Md5Ctx);
+ CvtHex(HA2, HA2Hex);
+
+ // calculate response
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+ MD5Update(&Md5Ctx, ":", 1);
+ if (*pszQop) {
+
+
+
+Franks, et al. Standards Track [Page 29]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+ MD5Update(&Md5Ctx, ":", 1);
+ MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
+ MD5Update(&Md5Ctx, ":", 1);
+ };
+ MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+ MD5Final(RespHash, &Md5Ctx);
+ CvtHex(RespHash, Response);
+};
+
+File "digtest.c":
+
+
+#include <stdio.h>
+#include "digcalc.h"
+
+void main(int argc, char ** argv) {
+
+ char * pszNonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093";
+ char * pszCNonce = "0a4f113b";
+ char * pszUser = "Mufasa";
+ char * pszRealm = "testrealm@host.com";
+ char * pszPass = "Circle Of Life";
+ char * pszAlg = "md5";
+ char szNonceCount[9] = "00000001";
+ char * pszMethod = "GET";
+ char * pszQop = "auth";
+ char * pszURI = "/dir/index.html";
+ HASHHEX HA1;
+ HASHHEX HA2 = "";
+ HASHHEX Response;
+
+ DigestCalcHA1(pszAlg, pszUser, pszRealm, pszPass, pszNonce,
+pszCNonce, HA1);
+ DigestCalcResponse(HA1, pszNonce, szNonceCount, pszCNonce, pszQop,
+ pszMethod, pszURI, HA2, Response);
+ printf("Response = %s\n", Response);
+};
+
+
+
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 30]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+6 Acknowledgments
+
+ Eric W. Sink, of AbiSource, Inc., was one of the original authors
+ before the specification underwent substantial revision.
+
+ In addition to the authors, valuable discussion instrumental in
+ creating this document has come from Peter J. Churchyard, Ned Freed,
+ and David M. Kristol.
+
+ Jim Gettys and Larry Masinter edited this document for update.
+
+7 References
+
+ [1] Berners-Lee, T., Fielding, R. and H. Frystyk, "Hypertext
+ Transfer Protocol -- HTTP/1.0", RFC 1945, May 1996.
+
+ [2] Fielding, R., Gettys, J., Mogul, J., Frysyk, H., Masinter, L.,
+ Leach, P. and T. Berners-Lee, "Hypertext Transfer Protocol --
+ HTTP/1.1", RFC 2616, June 1999.
+
+ [3] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April
+ 1992.
+
+ [4] Freed, N. and N. Borenstein. "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies",
+ RFC 2045, November 1996.
+
+ [5] Dierks, T. and C. Allen "The TLS Protocol, Version 1.0", RFC
+ 2246, January 1999.
+
+ [6] Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P.,
+ Luotonen, A., Sink, E. and L. Stewart, "An Extension to HTTP :
+ Digest Access Authentication", RFC 2069, January 1997.
+
+ [7] Berners Lee, T, Fielding, R. and L. Masinter, "Uniform Resource
+ Identifiers (URI): Generic Syntax", RFC 2396, August 1998.
+
+ [8] Kaliski, B.,Robshaw, M., "Message Authentication with MD5",
+ CryptoBytes, Sping 1995, RSA Inc,
+ (http://www.rsa.com/rsalabs/pubs/cryptobytes/spring95/md5.htm)
+
+ [9] Klensin, J., Catoe, R. and P. Krumviede, "IMAP/POP AUTHorize
+ Extension for Simple Challenge/Response", RFC 2195, September
+ 1997.
+
+ [10] Morgan, B., Alvestrand, H., Hodges, J., Wahl, M.,
+ "Authentication Methods for LDAP", Work in Progress.
+
+
+
+
+Franks, et al. Standards Track [Page 31]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+8 Authors' Addresses
+
+ John Franks
+ Professor of Mathematics
+ Department of Mathematics
+ Northwestern University
+ Evanston, IL 60208-2730, USA
+
+ EMail: john@math.nwu.edu
+
+
+ Phillip M. Hallam-Baker
+ Principal Consultant
+ Verisign Inc.
+ 301 Edgewater Place
+ Suite 210
+ Wakefield MA 01880, USA
+
+ EMail: pbaker@verisign.com
+
+
+ Jeffery L. Hostetler
+ Software Craftsman
+ AbiSource, Inc.
+ 6 Dunlap Court
+ Savoy, IL 61874
+
+ EMail: jeff@AbiSource.com
+
+
+ Scott D. Lawrence
+ Agranat Systems, Inc.
+ 5 Clocktower Place, Suite 400
+ Maynard, MA 01754, USA
+
+ EMail: lawrence@agranat.com
+
+
+ Paul J. Leach
+ Microsoft Corporation
+ 1 Microsoft Way
+ Redmond, WA 98052, USA
+
+ EMail: paulle@microsoft.com
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 32]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+ Ari Luotonen
+ Member of Technical Staff
+ Netscape Communications Corporation
+ 501 East Middlefield Road
+ Mountain View, CA 94043, USA
+
+
+ Lawrence C. Stewart
+ Open Market, Inc.
+ 215 First Street
+ Cambridge, MA 02142, USA
+
+ EMail: stewart@OpenMarket.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 33]
+\f
+RFC 2617 HTTP Authentication June 1999
+
+
+9. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Franks, et al. Standards Track [Page 34]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group R. Khare
+Request for Comments: 2817 4K Associates / UC Irvine
+Updates: 2616 S. Lawrence
+Category: Standards Track Agranat Systems, Inc.
+ May 2000
+
+
+ Upgrading to TLS Within HTTP/1.1
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+Abstract
+
+ This memo explains how to use the Upgrade mechanism in HTTP/1.1 to
+ initiate Transport Layer Security (TLS) over an existing TCP
+ connection. This allows unsecured and secured HTTP traffic to share
+ the same well known port (in this case, http: at 80 rather than
+ https: at 443). It also enables "virtual hosting", so a single HTTP +
+ TLS server can disambiguate traffic intended for several hostnames at
+ a single IP address.
+
+ Since HTTP/1.1 [1] defines Upgrade as a hop-by-hop mechanism, this
+ memo also documents the HTTP CONNECT method for establishing end-to-
+ end tunnels across HTTP proxies. Finally, this memo establishes new
+ IANA registries for public HTTP status codes, as well as public or
+ private Upgrade product tokens.
+
+ This memo does NOT affect the current definition of the 'https' URI
+ scheme, which already defines a separate namespace
+ (http://example.org/ and https://example.org/ are not equivalent).
+
+
+
+
+
+
+
+
+
+
+
+Khare & Lawrence Standards Track [Page 1]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+Table of Contents
+
+ 1. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . 2
+ 2. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2.1 Requirements Terminology . . . . . . . . . . . . . . . . . . . 4
+ 3. Client Requested Upgrade to HTTP over TLS . . . . . . . . . . 4
+ 3.1 Optional Upgrade . . . . . . . . . . . . . . . . . . . . . . . 4
+ 3.2 Mandatory Upgrade . . . . . . . . . . . . . . . . . . . . . . 4
+ 3.3 Server Acceptance of Upgrade Request . . . . . . . . . . . . . 4
+ 4. Server Requested Upgrade to HTTP over TLS . . . . . . . . . . 5
+ 4.1 Optional Advertisement . . . . . . . . . . . . . . . . . . . . 5
+ 4.2 Mandatory Advertisement . . . . . . . . . . . . . . . . . . . 5
+ 5. Upgrade across Proxies . . . . . . . . . . . . . . . . . . . . 6
+ 5.1 Implications of Hop By Hop Upgrade . . . . . . . . . . . . . . 6
+ 5.2 Requesting a Tunnel with CONNECT . . . . . . . . . . . . . . . 6
+ 5.3 Establishing a Tunnel with CONNECT . . . . . . . . . . . . . . 7
+ 6. Rationale for the use of a 4xx (client error) Status Code . . 7
+ 7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 8
+ 7.1 HTTP Status Code Registry . . . . . . . . . . . . . . . . . . 8
+ 7.2 HTTP Upgrade Token Registry . . . . . . . . . . . . . . . . . 8
+ 8. Security Considerations . . . . . . . . . . . . . . . . . . . 9
+ 8.1 Implications for the https: URI Scheme . . . . . . . . . . . . 10
+ 8.2 Security Considerations for CONNECT . . . . . . . . . . . . . 10
+ References . . . . . . . . . . . . . . . . . . . . . . . . . . 10
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . 11
+ A. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 12
+ Full Copyright Statement . . . . . . . . . . . . . . . . . . . 13
+
+1. Motivation
+
+ The historical practice of deploying HTTP over SSL3 [3] has
+ distinguished the combination from HTTP alone by a unique URI scheme
+ and the TCP port number. The scheme 'http' meant the HTTP protocol
+ alone on port 80, while 'https' meant the HTTP protocol over SSL on
+ port 443. Parallel well-known port numbers have similarly been
+ requested -- and in some cases, granted -- to distinguish between
+ secured and unsecured use of other application protocols (e.g.
+ snews, ftps). This approach effectively halves the number of
+ available well known ports.
+
+ At the Washington DC IETF meeting in December 1997, the Applications
+ Area Directors and the IESG reaffirmed that the practice of issuing
+ parallel "secure" port numbers should be deprecated. The HTTP/1.1
+ Upgrade mechanism can apply Transport Layer Security [6] to an open
+ HTTP connection.
+
+
+
+
+
+
+Khare & Lawrence Standards Track [Page 2]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+ In the nearly two years since, there has been broad acceptance of the
+ concept behind this proposal, but little interest in implementing
+ alternatives to port 443 for generic Web browsing. In fact, nothing
+ in this memo affects the current interpretation of https: URIs.
+ However, new application protocols built atop HTTP, such as the
+ Internet Printing Protocol [7], call for just such a mechanism in
+ order to move ahead in the IETF standards process.
+
+ The Upgrade mechanism also solves the "virtual hosting" problem.
+ Rather than allocating multiple IP addresses to a single host, an
+ HTTP/1.1 server will use the Host: header to disambiguate the
+ intended web service. As HTTP/1.1 usage has grown more prevalent,
+ more ISPs are offering name-based virtual hosting, thus delaying IP
+ address space exhaustion.
+
+ TLS (and SSL) have been hobbled by the same limitation as earlier
+ versions of HTTP: the initial handshake does not specify the intended
+ hostname, relying exclusively on the IP address. Using a cleartext
+ HTTP/1.1 Upgrade: preamble to the TLS handshake -- choosing the
+ certificates based on the initial Host: header -- will allow ISPs to
+ provide secure name-based virtual hosting as well.
+
+2. Introduction
+
+ TLS, a.k.a., SSL (Secure Sockets Layer), establishes a private end-
+ to-end connection, optionally including strong mutual authentication,
+ using a variety of cryptosystems. Initially, a handshake phase uses
+ three subprotocols to set up a record layer, authenticate endpoints,
+ set parameters, as well as report errors. Then, there is an ongoing
+ layered record protocol that handles encryption, compression, and
+ reassembly for the remainder of the connection. The latter is
+ intended to be completely transparent. For example, there is no
+ dependency between TLS's record markers and or certificates and
+ HTTP/1.1's chunked encoding or authentication.
+
+ Either the client or server can use the HTTP/1.1 [1] Upgrade
+ mechanism (Section 14.42) to indicate that a TLS-secured connection
+ is desired or necessary. This memo defines the "TLS/1.0" Upgrade
+ token, and a new HTTP Status Code, "426 Upgrade Required".
+
+ Section 3 and Section 4 describe the operation of a directly
+ connected client and server. Intermediate proxies must establish an
+ end-to-end tunnel before applying those operations, as explained in
+ Section 5.
+
+
+
+
+
+
+
+Khare & Lawrence Standards Track [Page 3]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+2.1 Requirements Terminology
+
+ Keywords "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT" and
+ "MAY" that appear in this document are to be interpreted as described
+ in RFC 2119 [11].
+
+3. Client Requested Upgrade to HTTP over TLS
+
+ When the client sends an HTTP/1.1 request with an Upgrade header
+ field containing the token "TLS/1.0", it is requesting the server to
+ complete the current HTTP/1.1 request after switching to TLS/1.0.
+
+3.1 Optional Upgrade
+
+ A client MAY offer to switch to secured operation during any clear
+ HTTP request when an unsecured response would be acceptable:
+
+ GET http://example.bank.com/acct_stat.html?749394889300 HTTP/1.1
+ Host: example.bank.com
+ Upgrade: TLS/1.0
+ Connection: Upgrade
+
+ In this case, the server MAY respond to the clear HTTP operation
+ normally, OR switch to secured operation (as detailed in the next
+ section).
+
+ Note that HTTP/1.1 [1] specifies "the upgrade keyword MUST be
+ supplied within a Connection header field (section 14.10) whenever
+ Upgrade is present in an HTTP/1.1 message".
+
+3.2 Mandatory Upgrade
+
+ If an unsecured response would be unacceptable, a client MUST send an
+ OPTIONS request first to complete the switch to TLS/1.0 (if
+ possible).
+
+ OPTIONS * HTTP/1.1
+ Host: example.bank.com
+ Upgrade: TLS/1.0
+ Connection: Upgrade
+
+3.3 Server Acceptance of Upgrade Request
+
+ As specified in HTTP/1.1 [1], if the server is prepared to initiate
+ the TLS handshake, it MUST send the intermediate "101 Switching
+ Protocol" and MUST include an Upgrade response header specifying the
+ tokens of the protocol stack it is switching to:
+
+
+
+
+Khare & Lawrence Standards Track [Page 4]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+ HTTP/1.1 101 Switching Protocols
+ Upgrade: TLS/1.0, HTTP/1.1
+ Connection: Upgrade
+
+ Note that the protocol tokens listed in the Upgrade header of a 101
+ Switching Protocols response specify an ordered 'bottom-up' stack.
+
+ As specified in HTTP/1.1 [1], Section 10.1.2: "The server will
+ switch protocols to those defined by the response's Upgrade header
+ field immediately after the empty line which terminates the 101
+ response".
+
+ Once the TLS handshake completes successfully, the server MUST
+ continue with the response to the original request. Any TLS handshake
+ failure MUST lead to disconnection, per the TLS error alert
+ specification.
+
+4. Server Requested Upgrade to HTTP over TLS
+
+ The Upgrade response header field advertises possible protocol
+ upgrades a server MAY accept. In conjunction with the "426 Upgrade
+ Required" status code, a server can advertise the exact protocol
+ upgrade(s) that a client MUST accept to complete the request.
+
+4.1 Optional Advertisement
+
+ As specified in HTTP/1.1 [1], the server MAY include an Upgrade
+ header in any response other than 101 or 426 to indicate a
+ willingness to switch to any (combination) of the protocols listed.
+
+4.2 Mandatory Advertisement
+
+ A server MAY indicate that a client request can not be completed
+ without TLS using the "426 Upgrade Required" status code, which MUST
+ include an an Upgrade header field specifying the token of the
+ required TLS version.
+
+ HTTP/1.1 426 Upgrade Required
+ Upgrade: TLS/1.0, HTTP/1.1
+ Connection: Upgrade
+
+ The server SHOULD include a message body in the 426 response which
+ indicates in human readable form the reason for the error and
+ describes any alternative courses which may be available to the user.
+
+ Note that even if a client is willing to use TLS, it must use the
+ operations in Section 3 to proceed; the TLS handshake cannot begin
+ immediately after the 426 response.
+
+
+
+Khare & Lawrence Standards Track [Page 5]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+5. Upgrade across Proxies
+
+ As a hop-by-hop header, Upgrade is negotiated between each pair of
+ HTTP counterparties. If a User Agent sends a request with an Upgrade
+ header to a proxy, it is requesting a change to the protocol between
+ itself and the proxy, not an end-to-end change.
+
+ Since TLS, in particular, requires end-to-end connectivity to provide
+ authentication and prevent man-in-the-middle attacks, this memo
+ specifies the CONNECT method to establish a tunnel across proxies.
+
+ Once a tunnel is established, any of the operations in Section 3 can
+ be used to establish a TLS connection.
+
+5.1 Implications of Hop By Hop Upgrade
+
+ If an origin server receives an Upgrade header from a proxy and
+ responds with a 101 Switching Protocols response, it is changing the
+ protocol only on the connection between the proxy and itself.
+ Similarly, a proxy might return a 101 response to its client to
+ change the protocol on that connection independently of the protocols
+ it is using to communicate toward the origin server.
+
+ These scenarios also complicate diagnosis of a 426 response. Since
+ Upgrade is a hop-by-hop header, a proxy that does not recognize 426
+ might remove the accompanying Upgrade header and prevent the client
+ from determining the required protocol switch. If a client receives
+ a 426 status without an accompanying Upgrade header, it will need to
+ request an end to end tunnel connection as described in Section 5.2
+ and repeat the request in order to obtain the required upgrade
+ information.
+
+ This hop-by-hop definition of Upgrade was a deliberate choice. It
+ allows for incremental deployment on either side of proxies, and for
+ optimized protocols between cascaded proxies without the knowledge of
+ the parties that are not a part of the change.
+
+5.2 Requesting a Tunnel with CONNECT
+
+ A CONNECT method requests that a proxy establish a tunnel connection
+ on its behalf. The Request-URI portion of the Request-Line is always
+ an 'authority' as defined by URI Generic Syntax [2], which is to say
+ the host name and port number destination of the requested connection
+ separated by a colon:
+
+ CONNECT server.example.com:80 HTTP/1.1
+ Host: server.example.com:80
+
+
+
+
+Khare & Lawrence Standards Track [Page 6]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+ Other HTTP mechanisms can be used normally with the CONNECT method --
+ except end-to-end protocol Upgrade requests, of course, since the
+ tunnel must be established first.
+
+ For example, proxy authentication might be used to establish the
+ authority to create a tunnel:
+
+ CONNECT server.example.com:80 HTTP/1.1
+ Host: server.example.com:80
+ Proxy-Authorization: basic aGVsbG86d29ybGQ=
+
+ Like any other pipelined HTTP/1.1 request, data to be tunneled may be
+ sent immediately after the blank line. The usual caveats also apply:
+ data may be discarded if the eventual response is negative, and the
+ connection may be reset with no response if more than one TCP segment
+ is outstanding.
+
+5.3 Establishing a Tunnel with CONNECT
+
+ Any successful (2xx) response to a CONNECT request indicates that the
+ proxy has established a connection to the requested host and port,
+ and has switched to tunneling the current connection to that server
+ connection.
+
+ It may be the case that the proxy itself can only reach the requested
+ origin server through another proxy. In this case, the first proxy
+ SHOULD make a CONNECT request of that next proxy, requesting a tunnel
+ to the authority. A proxy MUST NOT respond with any 2xx status code
+ unless it has either a direct or tunnel connection established to the
+ authority.
+
+ An origin server which receives a CONNECT request for itself MAY
+ respond with a 2xx status code to indicate that a connection is
+ established.
+
+ If at any point either one of the peers gets disconnected, any
+ outstanding data that came from that peer will be passed to the other
+ one, and after that also the other connection will be terminated by
+ the proxy. If there is outstanding data to that peer undelivered,
+ that data will be discarded.
+
+6. Rationale for the use of a 4xx (client error) Status Code
+
+ Reliable, interoperable negotiation of Upgrade features requires an
+ unambiguous failure signal. The 426 Upgrade Required status code
+ allows a server to definitively state the precise protocol extensions
+ a given resource must be served with.
+
+
+
+
+Khare & Lawrence Standards Track [Page 7]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+ It might at first appear that the response should have been some form
+ of redirection (a 3xx code), by analogy to an old-style redirection
+ to an https: URI. User agents that do not understand Upgrade:
+ preclude this.
+
+ Suppose that a 3xx code had been assigned for "Upgrade Required"; a
+ user agent that did not recognize it would treat it as 300. It would
+ then properly look for a "Location" header in the response and
+ attempt to repeat the request at the URL in that header field. Since
+ it did not know to Upgrade to incorporate the TLS layer, it would at
+ best fail again at the new URL.
+
+7. IANA Considerations
+
+ IANA shall create registries for two name spaces, as described in BCP
+ 26 [10]:
+
+ o HTTP Status Codes
+ o HTTP Upgrade Tokens
+
+7.1 HTTP Status Code Registry
+
+ The HTTP Status Code Registry defines the name space for the Status-
+ Code token in the Status line of an HTTP response. The initial
+ values for this name space are those specified by:
+
+ 1. Draft Standard for HTTP/1.1 [1]
+ 2. Web Distributed Authoring and Versioning [4] [defines 420-424]
+ 3. WebDAV Advanced Collections [5] (Work in Progress) [defines 425]
+ 4. Section 6 [defines 426]
+
+ Values to be added to this name space SHOULD be subject to review in
+ the form of a standards track document within the IETF Applications
+ Area. Any such document SHOULD be traceable through statuses of
+ either 'Obsoletes' or 'Updates' to the Draft Standard for
+ HTTP/1.1 [1].
+
+7.2 HTTP Upgrade Token Registry
+
+ The HTTP Upgrade Token Registry defines the name space for product
+ tokens used to identify protocols in the Upgrade HTTP header field.
+ Each registered token should be associated with one or a set of
+ specifications, and with contact information.
+
+ The Draft Standard for HTTP/1.1 [1] specifies that these tokens obey
+ the production for 'product':
+
+
+
+
+
+Khare & Lawrence Standards Track [Page 8]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+ product = token ["/" product-version]
+ product-version = token
+
+ Registrations should be allowed on a First Come First Served basis as
+ described in BCP 26 [10]. These specifications need not be IETF
+ documents or be subject to IESG review, but should obey the following
+ rules:
+
+ 1. A token, once registered, stays registered forever.
+ 2. The registration MUST name a responsible party for the
+ registration.
+ 3. The registration MUST name a point of contact.
+ 4. The registration MAY name the documentation required for the
+ token.
+ 5. The responsible party MAY change the registration at any time.
+ The IANA will keep a record of all such changes, and make them
+ available upon request.
+ 6. The responsible party for the first registration of a "product"
+ token MUST approve later registrations of a "version" token
+ together with that "product" token before they can be registered.
+ 7. If absolutely required, the IESG MAY reassign the responsibility
+ for a token. This will normally only be used in the case when a
+ responsible party cannot be contacted.
+
+ This specification defines the protocol token "TLS/1.0" as the
+ identifier for the protocol specified by The TLS Protocol [6].
+
+ It is NOT required that specifications for upgrade tokens be made
+ publicly available, but the contact information for the registration
+ SHOULD be.
+
+8. Security Considerations
+
+ The potential for a man-in-the-middle attack (deleting the Upgrade
+ header) remains the same as current, mixed http/https practice:
+
+ o Removing the Upgrade header is similar to rewriting web pages to
+ change https:// links to http:// links.
+ o The risk is only present if the server is willing to vend such
+ information over both a secure and an insecure channel in the
+ first place.
+ o If the client knows for a fact that a server is TLS-compliant, it
+ can insist on it by only sending an Upgrade request with a no-op
+ method like OPTIONS.
+ o Finally, as the https: specification warns, "users should
+ carefully examine the certificate presented by the server to
+ determine if it meets their expectations".
+
+
+
+
+Khare & Lawrence Standards Track [Page 9]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+ Furthermore, for clients that do not explicitly try to invoke TLS,
+ servers can use the Upgrade header in any response other than 101 or
+ 426 to advertise TLS compliance. Since TLS compliance should be
+ considered a feature of the server and not the resource at hand, it
+ should be sufficient to send it once, and let clients cache that
+ fact.
+
+8.1 Implications for the https: URI Scheme
+
+ While nothing in this memo affects the definition of the 'https' URI
+ scheme, widespread adoption of this mechanism for HyperText content
+ could use 'http' to identify both secure and non-secure resources.
+
+ The choice of what security characteristics are required on the
+ connection is left to the client and server. This allows either
+ party to use any information available in making this determination.
+ For example, user agents may rely on user preference settings or
+ information about the security of the network such as 'TLS required
+ on all POST operations not on my local net', or servers may apply
+ resource access rules such as 'the FORM on this page must be served
+ and submitted using TLS'.
+
+8.2 Security Considerations for CONNECT
+
+ A generic TCP tunnel is fraught with security risks. First, such
+ authorization should be limited to a small number of known ports.
+ The Upgrade: mechanism defined here only requires onward tunneling at
+ port 80. Second, since tunneled data is opaque to the proxy, there
+ are additional risks to tunneling to other well-known or reserved
+ ports. A putative HTTP client CONNECTing to port 25 could relay spam
+ via SMTP, for example.
+
+References
+
+ [1] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L.,
+ Leach, P. and T. Berners-Lee, "Hypertext Transfer Protocol --
+ HTTP/1.1", RFC 2616, June 1999.
+
+ [2] Berners-Lee, T., Fielding, R. and L. Masinter, "URI Generic
+ Syntax", RFC 2396, August 1998.
+
+ [3] Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000.
+
+ [4] Goland, Y., Whitehead, E., Faizi, A., Carter, S. and D. Jensen,
+ "Web Distributed Authoring and Versioning", RFC 2518, February
+ 1999.
+
+
+
+
+
+Khare & Lawrence Standards Track [Page 10]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+ [5] Slein, J., Whitehead, E.J., et al., "WebDAV Advanced Collections
+ Protocol", Work In Progress.
+
+ [6] Dierks, T. and C. Allen, "The TLS Protocol", RFC 2246, January
+ 1999.
+
+ [7] Herriot, R., Butler, S., Moore, P. and R. Turner, "Internet
+ Printing Protocol/1.0: Encoding and Transport", RFC 2565, April
+ 1999.
+
+ [8] Luotonen, A., "Tunneling TCP based protocols through Web proxy
+ servers", Work In Progress. (Also available in: Luotonen, Ari.
+ Web Proxy Servers, Prentice-Hall, 1997 ISBN:0136806120.)
+
+ [9] Rose, M., "Writing I-Ds and RFCs using XML", RFC 2629, June
+ 1999.
+
+ [10] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA
+ Considerations Section in RFCs", BCP 26, RFC 2434, October 1998.
+
+ [11] Bradner, S., "Key words for use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+Authors' Addresses
+
+ Rohit Khare
+ 4K Associates / UC Irvine
+ 3207 Palo Verde
+ Irvine, CA 92612
+ US
+
+ Phone: +1 626 806 7574
+ EMail: rohit@4K-associates.com
+ URI: http://www.4K-associates.com/
+
+
+ Scott Lawrence
+ Agranat Systems, Inc.
+ 5 Clocktower Place
+ Suite 400
+ Maynard, MA 01754
+ US
+
+ Phone: +1 978 461 0888
+ EMail: lawrence@agranat.com
+ URI: http://www.agranat.com/
+
+
+
+
+
+Khare & Lawrence Standards Track [Page 11]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+Appendix A. Acknowledgments
+
+ The CONNECT method was originally described in a Work in Progress
+ titled, "Tunneling TCP based protocols through Web proxy servers",
+ [8] by Ari Luotonen of Netscape Communications Corporation. It was
+ widely implemented by HTTP proxies, but was never made a part of any
+ IETF Standards Track document. The method name CONNECT was reserved,
+ but not defined in [1].
+
+ The definition provided here is derived directly from that earlier
+ memo, with some editorial changes and conformance to the stylistic
+ conventions since established in other HTTP specifications.
+
+ Additional Thanks to:
+
+ o Paul Hoffman for his work on the STARTTLS command extension for
+ ESMTP.
+ o Roy Fielding for assistance with the rationale behind Upgrade:
+ and its interaction with OPTIONS.
+ o Eric Rescorla for his work on standardizing the existing https:
+ practice to compare with.
+ o Marshall Rose, for the xml2rfc document type description and tools
+ [9].
+ o Jim Whitehead, for sorting out the current range of available HTTP
+ status codes.
+ o Henrik Frystyk Nielsen, whose work on the Mandatory extension
+ mechanism pointed out a hop-by-hop Upgrade still requires
+ tunneling.
+ o Harald Alvestrand for improvements to the token registration
+ rules.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Khare & Lawrence Standards Track [Page 12]
+\f
+RFC 2817 HTTP Upgrade to TLS May 2000
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Khare & Lawrence Standards Track [Page 13]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group E. Rescorla
+Request for Comments: 2818 RTFM, Inc.
+Category: Informational May 2000
+
+
+ HTTP Over TLS
+
+Status of this Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+Abstract
+
+ This memo describes how to use TLS to secure HTTP connections over
+ the Internet. Current practice is to layer HTTP over SSL (the
+ predecessor to TLS), distinguishing secured traffic from insecure
+ traffic by the use of a different server port. This document
+ documents that practice using TLS. A companion document describes a
+ method for using HTTP/TLS over the same port as normal HTTP
+ [RFC2817].
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . 2
+ 1.1. Requirements Terminology . . . . . . . . . . . . . . . 2
+ 2. HTTP Over TLS . . . . . . . . . . . . . . . . . . . . . . 2
+ 2.1. Connection Initiation . . . . . . . . . . . . . . . . . 2
+ 2.2. Connection Closure . . . . . . . . . . . . . . . . . . 2
+ 2.2.1. Client Behavior . . . . . . . . . . . . . . . . . . . 3
+ 2.2.2. Server Behavior . . . . . . . . . . . . . . . . . . . 3
+ 2.3. Port Number . . . . . . . . . . . . . . . . . . . . . . 4
+ 2.4. URI Format . . . . . . . . . . . . . . . . . . . . . . 4
+ 3. Endpoint Identification . . . . . . . . . . . . . . . . . 4
+ 3.1. Server Identity . . . . . . . . . . . . . . . . . . . . 4
+ 3.2. Client Identity . . . . . . . . . . . . . . . . . . . . 5
+ References . . . . . . . . . . . . . . . . . . . . . . . . . 6
+ Security Considerations . . . . . . . . . . . . . . . . . . 6
+ Author's Address . . . . . . . . . . . . . . . . . . . . . . 6
+ Full Copyright Statement . . . . . . . . . . . . . . . . . . 7
+
+
+
+
+
+
+Rescorla Informational [Page 1]
+\f
+RFC 2818 HTTP Over TLS May 2000
+
+
+1. Introduction
+
+ HTTP [RFC2616] was originally used in the clear on the Internet.
+ However, increased use of HTTP for sensitive applications has
+ required security measures. SSL, and its successor TLS [RFC2246] were
+ designed to provide channel-oriented security. This document
+ describes how to use HTTP over TLS.
+
+1.1. Requirements Terminology
+
+ Keywords "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT" and
+ "MAY" that appear in this document are to be interpreted as described
+ in [RFC2119].
+
+2. HTTP Over TLS
+
+ Conceptually, HTTP/TLS is very simple. Simply use HTTP over TLS
+ precisely as you would use HTTP over TCP.
+
+2.1. Connection Initiation
+
+ The agent acting as the HTTP client should also act as the TLS
+ client. It should initiate a connection to the server on the
+ appropriate port and then send the TLS ClientHello to begin the TLS
+ handshake. When the TLS handshake has finished. The client may then
+ initiate the first HTTP request. All HTTP data MUST be sent as TLS
+ "application data". Normal HTTP behavior, including retained
+ connections should be followed.
+
+2.2. Connection Closure
+
+ TLS provides a facility for secure connection closure. When a valid
+ closure alert is received, an implementation can be assured that no
+ further data will be received on that connection. TLS
+ implementations MUST initiate an exchange of closure alerts before
+ closing a connection. A TLS implementation MAY, after sending a
+ closure alert, close the connection without waiting for the peer to
+ send its closure alert, generating an "incomplete close". Note that
+ an implementation which does this MAY choose to reuse the session.
+ This SHOULD only be done when the application knows (typically
+ through detecting HTTP message boundaries) that it has received all
+ the message data that it cares about.
+
+ As specified in [RFC2246], any implementation which receives a
+ connection close without first receiving a valid closure alert (a
+ "premature close") MUST NOT reuse that session. Note that a
+ premature close does not call into question the security of the data
+ already received, but simply indicates that subsequent data might
+
+
+
+Rescorla Informational [Page 2]
+\f
+RFC 2818 HTTP Over TLS May 2000
+
+
+ have been truncated. Because TLS is oblivious to HTTP
+ request/response boundaries, it is necessary to examine the HTTP data
+ itself (specifically the Content-Length header) to determine whether
+ the truncation occurred inside a message or between messages.
+
+2.2.1. Client Behavior
+
+ Because HTTP uses connection closure to signal end of server data,
+ client implementations MUST treat any premature closes as errors and
+ the data received as potentially truncated. While in some cases the
+ HTTP protocol allows the client to find out whether truncation took
+ place so that, if it received the complete reply, it may tolerate
+ such errors following the principle to "[be] strict when sending and
+ tolerant when receiving" [RFC1958], often truncation does not show in
+ the HTTP protocol data; two cases in particular deserve special note:
+
+ A HTTP response without a Content-Length header. Since data length
+ in this situation is signalled by connection close a premature
+ close generated by the server cannot be distinguished from a
+ spurious close generated by an attacker.
+
+ A HTTP response with a valid Content-Length header closed before
+ all data has been read. Because TLS does not provide document
+ oriented protection, it is impossible to determine whether the
+ server has miscomputed the Content-Length or an attacker has
+ truncated the connection.
+
+ There is one exception to the above rule. When encountering a
+ premature close, a client SHOULD treat as completed all requests for
+ which it has received as much data as specified in the Content-Length
+ header.
+
+ A client detecting an incomplete close SHOULD recover gracefully. It
+ MAY resume a TLS session closed in this fashion.
+
+ Clients MUST send a closure alert before closing the connection.
+ Clients which are unprepared to receive any more data MAY choose not
+ to wait for the server's closure alert and simply close the
+ connection, thus generating an incomplete close on the server side.
+
+2.2.2. Server Behavior
+
+ RFC 2616 permits an HTTP client to close the connection at any time,
+ and requires servers to recover gracefully. In particular, servers
+ SHOULD be prepared to receive an incomplete close from the client,
+ since the client can often determine when the end of server data is.
+ Servers SHOULD be willing to resume TLS sessions closed in this
+ fashion.
+
+
+
+Rescorla Informational [Page 3]
+\f
+RFC 2818 HTTP Over TLS May 2000
+
+
+ Implementation note: In HTTP implementations which do not use
+ persistent connections, the server ordinarily expects to be able to
+ signal end of data by closing the connection. When Content-Length is
+ used, however, the client may have already sent the closure alert and
+ dropped the connection.
+
+ Servers MUST attempt to initiate an exchange of closure alerts with
+ the client before closing the connection. Servers MAY close the
+ connection after sending the closure alert, thus generating an
+ incomplete close on the client side.
+
+2.3. Port Number
+
+ The first data that an HTTP server expects to receive from the client
+ is the Request-Line production. The first data that a TLS server (and
+ hence an HTTP/TLS server) expects to receive is the ClientHello.
+ Consequently, common practice has been to run HTTP/TLS over a
+ separate port in order to distinguish which protocol is being used.
+ When HTTP/TLS is being run over a TCP/IP connection, the default port
+ is 443. This does not preclude HTTP/TLS from being run over another
+ transport. TLS only presumes a reliable connection-oriented data
+ stream.
+
+2.4. URI Format
+
+ HTTP/TLS is differentiated from HTTP URIs by using the 'https'
+ protocol identifier in place of the 'http' protocol identifier. An
+ example URI specifying HTTP/TLS is:
+
+ https://www.example.com/~smith/home.html
+
+3. Endpoint Identification
+
+3.1. Server Identity
+
+ In general, HTTP/TLS requests are generated by dereferencing a URI.
+ As a consequence, the hostname for the server is known to the client.
+ If the hostname is available, the client MUST check it against the
+ server's identity as presented in the server's Certificate message,
+ in order to prevent man-in-the-middle attacks.
+
+ If the client has external information as to the expected identity of
+ the server, the hostname check MAY be omitted. (For instance, a
+ client may be connecting to a machine whose address and hostname are
+ dynamic but the client knows the certificate that the server will
+ present.) In such cases, it is important to narrow the scope of
+ acceptable certificates as much as possible in order to prevent man
+
+
+
+
+Rescorla Informational [Page 4]
+\f
+RFC 2818 HTTP Over TLS May 2000
+
+
+ in the middle attacks. In special cases, it may be appropriate for
+ the client to simply ignore the server's identity, but it must be
+ understood that this leaves the connection open to active attack.
+
+ If a subjectAltName extension of type dNSName is present, that MUST
+ be used as the identity. Otherwise, the (most specific) Common Name
+ field in the Subject field of the certificate MUST be used. Although
+ the use of the Common Name is existing practice, it is deprecated and
+ Certification Authorities are encouraged to use the dNSName instead.
+
+ Matching is performed using the matching rules specified by
+ [RFC2459]. If more than one identity of a given type is present in
+ the certificate (e.g., more than one dNSName name, a match in any one
+ of the set is considered acceptable.) Names may contain the wildcard
+ character * which is considered to match any single domain name
+ component or component fragment. E.g., *.a.com matches foo.a.com but
+ not bar.foo.a.com. f*.com matches foo.com but not bar.com.
+
+ In some cases, the URI is specified as an IP address rather than a
+ hostname. In this case, the iPAddress subjectAltName must be present
+ in the certificate and must exactly match the IP in the URI.
+
+ If the hostname does not match the identity in the certificate, user
+ oriented clients MUST either notify the user (clients MAY give the
+ user the opportunity to continue with the connection in any case) or
+ terminate the connection with a bad certificate error. Automated
+ clients MUST log the error to an appropriate audit log (if available)
+ and SHOULD terminate the connection (with a bad certificate error).
+ Automated clients MAY provide a configuration setting that disables
+ this check, but MUST provide a setting which enables it.
+
+ Note that in many cases the URI itself comes from an untrusted
+ source. The above-described check provides no protection against
+ attacks where this source is compromised. For example, if the URI was
+ obtained by clicking on an HTML page which was itself obtained
+ without using HTTP/TLS, a man in the middle could have replaced the
+ URI. In order to prevent this form of attack, users should carefully
+ examine the certificate presented by the server to determine if it
+ meets their expectations.
+
+3.2. Client Identity
+
+ Typically, the server has no external knowledge of what the client's
+ identity ought to be and so checks (other than that the client has a
+ certificate chain rooted in an appropriate CA) are not possible. If a
+ server has such knowledge (typically from some source external to
+ HTTP or TLS) it SHOULD check the identity as described above.
+
+
+
+
+Rescorla Informational [Page 5]
+\f
+RFC 2818 HTTP Over TLS May 2000
+
+
+References
+
+ [RFC2459] Housley, R., Ford, W., Polk, W. and D. Solo, "Internet
+ Public Key Infrastructure: Part I: X.509 Certificate and
+ CRL Profile", RFC 2459, January 1999.
+
+ [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter,
+ L., Leach, P. and T. Berners-Lee, "Hypertext Transfer
+ Protocol, HTTP/1.1", RFC 2616, June 1999.
+
+ [RFC2119] Bradner, S., "Key Words for use in RFCs to indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2246] Dierks, T. and C. Allen, "The TLS Protocol", RFC 2246,
+ January 1999.
+
+ [RFC2817] Khare, R. and S. Lawrence, "Upgrading to TLS Within
+ HTTP/1.1", RFC 2817, May 2000.
+
+Security Considerations
+
+ This entire document is about security.
+
+Author's Address
+
+ Eric Rescorla
+ RTFM, Inc.
+ 30 Newell Road, #16
+ East Palo Alto, CA 94303
+
+ Phone: (650) 328-8631
+ EMail: ekr@rtfm.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Rescorla Informational [Page 6]
+\f
+RFC 2818 HTTP Over TLS May 2000
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Rescorla Informational [Page 7]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group D. Kristol
+Request for Comments: 2965 Bell Laboratories, Lucent Technologies
+Obsoletes: 2109 L. Montulli
+Category: Standards Track Epinions.com, Inc.
+ October 2000
+
+
+ HTTP State Management Mechanism
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+IESG Note
+
+ The IESG notes that this mechanism makes use of the .local top-level
+ domain (TLD) internally when handling host names that don't contain
+ any dots, and that this mechanism might not work in the expected way
+ should an actual .local TLD ever be registered.
+
+Abstract
+
+ This document specifies a way to create a stateful session with
+ Hypertext Transfer Protocol (HTTP) requests and responses. It
+ describes three new headers, Cookie, Cookie2, and Set-Cookie2, which
+ carry state information between participating origin servers and user
+ agents. The method described here differs from Netscape's Cookie
+ proposal [Netscape], but it can interoperate with HTTP/1.0 user
+ agents that use Netscape's method. (See the HISTORICAL section.)
+
+ This document reflects implementation experience with RFC 2109 and
+ obsoletes it.
+
+1. TERMINOLOGY
+
+ The terms user agent, client, server, proxy, origin server, and
+ http_URL have the same meaning as in the HTTP/1.1 specification
+ [RFC2616]. The terms abs_path and absoluteURI have the same meaning
+ as in the URI Syntax specification [RFC2396].
+
+
+
+
+Kristol & Montulli Standards Track [Page 1]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Host name (HN) means either the host domain name (HDN) or the numeric
+ Internet Protocol (IP) address of a host. The fully qualified domain
+ name is preferred; use of numeric IP addresses is strongly
+ discouraged.
+
+ The terms request-host and request-URI refer to the values the client
+ would send to the server as, respectively, the host (but not port)
+ and abs_path portions of the absoluteURI (http_URL) of the HTTP
+ request line. Note that request-host is a HN.
+
+ The term effective host name is related to host name. If a host name
+ contains no dots, the effective host name is that name with the
+ string .local appended to it. Otherwise the effective host name is
+ the same as the host name. Note that all effective host names
+ contain at least one dot.
+
+ The term request-port refers to the port portion of the absoluteURI
+ (http_URL) of the HTTP request line. If the absoluteURI has no
+ explicit port, the request-port is the HTTP default, 80. The
+ request-port of a cookie is the request-port of the request in which
+ a Set-Cookie2 response header was returned to the user agent.
+
+ Host names can be specified either as an IP address or a HDN string.
+ Sometimes we compare one host name with another. (Such comparisons
+ SHALL be case-insensitive.) Host A's name domain-matches host B's if
+
+ * their host name strings string-compare equal; or
+
+ * A is a HDN string and has the form NB, where N is a non-empty
+ name string, B has the form .B', and B' is a HDN string. (So,
+ x.y.com domain-matches .Y.com but not Y.com.)
+
+ Note that domain-match is not a commutative operation: a.b.c.com
+ domain-matches .c.com, but not the reverse.
+
+ The reach R of a host name H is defined as follows:
+
+ * If
+
+ - H is the host domain name of a host; and,
+
+ - H has the form A.B; and
+
+ - A has no embedded (that is, interior) dots; and
+
+ - B has at least one embedded dot, or B is the string "local".
+ then the reach of H is .B.
+
+
+
+
+Kristol & Montulli Standards Track [Page 2]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ * Otherwise, the reach of H is H.
+
+ For two strings that represent paths, P1 and P2, P1 path-matches P2
+ if P2 is a prefix of P1 (including the case where P1 and P2 string-
+ compare equal). Thus, the string /tec/waldo path-matches /tec.
+
+ Because it was used in Netscape's original implementation of state
+ management, we will use the term cookie to refer to the state
+ information that passes between an origin server and user agent, and
+ that gets stored by the user agent.
+
+1.1 Requirements
+
+ The key words "MAY", "MUST", "MUST NOT", "OPTIONAL", "RECOMMENDED",
+ "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+2. STATE AND SESSIONS
+
+ This document describes a way to create stateful sessions with HTTP
+ requests and responses. Currently, HTTP servers respond to each
+ client request without relating that request to previous or
+ subsequent requests; the state management mechanism allows clients
+ and servers that wish to exchange state information to place HTTP
+ requests and responses within a larger context, which we term a
+ "session". This context might be used to create, for example, a
+ "shopping cart", in which user selections can be aggregated before
+ purchase, or a magazine browsing system, in which a user's previous
+ reading affects which offerings are presented.
+
+ Neither clients nor servers are required to support cookies. A
+ server MAY refuse to provide content to a client that does not return
+ the cookies it sends.
+
+3. DESCRIPTION
+
+ We describe here a way for an origin server to send state information
+ to the user agent, and for the user agent to return the state
+ information to the origin server. The goal is to have a minimal
+ impact on HTTP and user agents.
+
+3.1 Syntax: General
+
+ The two state management headers, Set-Cookie2 and Cookie, have common
+ syntactic properties involving attribute-value pairs. The following
+ grammar uses the notation, and tokens DIGIT (decimal digits), token
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 3]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ (informally, a sequence of non-special, non-white space characters),
+ and http_URL from the HTTP/1.1 specification [RFC2616] to describe
+ their syntax.
+
+ av-pairs = av-pair *(";" av-pair)
+ av-pair = attr ["=" value] ; optional value
+ attr = token
+ value = token | quoted-string
+
+ Attributes (names) (attr) are case-insensitive. White space is
+ permitted between tokens. Note that while the above syntax
+ description shows value as optional, most attrs require them.
+
+ NOTE: The syntax above allows whitespace between the attribute and
+ the = sign.
+
+3.2 Origin Server Role
+
+ 3.2.1 General The origin server initiates a session, if it so
+ desires. To do so, it returns an extra response header to the
+ client, Set-Cookie2. (The details follow later.)
+
+ A user agent returns a Cookie request header (see below) to the
+ origin server if it chooses to continue a session. The origin server
+ MAY ignore it or use it to determine the current state of the
+ session. It MAY send back to the client a Set-Cookie2 response
+ header with the same or different information, or it MAY send no
+ Set-Cookie2 header at all. The origin server effectively ends a
+ session by sending the client a Set-Cookie2 header with Max-Age=0.
+
+ Servers MAY return Set-Cookie2 response headers with any response.
+ User agents SHOULD send Cookie request headers, subject to other
+ rules detailed below, with every request.
+
+ An origin server MAY include multiple Set-Cookie2 headers in a
+ response. Note that an intervening gateway could fold multiple such
+ headers into a single header.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 4]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ 3.2.2 Set-Cookie2 Syntax The syntax for the Set-Cookie2 response
+ header is
+
+ set-cookie = "Set-Cookie2:" cookies
+ cookies = 1#cookie
+ cookie = NAME "=" VALUE *(";" set-cookie-av)
+ NAME = attr
+ VALUE = value
+ set-cookie-av = "Comment" "=" value
+ | "CommentURL" "=" <"> http_URL <">
+ | "Discard"
+ | "Domain" "=" value
+ | "Max-Age" "=" value
+ | "Path" "=" value
+ | "Port" [ "=" <"> portlist <"> ]
+ | "Secure"
+ | "Version" "=" 1*DIGIT
+ portlist = 1#portnum
+ portnum = 1*DIGIT
+
+ Informally, the Set-Cookie2 response header comprises the token Set-
+ Cookie2:, followed by a comma-separated list of one or more cookies.
+ Each cookie begins with a NAME=VALUE pair, followed by zero or more
+ semi-colon-separated attribute-value pairs. The syntax for
+ attribute-value pairs was shown earlier. The specific attributes and
+ the semantics of their values follows. The NAME=VALUE attribute-
+ value pair MUST come first in each cookie. The others, if present,
+ can occur in any order. If an attribute appears more than once in a
+ cookie, the client SHALL use only the value associated with the first
+ appearance of the attribute; a client MUST ignore values after the
+ first.
+
+ The NAME of a cookie MAY be the same as one of the attributes in this
+ specification. However, because the cookie's NAME must come first in
+ a Set-Cookie2 response header, the NAME and its VALUE cannot be
+ confused with an attribute-value pair.
+
+ NAME=VALUE
+ REQUIRED. The name of the state information ("cookie") is NAME,
+ and its value is VALUE. NAMEs that begin with $ are reserved and
+ MUST NOT be used by applications.
+
+ The VALUE is opaque to the user agent and may be anything the
+ origin server chooses to send, possibly in a server-selected
+ printable ASCII encoding. "Opaque" implies that the content is of
+ interest and relevance only to the origin server. The content
+ may, in fact, be readable by anyone that examines the Set-Cookie2
+ header.
+
+
+
+Kristol & Montulli Standards Track [Page 5]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Comment=value
+ OPTIONAL. Because cookies can be used to derive or store private
+ information about a user, the value of the Comment attribute
+ allows an origin server to document how it intends to use the
+ cookie. The user can inspect the information to decide whether to
+ initiate or continue a session with this cookie. Characters in
+ value MUST be in UTF-8 encoding. [RFC2279]
+
+ CommentURL="http_URL"
+ OPTIONAL. Because cookies can be used to derive or store private
+ information about a user, the CommentURL attribute allows an
+ origin server to document how it intends to use the cookie. The
+ user can inspect the information identified by the URL to decide
+ whether to initiate or continue a session with this cookie.
+
+ Discard
+ OPTIONAL. The Discard attribute instructs the user agent to
+ discard the cookie unconditionally when the user agent terminates.
+
+ Domain=value
+ OPTIONAL. The value of the Domain attribute specifies the domain
+ for which the cookie is valid. If an explicitly specified value
+ does not start with a dot, the user agent supplies a leading dot.
+
+ Max-Age=value
+ OPTIONAL. The value of the Max-Age attribute is delta-seconds,
+ the lifetime of the cookie in seconds, a decimal non-negative
+ integer. To handle cached cookies correctly, a client SHOULD
+ calculate the age of the cookie according to the age calculation
+ rules in the HTTP/1.1 specification [RFC2616]. When the age is
+ greater than delta-seconds seconds, the client SHOULD discard the
+ cookie. A value of zero means the cookie SHOULD be discarded
+ immediately.
+
+ Path=value
+ OPTIONAL. The value of the Path attribute specifies the subset of
+ URLs on the origin server to which this cookie applies.
+
+ Port[="portlist"]
+ OPTIONAL. The Port attribute restricts the port to which a cookie
+ may be returned in a Cookie request header. Note that the syntax
+ REQUIREs quotes around the OPTIONAL portlist even if there is only
+ one portnum in portlist.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 6]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Secure
+ OPTIONAL. The Secure attribute (with no value) directs the user
+ agent to use only (unspecified) secure means to contact the origin
+ server whenever it sends back this cookie, to protect the
+ confidentially and authenticity of the information in the cookie.
+
+ The user agent (possibly with user interaction) MAY determine what
+ level of security it considers appropriate for "secure" cookies.
+ The Secure attribute should be considered security advice from the
+ server to the user agent, indicating that it is in the session's
+ interest to protect the cookie contents. When it sends a "secure"
+ cookie back to a server, the user agent SHOULD use no less than
+ the same level of security as was used when it received the cookie
+ from the server.
+
+ Version=value
+ REQUIRED. The value of the Version attribute, a decimal integer,
+ identifies the version of the state management specification to
+ which the cookie conforms. For this specification, Version=1
+ applies.
+
+ 3.2.3 Controlling Caching An origin server must be cognizant of the
+ effect of possible caching of both the returned resource and the
+ Set-Cookie2 header. Caching "public" documents is desirable. For
+ example, if the origin server wants to use a public document such as
+ a "front door" page as a sentinel to indicate the beginning of a
+ session for which a Set-Cookie2 response header must be generated,
+ the page SHOULD be stored in caches "pre-expired" so that the origin
+ server will see further requests. "Private documents", for example
+ those that contain information strictly private to a session, SHOULD
+ NOT be cached in shared caches.
+
+ If the cookie is intended for use by a single user, the Set-Cookie2
+ header SHOULD NOT be cached. A Set-Cookie2 header that is intended
+ to be shared by multiple users MAY be cached.
+
+ The origin server SHOULD send the following additional HTTP/1.1
+ response headers, depending on circumstances:
+
+ * To suppress caching of the Set-Cookie2 header:
+
+ Cache-control: no-cache="set-cookie2"
+
+ and one of the following:
+
+ * To suppress caching of a private document in shared caches:
+
+ Cache-control: private
+
+
+
+Kristol & Montulli Standards Track [Page 7]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ * To allow caching of a document and require that it be validated
+ before returning it to the client:
+
+ Cache-Control: must-revalidate, max-age=0
+
+ * To allow caching of a document, but to require that proxy
+ caches (not user agent caches) validate it before returning it
+ to the client:
+
+ Cache-Control: proxy-revalidate, max-age=0
+
+ * To allow caching of a document and request that it be validated
+ before returning it to the client (by "pre-expiring" it):
+
+ Cache-control: max-age=0
+
+ Not all caches will revalidate the document in every case.
+
+ HTTP/1.1 servers MUST send Expires: old-date (where old-date is a
+ date long in the past) on responses containing Set-Cookie2 response
+ headers unless they know for certain (by out of band means) that
+ there are no HTTP/1.0 proxies in the response chain. HTTP/1.1
+ servers MAY send other Cache-Control directives that permit caching
+ by HTTP/1.1 proxies in addition to the Expires: old-date directive;
+ the Cache-Control directive will override the Expires: old-date for
+ HTTP/1.1 proxies.
+
+3.3 User Agent Role
+
+ 3.3.1 Interpreting Set-Cookie2 The user agent keeps separate track
+ of state information that arrives via Set-Cookie2 response headers
+ from each origin server (as distinguished by name or IP address and
+ port). The user agent MUST ignore attribute-value pairs whose
+ attribute it does not recognize. The user agent applies these
+ defaults for optional attributes that are missing:
+
+ Discard The default behavior is dictated by the presence or absence
+ of a Max-Age attribute.
+
+ Domain Defaults to the effective request-host. (Note that because
+ there is no dot at the beginning of effective request-host,
+ the default Domain can only domain-match itself.)
+
+ Max-Age The default behavior is to discard the cookie when the user
+ agent exits.
+
+ Path Defaults to the path of the request URL that generated the
+ Set-Cookie2 response, up to and including the right-most /.
+
+
+
+Kristol & Montulli Standards Track [Page 8]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Port The default behavior is that a cookie MAY be returned to any
+ request-port.
+
+ Secure If absent, the user agent MAY send the cookie over an
+ insecure channel.
+
+ 3.3.2 Rejecting Cookies To prevent possible security or privacy
+ violations, a user agent rejects a cookie according to rules below.
+ The goal of the rules is to try to limit the set of servers for which
+ a cookie is valid, based on the values of the Path, Domain, and Port
+ attributes and the request-URI, request-host and request-port.
+
+ A user agent rejects (SHALL NOT store its information) if the Version
+ attribute is missing. Moreover, a user agent rejects (SHALL NOT
+ store its information) if any of the following is true of the
+ attributes explicitly present in the Set-Cookie2 response header:
+
+ * The value for the Path attribute is not a prefix of the
+ request-URI.
+
+ * The value for the Domain attribute contains no embedded dots,
+ and the value is not .local.
+
+ * The effective host name that derives from the request-host does
+ not domain-match the Domain attribute.
+
+ * The request-host is a HDN (not IP address) and has the form HD,
+ where D is the value of the Domain attribute, and H is a string
+ that contains one or more dots.
+
+ * The Port attribute has a "port-list", and the request-port was
+ not in the list.
+
+ Examples:
+
+ * A Set-Cookie2 from request-host y.x.foo.com for Domain=.foo.com
+ would be rejected, because H is y.x and contains a dot.
+
+ * A Set-Cookie2 from request-host x.foo.com for Domain=.foo.com
+ would be accepted.
+
+ * A Set-Cookie2 with Domain=.com or Domain=.com., will always be
+ rejected, because there is no embedded dot.
+
+ * A Set-Cookie2 with Domain=ajax.com will be accepted, and the
+ value for Domain will be taken to be .ajax.com, because a dot
+ gets prepended to the value.
+
+
+
+
+Kristol & Montulli Standards Track [Page 9]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ * A Set-Cookie2 with Port="80,8000" will be accepted if the
+ request was made to port 80 or 8000 and will be rejected
+ otherwise.
+
+ * A Set-Cookie2 from request-host example for Domain=.local will
+ be accepted, because the effective host name for the request-
+ host is example.local, and example.local domain-matches .local.
+
+ 3.3.3 Cookie Management If a user agent receives a Set-Cookie2
+ response header whose NAME is the same as that of a cookie it has
+ previously stored, the new cookie supersedes the old when: the old
+ and new Domain attribute values compare equal, using a case-
+ insensitive string-compare; and, the old and new Path attribute
+ values string-compare equal (case-sensitive). However, if the Set-
+ Cookie2 has a value for Max-Age of zero, the (old and new) cookie is
+ discarded. Otherwise a cookie persists (resources permitting) until
+ whichever happens first, then gets discarded: its Max-Age lifetime is
+ exceeded; or, if the Discard attribute is set, the user agent
+ terminates the session.
+
+ Because user agents have finite space in which to store cookies, they
+ MAY also discard older cookies to make space for newer ones, using,
+ for example, a least-recently-used algorithm, along with constraints
+ on the maximum number of cookies that each origin server may set.
+
+ If a Set-Cookie2 response header includes a Comment attribute, the
+ user agent SHOULD store that information in a human-readable form
+ with the cookie and SHOULD display the comment text as part of a
+ cookie inspection user interface.
+
+ If a Set-Cookie2 response header includes a CommentURL attribute, the
+ user agent SHOULD store that information in a human-readable form
+ with the cookie, or, preferably, SHOULD allow the user to follow the
+ http_URL link as part of a cookie inspection user interface.
+
+ The cookie inspection user interface may include a facility whereby a
+ user can decide, at the time the user agent receives the Set-Cookie2
+ response header, whether or not to accept the cookie. A potentially
+ confusing situation could arise if the following sequence occurs:
+
+ * the user agent receives a cookie that contains a CommentURL
+ attribute;
+
+ * the user agent's cookie inspection interface is configured so
+ that it presents a dialog to the user before the user agent
+ accepts the cookie;
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 10]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ * the dialog allows the user to follow the CommentURL link when
+ the user agent receives the cookie; and,
+
+ * when the user follows the CommentURL link, the origin server
+ (or another server, via other links in the returned content)
+ returns another cookie.
+
+ The user agent SHOULD NOT send any cookies in this context. The user
+ agent MAY discard any cookie it receives in this context that the
+ user has not, through some user agent mechanism, deemed acceptable.
+
+ User agents SHOULD allow the user to control cookie destruction, but
+ they MUST NOT extend the cookie's lifetime beyond that controlled by
+ the Discard and Max-Age attributes. An infrequently-used cookie may
+ function as a "preferences file" for network applications, and a user
+ may wish to keep it even if it is the least-recently-used cookie. One
+ possible implementation would be an interface that allows the
+ permanent storage of a cookie through a checkbox (or, conversely, its
+ immediate destruction).
+
+ Privacy considerations dictate that the user have considerable
+ control over cookie management. The PRIVACY section contains more
+ information.
+
+ 3.3.4 Sending Cookies to the Origin Server When it sends a request
+ to an origin server, the user agent includes a Cookie request header
+ if it has stored cookies that are applicable to the request, based on
+
+ * the request-host and request-port;
+
+ * the request-URI;
+
+ * the cookie's age.
+
+ The syntax for the header is:
+
+cookie = "Cookie:" cookie-version 1*((";" | ",") cookie-value)
+cookie-value = NAME "=" VALUE [";" path] [";" domain] [";" port]
+cookie-version = "$Version" "=" value
+NAME = attr
+VALUE = value
+path = "$Path" "=" value
+domain = "$Domain" "=" value
+port = "$Port" [ "=" <"> value <"> ]
+
+ The value of the cookie-version attribute MUST be the value from the
+ Version attribute of the corresponding Set-Cookie2 response header.
+ Otherwise the value for cookie-version is 0. The value for the path
+
+
+
+Kristol & Montulli Standards Track [Page 11]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ attribute MUST be the value from the Path attribute, if one was
+ present, of the corresponding Set-Cookie2 response header. Otherwise
+ the attribute SHOULD be omitted from the Cookie request header. The
+ value for the domain attribute MUST be the value from the Domain
+ attribute, if one was present, of the corresponding Set-Cookie2
+ response header. Otherwise the attribute SHOULD be omitted from the
+ Cookie request header.
+
+ The port attribute of the Cookie request header MUST mirror the Port
+ attribute, if one was present, in the corresponding Set-Cookie2
+ response header. That is, the port attribute MUST be present if the
+ Port attribute was present in the Set-Cookie2 header, and it MUST
+ have the same value, if any. Otherwise, if the Port attribute was
+ absent from the Set-Cookie2 header, the attribute likewise MUST be
+ omitted from the Cookie request header.
+
+ Note that there is neither a Comment nor a CommentURL attribute in
+ the Cookie request header corresponding to the ones in the Set-
+ Cookie2 response header. The user agent does not return the comment
+ information to the origin server.
+
+ The user agent applies the following rules to choose applicable
+ cookie-values to send in Cookie request headers from among all the
+ cookies it has received.
+
+ Domain Selection
+ The origin server's effective host name MUST domain-match the
+ Domain attribute of the cookie.
+
+ Port Selection
+ There are three possible behaviors, depending on the Port
+ attribute in the Set-Cookie2 response header:
+
+ 1. By default (no Port attribute), the cookie MAY be sent to any
+ port.
+
+ 2. If the attribute is present but has no value (e.g., Port), the
+ cookie MUST only be sent to the request-port it was received
+ from.
+
+ 3. If the attribute has a port-list, the cookie MUST only be
+ returned if the new request-port is one of those listed in
+ port-list.
+
+ Path Selection
+ The request-URI MUST path-match the Path attribute of the cookie.
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 12]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Max-Age Selection
+ Cookies that have expired should have been discarded and thus are
+ not forwarded to an origin server.
+
+ If multiple cookies satisfy the criteria above, they are ordered in
+ the Cookie header such that those with more specific Path attributes
+ precede those with less specific. Ordering with respect to other
+ attributes (e.g., Domain) is unspecified.
+
+ Note: For backward compatibility, the separator in the Cookie header
+ is semi-colon (;) everywhere. A server SHOULD also accept comma (,)
+ as the separator between cookie-values for future compatibility.
+
+ 3.3.5 Identifying What Version is Understood: Cookie2 The Cookie2
+ request header facilitates interoperation between clients and servers
+ that understand different versions of the cookie specification. When
+ the client sends one or more cookies to an origin server, if at least
+ one of those cookies contains a $Version attribute whose value is
+ different from the version that the client understands, then the
+ client MUST also send a Cookie2 request header, the syntax for which
+ is
+
+ cookie2 = "Cookie2:" cookie-version
+
+ Here the value for cookie-version is the highest version of cookie
+ specification (currently 1) that the client understands. The client
+ needs to send at most one such request header per request.
+
+ 3.3.6 Sending Cookies in Unverifiable Transactions Users MUST have
+ control over sessions in order to ensure privacy. (See PRIVACY
+ section below.) To simplify implementation and to prevent an
+ additional layer of complexity where adequate safeguards exist,
+ however, this document distinguishes between transactions that are
+ verifiable and those that are unverifiable. A transaction is
+ verifiable if the user, or a user-designated agent, has the option to
+ review the request-URI prior to its use in the transaction. A
+ transaction is unverifiable if the user does not have that option.
+ Unverifiable transactions typically arise when a user agent
+ automatically requests inlined or embedded entities or when it
+ resolves redirection (3xx) responses from an origin server.
+ Typically the origin transaction, the transaction that the user
+ initiates, is verifiable, and that transaction may directly or
+ indirectly induce the user agent to make unverifiable transactions.
+
+ An unverifiable transaction is to a third-party host if its request-
+ host U does not domain-match the reach R of the request-host O in the
+ origin transaction.
+
+
+
+
+Kristol & Montulli Standards Track [Page 13]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ When it makes an unverifiable transaction, a user agent MUST disable
+ all cookie processing (i.e., MUST NOT send cookies, and MUST NOT
+ accept any received cookies) if the transaction is to a third-party
+ host.
+
+ This restriction prevents a malicious service author from using
+ unverifiable transactions to induce a user agent to start or continue
+ a session with a server in a different domain. The starting or
+ continuation of such sessions could be contrary to the privacy
+ expectations of the user, and could also be a security problem.
+
+ User agents MAY offer configurable options that allow the user agent,
+ or any autonomous programs that the user agent executes, to ignore
+ the above rule, so long as these override options default to "off".
+
+ (N.B. Mechanisms may be proposed that will automate overriding the
+ third-party restrictions under controlled conditions.)
+
+ Many current user agents already provide a review option that would
+ render many links verifiable. For instance, some user agents display
+ the URL that would be referenced for a particular link when the mouse
+ pointer is placed over that link. The user can therefore determine
+ whether to visit that site before causing the browser to do so.
+ (Though not implemented on current user agents, a similar technique
+ could be used for a button used to submit a form -- the user agent
+ could display the action to be taken if the user were to select that
+ button.) However, even this would not make all links verifiable; for
+ example, links to automatically loaded images would not normally be
+ subject to "mouse pointer" verification.
+
+ Many user agents also provide the option for a user to view the HTML
+ source of a document, or to save the source to an external file where
+ it can be viewed by another application. While such an option does
+ provide a crude review mechanism, some users might not consider it
+ acceptable for this purpose.
+
+3.4 How an Origin Server Interprets the Cookie Header
+
+ A user agent returns much of the information in the Set-Cookie2
+ header to the origin server when the request-URI path-matches the
+ Path attribute of the cookie. When it receives a Cookie header, the
+ origin server SHOULD treat cookies with NAMEs whose prefix is $
+ specially, as an attribute for the cookie.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 14]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+3.5 Caching Proxy Role
+
+ One reason for separating state information from both a URL and
+ document content is to facilitate the scaling that caching permits.
+ To support cookies, a caching proxy MUST obey these rules already in
+ the HTTP specification:
+
+ * Honor requests from the cache, if possible, based on cache
+ validity rules.
+
+ * Pass along a Cookie request header in any request that the
+ proxy must make of another server.
+
+ * Return the response to the client. Include any Set-Cookie2
+ response header.
+
+ * Cache the received response subject to the control of the usual
+ headers, such as Expires,
+
+ Cache-control: no-cache
+
+ and
+
+ Cache-control: private
+
+ * Cache the Set-Cookie2 subject to the control of the usual
+ header,
+
+ Cache-control: no-cache="set-cookie2"
+
+ (The Set-Cookie2 header should usually not be cached.)
+
+ Proxies MUST NOT introduce Set-Cookie2 (Cookie) headers of their own
+ in proxy responses (requests).
+
+4. EXAMPLES
+
+4.1 Example 1
+
+ Most detail of request and response headers has been omitted. Assume
+ the user agent has no stored cookies.
+
+ 1. User Agent -> Server
+
+ POST /acme/login HTTP/1.1
+ [form data]
+
+ User identifies self via a form.
+
+
+
+Kristol & Montulli Standards Track [Page 15]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ 2. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"
+
+ Cookie reflects user's identity.
+
+ 3. User Agent -> Server
+
+ POST /acme/pickitem HTTP/1.1
+ Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
+ [form data]
+
+ User selects an item for "shopping basket".
+
+ 4. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
+ Path="/acme"
+
+ Shopping basket contains an item.
+
+ 5. User Agent -> Server
+
+ POST /acme/shipping HTTP/1.1
+ Cookie: $Version="1";
+ Customer="WILE_E_COYOTE"; $Path="/acme";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+ [form data]
+
+ User selects shipping method from form.
+
+ 6. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"
+
+ New cookie reflects shipping method.
+
+ 7. User Agent -> Server
+
+ POST /acme/process HTTP/1.1
+ Cookie: $Version="1";
+ Customer="WILE_E_COYOTE"; $Path="/acme";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme";
+ Shipping="FedEx"; $Path="/acme"
+ [form data]
+
+
+
+Kristol & Montulli Standards Track [Page 16]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ User chooses to process order.
+
+ 8. Server -> User Agent
+
+ HTTP/1.1 200 OK
+
+ Transaction is complete.
+
+ The user agent makes a series of requests on the origin server, after
+ each of which it receives a new cookie. All the cookies have the
+ same Path attribute and (default) domain. Because the request-URIs
+ all path-match /acme, the Path attribute of each cookie, each request
+ contains all the cookies received so far.
+
+4.2 Example 2
+
+ This example illustrates the effect of the Path attribute. All
+ detail of request and response headers has been omitted. Assume the
+ user agent has no stored cookies.
+
+ Imagine the user agent has received, in response to earlier requests,
+ the response headers
+
+ Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
+ Path="/acme"
+
+ and
+
+ Set-Cookie2: Part_Number="Riding_Rocket_0023"; Version="1";
+ Path="/acme/ammo"
+
+ A subsequent request by the user agent to the (same) server for URLs
+ of the form /acme/ammo/... would include the following request
+ header:
+
+ Cookie: $Version="1";
+ Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+
+ Note that the NAME=VALUE pair for the cookie with the more specific
+ Path attribute, /acme/ammo, comes before the one with the less
+ specific Path attribute, /acme. Further note that the same cookie
+ name appears more than once.
+
+ A subsequent request by the user agent to the (same) server for a URL
+ of the form /acme/parts/ would include the following request header:
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 17]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001";
+ $Path="/acme"
+
+ Here, the second cookie's Path attribute /acme/ammo is not a prefix
+ of the request URL, /acme/parts/, so the cookie does not get
+ forwarded to the server.
+
+5. IMPLEMENTATION CONSIDERATIONS
+
+ Here we provide guidance on likely or desirable details for an origin
+ server that implements state management.
+
+5.1 Set-Cookie2 Content
+
+ An origin server's content should probably be divided into disjoint
+ application areas, some of which require the use of state
+ information. The application areas can be distinguished by their
+ request URLs. The Set-Cookie2 header can incorporate information
+ about the application areas by setting the Path attribute for each
+ one.
+
+ The session information can obviously be clear or encoded text that
+ describes state. However, if it grows too large, it can become
+ unwieldy. Therefore, an implementor might choose for the session
+ information to be a key to a server-side resource. Of course, using
+ a database creates some problems that this state management
+ specification was meant to avoid, namely:
+
+ 1. keeping real state on the server side;
+
+ 2. how and when to garbage-collect the database entry, in case the
+ user agent terminates the session by, for example, exiting.
+
+5.2 Stateless Pages
+
+ Caching benefits the scalability of WWW. Therefore it is important
+ to reduce the number of documents that have state embedded in them
+ inherently. For example, if a shopping-basket-style application
+ always displays a user's current basket contents on each page, those
+ pages cannot be cached, because each user's basket's contents would
+ be different. On the other hand, if each page contains just a link
+ that allows the user to "Look at My Shopping Basket", the page can be
+ cached.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 18]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+5.3 Implementation Limits
+
+ Practical user agent implementations have limits on the number and
+ size of cookies that they can store. In general, user agents' cookie
+ support should have no fixed limits. They should strive to store as
+ many frequently-used cookies as possible. Furthermore, general-use
+ user agents SHOULD provide each of the following minimum capabilities
+ individually, although not necessarily simultaneously:
+
+ * at least 300 cookies
+
+ * at least 4096 bytes per cookie (as measured by the characters
+ that comprise the cookie non-terminal in the syntax description
+ of the Set-Cookie2 header, and as received in the Set-Cookie2
+ header)
+
+ * at least 20 cookies per unique host or domain name
+
+ User agents created for specific purposes or for limited-capacity
+ devices SHOULD provide at least 20 cookies of 4096 bytes, to ensure
+ that the user can interact with a session-based origin server.
+
+ The information in a Set-Cookie2 response header MUST be retained in
+ its entirety. If for some reason there is inadequate space to store
+ the cookie, it MUST be discarded, not truncated.
+
+ Applications should use as few and as small cookies as possible, and
+ they should cope gracefully with the loss of a cookie.
+
+ 5.3.1 Denial of Service Attacks User agents MAY choose to set an
+ upper bound on the number of cookies to be stored from a given host
+ or domain name or on the size of the cookie information. Otherwise a
+ malicious server could attempt to flood a user agent with many
+ cookies, or large cookies, on successive responses, which would force
+ out cookies the user agent had received from other servers. However,
+ the minima specified above SHOULD still be supported.
+
+6. PRIVACY
+
+ Informed consent should guide the design of systems that use cookies.
+ A user should be able to find out how a web site plans to use
+ information in a cookie and should be able to choose whether or not
+ those policies are acceptable. Both the user agent and the origin
+ server must assist informed consent.
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 19]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+6.1 User Agent Control
+
+ An origin server could create a Set-Cookie2 header to track the path
+ of a user through the server. Users may object to this behavior as
+ an intrusive accumulation of information, even if their identity is
+ not evident. (Identity might become evident, for example, if a user
+ subsequently fills out a form that contains identifying information.)
+ This state management specification therefore requires that a user
+ agent give the user control over such a possible intrusion, although
+ the interface through which the user is given this control is left
+ unspecified. However, the control mechanisms provided SHALL at least
+ allow the user
+
+ * to completely disable the sending and saving of cookies.
+
+ * to determine whether a stateful session is in progress.
+
+ * to control the saving of a cookie on the basis of the cookie's
+ Domain attribute.
+
+ Such control could be provided, for example, by mechanisms
+
+ * to notify the user when the user agent is about to send a
+ cookie to the origin server, to offer the option not to begin a
+ session.
+
+ * to display a visual indication that a stateful session is in
+ progress.
+
+ * to let the user decide which cookies, if any, should be saved
+ when the user concludes a window or user agent session.
+
+ * to let the user examine and delete the contents of a cookie at
+ any time.
+
+ A user agent usually begins execution with no remembered state
+ information. It SHOULD be possible to configure a user agent never
+ to send Cookie headers, in which case it can never sustain state with
+ an origin server. (The user agent would then behave like one that is
+ unaware of how to handle Set-Cookie2 response headers.)
+
+ When the user agent terminates execution, it SHOULD let the user
+ discard all state information. Alternatively, the user agent MAY ask
+ the user whether state information should be retained; the default
+ should be "no". If the user chooses to retain state information, it
+ would be restored the next time the user agent runs.
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 20]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ NOTE: User agents should probably be cautious about using files to
+ store cookies long-term. If a user runs more than one instance of
+ the user agent, the cookies could be commingled or otherwise
+ corrupted.
+
+6.2 Origin Server Role
+
+ An origin server SHOULD promote informed consent by adding CommentURL
+ or Comment information to the cookies it sends. CommentURL is
+ preferred because of the opportunity to provide richer information in
+ a multiplicity of languages.
+
+6.3 Clear Text
+
+ The information in the Set-Cookie2 and Cookie headers is unprotected.
+ As a consequence:
+
+ 1. Any sensitive information that is conveyed in them is exposed
+ to intruders.
+
+ 2. A malicious intermediary could alter the headers as they travel
+ in either direction, with unpredictable results.
+
+ These facts imply that information of a personal and/or financial
+ nature should only be sent over a secure channel. For less sensitive
+ information, or when the content of the header is a database key, an
+ origin server should be vigilant to prevent a bad Cookie value from
+ causing failures.
+
+ A user agent in a shared user environment poses a further risk.
+ Using a cookie inspection interface, User B could examine the
+ contents of cookies that were saved when User A used the machine.
+
+7. SECURITY CONSIDERATIONS
+
+7.1 Protocol Design
+
+ The restrictions on the value of the Domain attribute, and the rules
+ concerning unverifiable transactions, are meant to reduce the ways
+ that cookies can "leak" to the "wrong" site. The intent is to
+ restrict cookies to one host, or a closely related set of hosts.
+ Therefore a request-host is limited as to what values it can set for
+ Domain. We consider it acceptable for hosts host1.foo.com and
+ host2.foo.com to share cookies, but not a.com and b.com.
+
+ Similarly, a server can set a Path only for cookies that are related
+ to the request-URI.
+
+
+
+
+Kristol & Montulli Standards Track [Page 21]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+7.2 Cookie Spoofing
+
+ Proper application design can avoid spoofing attacks from related
+ domains. Consider:
+
+ 1. User agent makes request to victim.cracker.edu, gets back
+ cookie session_id="1234" and sets the default domain
+ victim.cracker.edu.
+
+ 2. User agent makes request to spoof.cracker.edu, gets back cookie
+ session-id="1111", with Domain=".cracker.edu".
+
+ 3. User agent makes request to victim.cracker.edu again, and
+ passes
+
+ Cookie: $Version="1"; session_id="1234",
+ $Version="1"; session_id="1111"; $Domain=".cracker.edu"
+
+ The server at victim.cracker.edu should detect that the second
+ cookie was not one it originated by noticing that the Domain
+ attribute is not for itself and ignore it.
+
+7.3 Unexpected Cookie Sharing
+
+ A user agent SHOULD make every attempt to prevent the sharing of
+ session information between hosts that are in different domains.
+ Embedded or inlined objects may cause particularly severe privacy
+ problems if they can be used to share cookies between disparate
+ hosts. For example, a malicious server could embed cookie
+ information for host a.com in a URI for a CGI on host b.com. User
+ agent implementors are strongly encouraged to prevent this sort of
+ exchange whenever possible.
+
+7.4 Cookies For Account Information
+
+ While it is common practice to use them this way, cookies are not
+ designed or intended to be used to hold authentication information,
+ such as account names and passwords. Unless such cookies are
+ exchanged over an encrypted path, the account information they
+ contain is highly vulnerable to perusal and theft.
+
+8. OTHER, SIMILAR, PROPOSALS
+
+ Apart from RFC 2109, three other proposals have been made to
+ accomplish similar goals. This specification began as an amalgam of
+ Kristol's State-Info proposal [DMK95] and Netscape's Cookie proposal
+ [Netscape].
+
+
+
+
+Kristol & Montulli Standards Track [Page 22]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Brian Behlendorf proposed a Session-ID header that would be user-
+ agent-initiated and could be used by an origin server to track
+ "clicktrails". It would not carry any origin-server-defined state,
+ however. Phillip Hallam-Baker has proposed another client-defined
+ session ID mechanism for similar purposes.
+
+ While both session IDs and cookies can provide a way to sustain
+ stateful sessions, their intended purpose is different, and,
+ consequently, the privacy requirements for them are different. A
+ user initiates session IDs to allow servers to track progress through
+ them, or to distinguish multiple users on a shared machine. Cookies
+ are server-initiated, so the cookie mechanism described here gives
+ users control over something that would otherwise take place without
+ the users' awareness. Furthermore, cookies convey rich, server-
+ selected information, whereas session IDs comprise user-selected,
+ simple information.
+
+9. HISTORICAL
+
+9.1 Compatibility with Existing Implementations
+
+ Existing cookie implementations, based on the Netscape specification,
+ use the Set-Cookie (not Set-Cookie2) header. User agents that
+ receive in the same response both a Set-Cookie and Set-Cookie2
+ response header for the same cookie MUST discard the Set-Cookie
+ information and use only the Set-Cookie2 information. Furthermore, a
+ user agent MUST assume, if it received a Set-Cookie2 response header,
+ that the sending server complies with this document and will
+ understand Cookie request headers that also follow this
+ specification.
+
+ New cookies MUST replace both equivalent old- and new-style cookies.
+ That is, if a user agent that follows both this specification and
+ Netscape's original specification receives a Set-Cookie2 response
+ header, and the NAME and the Domain and Path attributes match (per
+ the Cookie Management section) a Netscape-style cookie, the
+ Netscape-style cookie MUST be discarded, and the user agent MUST
+ retain only the cookie adhering to this specification.
+
+ Older user agents that do not understand this specification, but that
+ do understand Netscape's original specification, will not recognize
+ the Set-Cookie2 response header and will receive and send cookies
+ according to the older specification.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 23]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ A user agent that supports both this specification and Netscape-style
+ cookies SHOULD send a Cookie request header that follows the older
+ Netscape specification if it received the cookie in a Set-Cookie
+ response header and not in a Set-Cookie2 response header. However,
+ it SHOULD send the following request header as well:
+
+ Cookie2: $Version="1"
+
+ The Cookie2 header advises the server that the user agent understands
+ new-style cookies. If the server understands new-style cookies, as
+ well, it SHOULD continue the stateful session by sending a Set-
+ Cookie2 response header, rather than Set-Cookie. A server that does
+ not understand new-style cookies will simply ignore the Cookie2
+ request header.
+
+9.2 Caching and HTTP/1.0
+
+ Some caches, such as those conforming to HTTP/1.0, will inevitably
+ cache the Set-Cookie2 and Set-Cookie headers, because there was no
+ mechanism to suppress caching of headers prior to HTTP/1.1. This
+ caching can lead to security problems. Documents transmitted by an
+ origin server along with Set-Cookie2 and Set-Cookie headers usually
+ either will be uncachable, or will be "pre-expired". As long as
+ caches obey instructions not to cache documents (following Expires:
+ <a date in the past> or Pragma: no-cache (HTTP/1.0), or Cache-
+ control: no-cache (HTTP/1.1)) uncachable documents present no
+ problem. However, pre-expired documents may be stored in caches.
+ They require validation (a conditional GET) on each new request, but
+ some cache operators loosen the rules for their caches, and sometimes
+ serve expired documents without first validating them. This
+ combination of factors can lead to cookies meant for one user later
+ being sent to another user. The Set-Cookie2 and Set-Cookie headers
+ are stored in the cache, and, although the document is stale
+ (expired), the cache returns the document in response to later
+ requests, including cached headers.
+
+10. ACKNOWLEDGEMENTS
+
+ This document really represents the collective efforts of the HTTP
+ Working Group of the IETF and, particularly, the following people, in
+ addition to the authors: Roy Fielding, Yaron Goland, Marc Hedlund,
+ Ted Hardie, Koen Holtman, Shel Kaphan, Rohit Khare, Foteos Macrides,
+ David W. Morris.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 24]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+11. AUTHORS' ADDRESSES
+
+ David M. Kristol
+ Bell Laboratories, Lucent Technologies
+ 600 Mountain Ave. Room 2A-333
+ Murray Hill, NJ 07974
+
+ Phone: (908) 582-2250
+ Fax: (908) 582-1239
+ EMail: dmk@bell-labs.com
+
+
+ Lou Montulli
+ Epinions.com, Inc.
+ 2037 Landings Dr.
+ Mountain View, CA 94301
+
+ EMail: lou@montulli.org
+
+12. REFERENCES
+
+ [DMK95] Kristol, D.M., "Proposed HTTP State-Info Mechanism",
+ available at <http://portal.research.bell-
+ labs.com/~dmk/state-info.html>, September, 1995.
+
+ [Netscape] "Persistent Client State -- HTTP Cookies", available at
+ <http://www.netscape.com/newsref/std/cookie_spec.html>,
+ undated.
+
+ [RFC2109] Kristol, D. and L. Montulli, "HTTP State Management
+ Mechanism", RFC 2109, February 1997.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2279] Yergeau, F., "UTF-8, a transformation format of Unicode
+ and ISO-10646", RFC 2279, January 1998.
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+ [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and T.
+ Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1",
+ RFC 2616, June 1999.
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 25]
+\f
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+13. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 26]
+\f
--- /dev/null
+
+
+
+
+
+
+Network Working Group T. Berners-Lee
+Request for Comments: 3986 W3C/MIT
+STD: 66 R. Fielding
+Updates: 1738 Day Software
+Obsoletes: 2732, 2396, 1808 L. Masinter
+Category: Standards Track Adobe Systems
+ January 2005
+
+
+ Uniform Resource Identifier (URI): Generic Syntax
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2005).
+
+Abstract
+
+ A Uniform Resource Identifier (URI) is a compact sequence of
+ characters that identifies an abstract or physical resource. This
+ specification defines the generic URI syntax and a process for
+ resolving URI references that might be in relative form, along with
+ guidelines and security considerations for the use of URIs on the
+ Internet. The URI syntax defines a grammar that is a superset of all
+ valid URIs, allowing an implementation to parse the common components
+ of a URI reference without knowing the scheme-specific requirements
+ of every possible identifier. This specification does not define a
+ generative grammar for URIs; that task is performed by the individual
+ specifications of each URI scheme.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 1]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4
+ 1.1. Overview of URIs . . . . . . . . . . . . . . . . . . . . 4
+ 1.1.1. Generic Syntax . . . . . . . . . . . . . . . . . 6
+ 1.1.2. Examples . . . . . . . . . . . . . . . . . . . . 7
+ 1.1.3. URI, URL, and URN . . . . . . . . . . . . . . . 7
+ 1.2. Design Considerations . . . . . . . . . . . . . . . . . 8
+ 1.2.1. Transcription . . . . . . . . . . . . . . . . . 8
+ 1.2.2. Separating Identification from Interaction . . . 9
+ 1.2.3. Hierarchical Identifiers . . . . . . . . . . . . 10
+ 1.3. Syntax Notation . . . . . . . . . . . . . . . . . . . . 11
+ 2. Characters . . . . . . . . . . . . . . . . . . . . . . . . . . 11
+ 2.1. Percent-Encoding . . . . . . . . . . . . . . . . . . . . 12
+ 2.2. Reserved Characters . . . . . . . . . . . . . . . . . . 12
+ 2.3. Unreserved Characters . . . . . . . . . . . . . . . . . 13
+ 2.4. When to Encode or Decode . . . . . . . . . . . . . . . . 14
+ 2.5. Identifying Data . . . . . . . . . . . . . . . . . . . . 14
+ 3. Syntax Components . . . . . . . . . . . . . . . . . . . . . . 16
+ 3.1. Scheme . . . . . . . . . . . . . . . . . . . . . . . . . 17
+ 3.2. Authority . . . . . . . . . . . . . . . . . . . . . . . 17
+ 3.2.1. User Information . . . . . . . . . . . . . . . . 18
+ 3.2.2. Host . . . . . . . . . . . . . . . . . . . . . . 18
+ 3.2.3. Port . . . . . . . . . . . . . . . . . . . . . . 22
+ 3.3. Path . . . . . . . . . . . . . . . . . . . . . . . . . . 22
+ 3.4. Query . . . . . . . . . . . . . . . . . . . . . . . . . 23
+ 3.5. Fragment . . . . . . . . . . . . . . . . . . . . . . . . 24
+ 4. Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
+ 4.1. URI Reference . . . . . . . . . . . . . . . . . . . . . 25
+ 4.2. Relative Reference . . . . . . . . . . . . . . . . . . . 26
+ 4.3. Absolute URI . . . . . . . . . . . . . . . . . . . . . . 27
+ 4.4. Same-Document Reference . . . . . . . . . . . . . . . . 27
+ 4.5. Suffix Reference . . . . . . . . . . . . . . . . . . . . 27
+ 5. Reference Resolution . . . . . . . . . . . . . . . . . . . . . 28
+ 5.1. Establishing a Base URI . . . . . . . . . . . . . . . . 28
+ 5.1.1. Base URI Embedded in Content . . . . . . . . . . 29
+ 5.1.2. Base URI from the Encapsulating Entity . . . . . 29
+ 5.1.3. Base URI from the Retrieval URI . . . . . . . . 30
+ 5.1.4. Default Base URI . . . . . . . . . . . . . . . . 30
+ 5.2. Relative Resolution . . . . . . . . . . . . . . . . . . 30
+ 5.2.1. Pre-parse the Base URI . . . . . . . . . . . . . 31
+ 5.2.2. Transform References . . . . . . . . . . . . . . 31
+ 5.2.3. Merge Paths . . . . . . . . . . . . . . . . . . 32
+ 5.2.4. Remove Dot Segments . . . . . . . . . . . . . . 33
+ 5.3. Component Recomposition . . . . . . . . . . . . . . . . 35
+ 5.4. Reference Resolution Examples . . . . . . . . . . . . . 35
+ 5.4.1. Normal Examples . . . . . . . . . . . . . . . . 36
+ 5.4.2. Abnormal Examples . . . . . . . . . . . . . . . 36
+
+
+
+Berners-Lee, et al. Standards Track [Page 2]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ 6. Normalization and Comparison . . . . . . . . . . . . . . . . . 38
+ 6.1. Equivalence . . . . . . . . . . . . . . . . . . . . . . 38
+ 6.2. Comparison Ladder . . . . . . . . . . . . . . . . . . . 39
+ 6.2.1. Simple String Comparison . . . . . . . . . . . . 39
+ 6.2.2. Syntax-Based Normalization . . . . . . . . . . . 40
+ 6.2.3. Scheme-Based Normalization . . . . . . . . . . . 41
+ 6.2.4. Protocol-Based Normalization . . . . . . . . . . 42
+ 7. Security Considerations . . . . . . . . . . . . . . . . . . . 43
+ 7.1. Reliability and Consistency . . . . . . . . . . . . . . 43
+ 7.2. Malicious Construction . . . . . . . . . . . . . . . . . 43
+ 7.3. Back-End Transcoding . . . . . . . . . . . . . . . . . . 44
+ 7.4. Rare IP Address Formats . . . . . . . . . . . . . . . . 45
+ 7.5. Sensitive Information . . . . . . . . . . . . . . . . . 45
+ 7.6. Semantic Attacks . . . . . . . . . . . . . . . . . . . . 45
+ 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 46
+ 9. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 46
+ 10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 46
+ 10.1. Normative References . . . . . . . . . . . . . . . . . . 46
+ 10.2. Informative References . . . . . . . . . . . . . . . . . 47
+ A. Collected ABNF for URI . . . . . . . . . . . . . . . . . . . . 49
+ B. Parsing a URI Reference with a Regular Expression . . . . . . 50
+ C. Delimiting a URI in Context . . . . . . . . . . . . . . . . . 51
+ D. Changes from RFC 2396 . . . . . . . . . . . . . . . . . . . . 53
+ D.1. Additions . . . . . . . . . . . . . . . . . . . . . . . 53
+ D.2. Modifications . . . . . . . . . . . . . . . . . . . . . 53
+ Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 60
+ Full Copyright Statement . . . . . . . . . . . . . . . . . . . . . 61
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 3]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+1. Introduction
+
+ A Uniform Resource Identifier (URI) provides a simple and extensible
+ means for identifying a resource. This specification of URI syntax
+ and semantics is derived from concepts introduced by the World Wide
+ Web global information initiative, whose use of these identifiers
+ dates from 1990 and is described in "Universal Resource Identifiers
+ in WWW" [RFC1630]. The syntax is designed to meet the
+ recommendations laid out in "Functional Recommendations for Internet
+ Resource Locators" [RFC1736] and "Functional Requirements for Uniform
+ Resource Names" [RFC1737].
+
+ This document obsoletes [RFC2396], which merged "Uniform Resource
+ Locators" [RFC1738] and "Relative Uniform Resource Locators"
+ [RFC1808] in order to define a single, generic syntax for all URIs.
+ It obsoletes [RFC2732], which introduced syntax for an IPv6 address.
+ It excludes portions of RFC 1738 that defined the specific syntax of
+ individual URI schemes; those portions will be updated as separate
+ documents. The process for registration of new URI schemes is
+ defined separately by [BCP35]. Advice for designers of new URI
+ schemes can be found in [RFC2718]. All significant changes from RFC
+ 2396 are noted in Appendix D.
+
+ This specification uses the terms "character" and "coded character
+ set" in accordance with the definitions provided in [BCP19], and
+ "character encoding" in place of what [BCP19] refers to as a
+ "charset".
+
+1.1. Overview of URIs
+
+ URIs are characterized as follows:
+
+ Uniform
+
+ Uniformity provides several benefits. It allows different types
+ of resource identifiers to be used in the same context, even when
+ the mechanisms used to access those resources may differ. It
+ allows uniform semantic interpretation of common syntactic
+ conventions across different types of resource identifiers. It
+ allows introduction of new types of resource identifiers without
+ interfering with the way that existing identifiers are used. It
+ allows the identifiers to be reused in many different contexts,
+ thus permitting new applications or protocols to leverage a pre-
+ existing, large, and widely used set of resource identifiers.
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 4]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ Resource
+
+ This specification does not limit the scope of what might be a
+ resource; rather, the term "resource" is used in a general sense
+ for whatever might be identified by a URI. Familiar examples
+ include an electronic document, an image, a source of information
+ with a consistent purpose (e.g., "today's weather report for Los
+ Angeles"), a service (e.g., an HTTP-to-SMS gateway), and a
+ collection of other resources. A resource is not necessarily
+ accessible via the Internet; e.g., human beings, corporations, and
+ bound books in a library can also be resources. Likewise,
+ abstract concepts can be resources, such as the operators and
+ operands of a mathematical equation, the types of a relationship
+ (e.g., "parent" or "employee"), or numeric values (e.g., zero,
+ one, and infinity).
+
+ Identifier
+
+ An identifier embodies the information required to distinguish
+ what is being identified from all other things within its scope of
+ identification. Our use of the terms "identify" and "identifying"
+ refer to this purpose of distinguishing one resource from all
+ other resources, regardless of how that purpose is accomplished
+ (e.g., by name, address, or context). These terms should not be
+ mistaken as an assumption that an identifier defines or embodies
+ the identity of what is referenced, though that may be the case
+ for some identifiers. Nor should it be assumed that a system
+ using URIs will access the resource identified: in many cases,
+ URIs are used to denote resources without any intention that they
+ be accessed. Likewise, the "one" resource identified might not be
+ singular in nature (e.g., a resource might be a named set or a
+ mapping that varies over time).
+
+ A URI is an identifier consisting of a sequence of characters
+ matching the syntax rule named <URI> in Section 3. It enables
+ uniform identification of resources via a separately defined
+ extensible set of naming schemes (Section 3.1). How that
+ identification is accomplished, assigned, or enabled is delegated to
+ each scheme specification.
+
+ This specification does not place any limits on the nature of a
+ resource, the reasons why an application might seek to refer to a
+ resource, or the kinds of systems that might use URIs for the sake of
+ identifying resources. This specification does not require that a
+ URI persists in identifying the same resource over time, though that
+ is a common goal of all URI schemes. Nevertheless, nothing in this
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 5]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ specification prevents an application from limiting itself to
+ particular types of resources, or to a subset of URIs that maintains
+ characteristics desired by that application.
+
+ URIs have a global scope and are interpreted consistently regardless
+ of context, though the result of that interpretation may be in
+ relation to the end-user's context. For example, "http://localhost/"
+ has the same interpretation for every user of that reference, even
+ though the network interface corresponding to "localhost" may be
+ different for each end-user: interpretation is independent of access.
+ However, an action made on the basis of that reference will take
+ place in relation to the end-user's context, which implies that an
+ action intended to refer to a globally unique thing must use a URI
+ that distinguishes that resource from all other things. URIs that
+ identify in relation to the end-user's local context should only be
+ used when the context itself is a defining aspect of the resource,
+ such as when an on-line help manual refers to a file on the end-
+ user's file system (e.g., "file:///etc/hosts").
+
+1.1.1. Generic Syntax
+
+ Each URI begins with a scheme name, as defined in Section 3.1, that
+ refers to a specification for assigning identifiers within that
+ scheme. As such, the URI syntax is a federated and extensible naming
+ system wherein each scheme's specification may further restrict the
+ syntax and semantics of identifiers using that scheme.
+
+ This specification defines those elements of the URI syntax that are
+ required of all URI schemes or are common to many URI schemes. It
+ thus defines the syntax and semantics needed to implement a scheme-
+ independent parsing mechanism for URI references, by which the
+ scheme-dependent handling of a URI can be postponed until the
+ scheme-dependent semantics are needed. Likewise, protocols and data
+ formats that make use of URI references can refer to this
+ specification as a definition for the range of syntax allowed for all
+ URIs, including those schemes that have yet to be defined. This
+ decouples the evolution of identification schemes from the evolution
+ of protocols, data formats, and implementations that make use of
+ URIs.
+
+ A parser of the generic URI syntax can parse any URI reference into
+ its major components. Once the scheme is determined, further
+ scheme-specific parsing can be performed on the components. In other
+ words, the URI generic syntax is a superset of the syntax of all URI
+ schemes.
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 6]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+1.1.2. Examples
+
+ The following example URIs illustrate several URI schemes and
+ variations in their common syntax components:
+
+ ftp://ftp.is.co.za/rfc/rfc1808.txt
+
+ http://www.ietf.org/rfc/rfc2396.txt
+
+ ldap://[2001:db8::7]/c=GB?objectClass?one
+
+ mailto:John.Doe@example.com
+
+ news:comp.infosystems.www.servers.unix
+
+ tel:+1-816-555-1212
+
+ telnet://192.0.2.16:80/
+
+ urn:oasis:names:specification:docbook:dtd:xml:4.1.2
+
+
+1.1.3. URI, URL, and URN
+
+ A URI can be further classified as a locator, a name, or both. The
+ term "Uniform Resource Locator" (URL) refers to the subset of URIs
+ that, in addition to identifying a resource, provide a means of
+ locating the resource by describing its primary access mechanism
+ (e.g., its network "location"). The term "Uniform Resource Name"
+ (URN) has been used historically to refer to both URIs under the
+ "urn" scheme [RFC2141], which are required to remain globally unique
+ and persistent even when the resource ceases to exist or becomes
+ unavailable, and to any other URI with the properties of a name.
+
+ An individual scheme does not have to be classified as being just one
+ of "name" or "locator". Instances of URIs from any given scheme may
+ have the characteristics of names or locators or both, often
+ depending on the persistence and care in the assignment of
+ identifiers by the naming authority, rather than on any quality of
+ the scheme. Future specifications and related documentation should
+ use the general term "URI" rather than the more restrictive terms
+ "URL" and "URN" [RFC3305].
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 7]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+1.2. Design Considerations
+
+1.2.1. Transcription
+
+ The URI syntax has been designed with global transcription as one of
+ its main considerations. A URI is a sequence of characters from a
+ very limited set: the letters of the basic Latin alphabet, digits,
+ and a few special characters. A URI may be represented in a variety
+ of ways; e.g., ink on paper, pixels on a screen, or a sequence of
+ character encoding octets. The interpretation of a URI depends only
+ on the characters used and not on how those characters are
+ represented in a network protocol.
+
+ The goal of transcription can be described by a simple scenario.
+ Imagine two colleagues, Sam and Kim, sitting in a pub at an
+ international conference and exchanging research ideas. Sam asks Kim
+ for a location to get more information, so Kim writes the URI for the
+ research site on a napkin. Upon returning home, Sam takes out the
+ napkin and types the URI into a computer, which then retrieves the
+ information to which Kim referred.
+
+ There are several design considerations revealed by the scenario:
+
+ o A URI is a sequence of characters that is not always represented
+ as a sequence of octets.
+
+ o A URI might be transcribed from a non-network source and thus
+ should consist of characters that are most likely able to be
+ entered into a computer, within the constraints imposed by
+ keyboards (and related input devices) across languages and
+ locales.
+
+ o A URI often has to be remembered by people, and it is easier for
+ people to remember a URI when it consists of meaningful or
+ familiar components.
+
+ These design considerations are not always in alignment. For
+ example, it is often the case that the most meaningful name for a URI
+ component would require characters that cannot be typed into some
+ systems. The ability to transcribe a resource identifier from one
+ medium to another has been considered more important than having a
+ URI consist of the most meaningful of components.
+
+ In local or regional contexts and with improving technology, users
+ might benefit from being able to use a wider range of characters;
+ such use is not defined by this specification. Percent-encoded
+ octets (Section 2.1) may be used within a URI to represent characters
+ outside the range of the US-ASCII coded character set if this
+
+
+
+Berners-Lee, et al. Standards Track [Page 8]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ representation is allowed by the scheme or by the protocol element in
+ which the URI is referenced. Such a definition should specify the
+ character encoding used to map those characters to octets prior to
+ being percent-encoded for the URI.
+
+1.2.2. Separating Identification from Interaction
+
+ A common misunderstanding of URIs is that they are only used to refer
+ to accessible resources. The URI itself only provides
+ identification; access to the resource is neither guaranteed nor
+ implied by the presence of a URI. Instead, any operation associated
+ with a URI reference is defined by the protocol element, data format
+ attribute, or natural language text in which it appears.
+
+ Given a URI, a system may attempt to perform a variety of operations
+ on the resource, as might be characterized by words such as "access",
+ "update", "replace", or "find attributes". Such operations are
+ defined by the protocols that make use of URIs, not by this
+ specification. However, we do use a few general terms for describing
+ common operations on URIs. URI "resolution" is the process of
+ determining an access mechanism and the appropriate parameters
+ necessary to dereference a URI; this resolution may require several
+ iterations. To use that access mechanism to perform an action on the
+ URI's resource is to "dereference" the URI.
+
+ When URIs are used within information retrieval systems to identify
+ sources of information, the most common form of URI dereference is
+ "retrieval": making use of a URI in order to retrieve a
+ representation of its associated resource. A "representation" is a
+ sequence of octets, along with representation metadata describing
+ those octets, that constitutes a record of the state of the resource
+ at the time when the representation is generated. Retrieval is
+ achieved by a process that might include using the URI as a cache key
+ to check for a locally cached representation, resolution of the URI
+ to determine an appropriate access mechanism (if any), and
+ dereference of the URI for the sake of applying a retrieval
+ operation. Depending on the protocols used to perform the retrieval,
+ additional information might be supplied about the resource (resource
+ metadata) and its relation to other resources.
+
+ URI references in information retrieval systems are designed to be
+ late-binding: the result of an access is generally determined when it
+ is accessed and may vary over time or due to other aspects of the
+ interaction. These references are created in order to be used in the
+ future: what is being identified is not some specific result that was
+ obtained in the past, but rather some characteristic that is expected
+ to be true for future results. In such cases, the resource referred
+ to by the URI is actually a sameness of characteristics as observed
+
+
+
+Berners-Lee, et al. Standards Track [Page 9]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ over time, perhaps elucidated by additional comments or assertions
+ made by the resource provider.
+
+ Although many URI schemes are named after protocols, this does not
+ imply that use of these URIs will result in access to the resource
+ via the named protocol. URIs are often used simply for the sake of
+ identification. Even when a URI is used to retrieve a representation
+ of a resource, that access might be through gateways, proxies,
+ caches, and name resolution services that are independent of the
+ protocol associated with the scheme name. The resolution of some
+ URIs may require the use of more than one protocol (e.g., both DNS
+ and HTTP are typically used to access an "http" URI's origin server
+ when a representation isn't found in a local cache).
+
+1.2.3. Hierarchical Identifiers
+
+ The URI syntax is organized hierarchically, with components listed in
+ order of decreasing significance from left to right. For some URI
+ schemes, the visible hierarchy is limited to the scheme itself:
+ everything after the scheme component delimiter (":") is considered
+ opaque to URI processing. Other URI schemes make the hierarchy
+ explicit and visible to generic parsing algorithms.
+
+ The generic syntax uses the slash ("/"), question mark ("?"), and
+ number sign ("#") characters to delimit components that are
+ significant to the generic parser's hierarchical interpretation of an
+ identifier. In addition to aiding the readability of such
+ identifiers through the consistent use of familiar syntax, this
+ uniform representation of hierarchy across naming schemes allows
+ scheme-independent references to be made relative to that hierarchy.
+
+ It is often the case that a group or "tree" of documents has been
+ constructed to serve a common purpose, wherein the vast majority of
+ URI references in these documents point to resources within the tree
+ rather than outside it. Similarly, documents located at a particular
+ site are much more likely to refer to other resources at that site
+ than to resources at remote sites. Relative referencing of URIs
+ allows document trees to be partially independent of their location
+ and access scheme. For instance, it is possible for a single set of
+ hypertext documents to be simultaneously accessible and traversable
+ via each of the "file", "http", and "ftp" schemes if the documents
+ refer to each other with relative references. Furthermore, such
+ document trees can be moved, as a whole, without changing any of the
+ relative references.
+
+ A relative reference (Section 4.2) refers to a resource by describing
+ the difference within a hierarchical name space between the reference
+ context and the target URI. The reference resolution algorithm,
+
+
+
+Berners-Lee, et al. Standards Track [Page 10]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ presented in Section 5, defines how such a reference is transformed
+ to the target URI. As relative references can only be used within
+ the context of a hierarchical URI, designers of new URI schemes
+ should use a syntax consistent with the generic syntax's hierarchical
+ components unless there are compelling reasons to forbid relative
+ referencing within that scheme.
+
+ NOTE: Previous specifications used the terms "partial URI" and
+ "relative URI" to denote a relative reference to a URI. As some
+ readers misunderstood those terms to mean that relative URIs are a
+ subset of URIs rather than a method of referencing URIs, this
+ specification simply refers to them as relative references.
+
+ All URI references are parsed by generic syntax parsers when used.
+ However, because hierarchical processing has no effect on an absolute
+ URI used in a reference unless it contains one or more dot-segments
+ (complete path segments of "." or "..", as described in Section 3.3),
+ URI scheme specifications can define opaque identifiers by
+ disallowing use of slash characters, question mark characters, and
+ the URIs "scheme:." and "scheme:..".
+
+1.3. Syntax Notation
+
+ This specification uses the Augmented Backus-Naur Form (ABNF)
+ notation of [RFC2234], including the following core ABNF syntax rules
+ defined by that specification: ALPHA (letters), CR (carriage return),
+ DIGIT (decimal digits), DQUOTE (double quote), HEXDIG (hexadecimal
+ digits), LF (line feed), and SP (space). The complete URI syntax is
+ collected in Appendix A.
+
+2. Characters
+
+ The URI syntax provides a method of encoding data, presumably for the
+ sake of identifying a resource, as a sequence of characters. The URI
+ characters are, in turn, frequently encoded as octets for transport
+ or presentation. This specification does not mandate any particular
+ character encoding for mapping between URI characters and the octets
+ used to store or transmit those characters. When a URI appears in a
+ protocol element, the character encoding is defined by that protocol;
+ without such a definition, a URI is assumed to be in the same
+ character encoding as the surrounding text.
+
+ The ABNF notation defines its terminal values to be non-negative
+ integers (codepoints) based on the US-ASCII coded character set
+ [ASCII]. Because a URI is a sequence of characters, we must invert
+ that relation in order to understand the URI syntax. Therefore, the
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 11]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ integer values used by the ABNF must be mapped back to their
+ corresponding characters via US-ASCII in order to complete the syntax
+ rules.
+
+ A URI is composed from a limited set of characters consisting of
+ digits, letters, and a few graphic symbols. A reserved subset of
+ those characters may be used to delimit syntax components within a
+ URI while the remaining characters, including both the unreserved set
+ and those reserved characters not acting as delimiters, define each
+ component's identifying data.
+
+2.1. Percent-Encoding
+
+ A percent-encoding mechanism is used to represent a data octet in a
+ component when that octet's corresponding character is outside the
+ allowed set or is being used as a delimiter of, or within, the
+ component. A percent-encoded octet is encoded as a character
+ triplet, consisting of the percent character "%" followed by the two
+ hexadecimal digits representing that octet's numeric value. For
+ example, "%20" is the percent-encoding for the binary octet
+ "00100000" (ABNF: %x20), which in US-ASCII corresponds to the space
+ character (SP). Section 2.4 describes when percent-encoding and
+ decoding is applied.
+
+ pct-encoded = "%" HEXDIG HEXDIG
+
+ The uppercase hexadecimal digits 'A' through 'F' are equivalent to
+ the lowercase digits 'a' through 'f', respectively. If two URIs
+ differ only in the case of hexadecimal digits used in percent-encoded
+ octets, they are equivalent. For consistency, URI producers and
+ normalizers should use uppercase hexadecimal digits for all percent-
+ encodings.
+
+2.2. Reserved Characters
+
+ URIs include components and subcomponents that are delimited by
+ characters in the "reserved" set. These characters are called
+ "reserved" because they may (or may not) be defined as delimiters by
+ the generic syntax, by each scheme-specific syntax, or by the
+ implementation-specific syntax of a URI's dereferencing algorithm.
+ If data for a URI component would conflict with a reserved
+ character's purpose as a delimiter, then the conflicting data must be
+ percent-encoded before the URI is formed.
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 12]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ reserved = gen-delims / sub-delims
+
+ gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+
+ sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ / "*" / "+" / "," / ";" / "="
+
+ The purpose of reserved characters is to provide a set of delimiting
+ characters that are distinguishable from other data within a URI.
+ URIs that differ in the replacement of a reserved character with its
+ corresponding percent-encoded octet are not equivalent. Percent-
+ encoding a reserved character, or decoding a percent-encoded octet
+ that corresponds to a reserved character, will change how the URI is
+ interpreted by most applications. Thus, characters in the reserved
+ set are protected from normalization and are therefore safe to be
+ used by scheme-specific and producer-specific algorithms for
+ delimiting data subcomponents within a URI.
+
+ A subset of the reserved characters (gen-delims) is used as
+ delimiters of the generic URI components described in Section 3. A
+ component's ABNF syntax rule will not use the reserved or gen-delims
+ rule names directly; instead, each syntax rule lists the characters
+ allowed within that component (i.e., not delimiting it), and any of
+ those characters that are also in the reserved set are "reserved" for
+ use as subcomponent delimiters within the component. Only the most
+ common subcomponents are defined by this specification; other
+ subcomponents may be defined by a URI scheme's specification, or by
+ the implementation-specific syntax of a URI's dereferencing
+ algorithm, provided that such subcomponents are delimited by
+ characters in the reserved set allowed within that component.
+
+ URI producing applications should percent-encode data octets that
+ correspond to characters in the reserved set unless these characters
+ are specifically allowed by the URI scheme to represent data in that
+ component. If a reserved character is found in a URI component and
+ no delimiting role is known for that character, then it must be
+ interpreted as representing the data octet corresponding to that
+ character's encoding in US-ASCII.
+
+2.3. Unreserved Characters
+
+ Characters that are allowed in a URI but do not have a reserved
+ purpose are called unreserved. These include uppercase and lowercase
+ letters, decimal digits, hyphen, period, underscore, and tilde.
+
+ unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 13]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ URIs that differ in the replacement of an unreserved character with
+ its corresponding percent-encoded US-ASCII octet are equivalent: they
+ identify the same resource. However, URI comparison implementations
+ do not always perform normalization prior to comparison (see Section
+ 6). For consistency, percent-encoded octets in the ranges of ALPHA
+ (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E),
+ underscore (%5F), or tilde (%7E) should not be created by URI
+ producers and, when found in a URI, should be decoded to their
+ corresponding unreserved characters by URI normalizers.
+
+2.4. When to Encode or Decode
+
+ Under normal circumstances, the only time when octets within a URI
+ are percent-encoded is during the process of producing the URI from
+ its component parts. This is when an implementation determines which
+ of the reserved characters are to be used as subcomponent delimiters
+ and which can be safely used as data. Once produced, a URI is always
+ in its percent-encoded form.
+
+ When a URI is dereferenced, the components and subcomponents
+ significant to the scheme-specific dereferencing process (if any)
+ must be parsed and separated before the percent-encoded octets within
+ those components can be safely decoded, as otherwise the data may be
+ mistaken for component delimiters. The only exception is for
+ percent-encoded octets corresponding to characters in the unreserved
+ set, which can be decoded at any time. For example, the octet
+ corresponding to the tilde ("~") character is often encoded as "%7E"
+ by older URI processing implementations; the "%7E" can be replaced by
+ "~" without changing its interpretation.
+
+ Because the percent ("%") character serves as the indicator for
+ percent-encoded octets, it must be percent-encoded as "%25" for that
+ octet to be used as data within a URI. Implementations must not
+ percent-encode or decode the same string more than once, as decoding
+ an already decoded string might lead to misinterpreting a percent
+ data octet as the beginning of a percent-encoding, or vice versa in
+ the case of percent-encoding an already percent-encoded string.
+
+2.5. Identifying Data
+
+ URI characters provide identifying data for each of the URI
+ components, serving as an external interface for identification
+ between systems. Although the presence and nature of the URI
+ production interface is hidden from clients that use its URIs (and is
+ thus beyond the scope of the interoperability requirements defined by
+ this specification), it is a frequent source of confusion and errors
+ in the interpretation of URI character issues. Implementers have to
+ be aware that there are multiple character encodings involved in the
+
+
+
+Berners-Lee, et al. Standards Track [Page 14]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ production and transmission of URIs: local name and data encoding,
+ public interface encoding, URI character encoding, data format
+ encoding, and protocol encoding.
+
+ Local names, such as file system names, are stored with a local
+ character encoding. URI producing applications (e.g., origin
+ servers) will typically use the local encoding as the basis for
+ producing meaningful names. The URI producer will transform the
+ local encoding to one that is suitable for a public interface and
+ then transform the public interface encoding into the restricted set
+ of URI characters (reserved, unreserved, and percent-encodings).
+ Those characters are, in turn, encoded as octets to be used as a
+ reference within a data format (e.g., a document charset), and such
+ data formats are often subsequently encoded for transmission over
+ Internet protocols.
+
+ For most systems, an unreserved character appearing within a URI
+ component is interpreted as representing the data octet corresponding
+ to that character's encoding in US-ASCII. Consumers of URIs assume
+ that the letter "X" corresponds to the octet "01011000", and even
+ when that assumption is incorrect, there is no harm in making it. A
+ system that internally provides identifiers in the form of a
+ different character encoding, such as EBCDIC, will generally perform
+ character translation of textual identifiers to UTF-8 [STD63] (or
+ some other superset of the US-ASCII character encoding) at an
+ internal interface, thereby providing more meaningful identifiers
+ than those resulting from simply percent-encoding the original
+ octets.
+
+ For example, consider an information service that provides data,
+ stored locally using an EBCDIC-based file system, to clients on the
+ Internet through an HTTP server. When an author creates a file with
+ the name "Laguna Beach" on that file system, the "http" URI
+ corresponding to that resource is expected to contain the meaningful
+ string "Laguna%20Beach". If, however, that server produces URIs by
+ using an overly simplistic raw octet mapping, then the result would
+ be a URI containing "%D3%81%87%A4%95%81@%C2%85%81%83%88". An
+ internal transcoding interface fixes this problem by transcoding the
+ local name to a superset of US-ASCII prior to producing the URI.
+ Naturally, proper interpretation of an incoming URI on such an
+ interface requires that percent-encoded octets be decoded (e.g.,
+ "%20" to SP) before the reverse transcoding is applied to obtain the
+ local name.
+
+ In some cases, the internal interface between a URI component and the
+ identifying data that it has been crafted to represent is much less
+ direct than a character encoding translation. For example, portions
+ of a URI might reflect a query on non-ASCII data, or numeric
+
+
+
+Berners-Lee, et al. Standards Track [Page 15]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ coordinates on a map. Likewise, a URI scheme may define components
+ with additional encoding requirements that are applied prior to
+ forming the component and producing the URI.
+
+ When a new URI scheme defines a component that represents textual
+ data consisting of characters from the Universal Character Set [UCS],
+ the data should first be encoded as octets according to the UTF-8
+ character encoding [STD63]; then only those octets that do not
+ correspond to characters in the unreserved set should be percent-
+ encoded. For example, the character A would be represented as "A",
+ the character LATIN CAPITAL LETTER A WITH GRAVE would be represented
+ as "%C3%80", and the character KATAKANA LETTER A would be represented
+ as "%E3%82%A2".
+
+3. Syntax Components
+
+ The generic URI syntax consists of a hierarchical sequence of
+ components referred to as the scheme, authority, path, query, and
+ fragment.
+
+ URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+
+ hier-part = "//" authority path-abempty
+ / path-absolute
+ / path-rootless
+ / path-empty
+
+ The scheme and path components are required, though the path may be
+ empty (no characters). When authority is present, the path must
+ either be empty or begin with a slash ("/") character. When
+ authority is not present, the path cannot begin with two slash
+ characters ("//"). These restrictions result in five different ABNF
+ rules for a path (Section 3.3), only one of which will match any
+ given URI reference.
+
+ The following are two example URIs and their component parts:
+
+ foo://example.com:8042/over/there?name=ferret#nose
+ \_/ \______________/\_________/ \_________/ \__/
+ | | | | |
+ scheme authority path query fragment
+ | _____________________|__
+ / \ / \
+ urn:example:animal:ferret:nose
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 16]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+3.1. Scheme
+
+ Each URI begins with a scheme name that refers to a specification for
+ assigning identifiers within that scheme. As such, the URI syntax is
+ a federated and extensible naming system wherein each scheme's
+ specification may further restrict the syntax and semantics of
+ identifiers using that scheme.
+
+ Scheme names consist of a sequence of characters beginning with a
+ letter and followed by any combination of letters, digits, plus
+ ("+"), period ("."), or hyphen ("-"). Although schemes are case-
+ insensitive, the canonical form is lowercase and documents that
+ specify schemes must do so with lowercase letters. An implementation
+ should accept uppercase letters as equivalent to lowercase in scheme
+ names (e.g., allow "HTTP" as well as "http") for the sake of
+ robustness but should only produce lowercase scheme names for
+ consistency.
+
+ scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+
+ Individual schemes are not specified by this document. The process
+ for registration of new URI schemes is defined separately by [BCP35].
+ The scheme registry maintains the mapping between scheme names and
+ their specifications. Advice for designers of new URI schemes can be
+ found in [RFC2718]. URI scheme specifications must define their own
+ syntax so that all strings matching their scheme-specific syntax will
+ also match the <absolute-URI> grammar, as described in Section 4.3.
+
+ When presented with a URI that violates one or more scheme-specific
+ restrictions, the scheme-specific resolution process should flag the
+ reference as an error rather than ignore the unused parts; doing so
+ reduces the number of equivalent URIs and helps detect abuses of the
+ generic syntax, which might indicate that the URI has been
+ constructed to mislead the user (Section 7.6).
+
+3.2. Authority
+
+ Many URI schemes include a hierarchical element for a naming
+ authority so that governance of the name space defined by the
+ remainder of the URI is delegated to that authority (which may, in
+ turn, delegate it further). The generic syntax provides a common
+ means for distinguishing an authority based on a registered name or
+ server address, along with optional port and user information.
+
+ The authority component is preceded by a double slash ("//") and is
+ terminated by the next slash ("/"), question mark ("?"), or number
+ sign ("#") character, or by the end of the URI.
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 17]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ authority = [ userinfo "@" ] host [ ":" port ]
+
+ URI producers and normalizers should omit the ":" delimiter that
+ separates host from port if the port component is empty. Some
+ schemes do not allow the userinfo and/or port subcomponents.
+
+ If a URI contains an authority component, then the path component
+ must either be empty or begin with a slash ("/") character. Non-
+ validating parsers (those that merely separate a URI reference into
+ its major components) will often ignore the subcomponent structure of
+ authority, treating it as an opaque string from the double-slash to
+ the first terminating delimiter, until such time as the URI is
+ dereferenced.
+
+3.2.1. User Information
+
+ The userinfo subcomponent may consist of a user name and, optionally,
+ scheme-specific information about how to gain authorization to access
+ the resource. The user information, if present, is followed by a
+ commercial at-sign ("@") that delimits it from the host.
+
+ userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+
+ Use of the format "user:password" in the userinfo field is
+ deprecated. Applications should not render as clear text any data
+ after the first colon (":") character found within a userinfo
+ subcomponent unless the data after the colon is the empty string
+ (indicating no password). Applications may choose to ignore or
+ reject such data when it is received as part of a reference and
+ should reject the storage of such data in unencrypted form. The
+ passing of authentication information in clear text has proven to be
+ a security risk in almost every case where it has been used.
+
+ Applications that render a URI for the sake of user feedback, such as
+ in graphical hypertext browsing, should render userinfo in a way that
+ is distinguished from the rest of a URI, when feasible. Such
+ rendering will assist the user in cases where the userinfo has been
+ misleadingly crafted to look like a trusted domain name
+ (Section 7.6).
+
+3.2.2. Host
+
+ The host subcomponent of authority is identified by an IP literal
+ encapsulated within square brackets, an IPv4 address in dotted-
+ decimal form, or a registered name. The host subcomponent is case-
+ insensitive. The presence of a host subcomponent within a URI does
+ not imply that the scheme requires access to the given host on the
+ Internet. In many cases, the host syntax is used only for the sake
+
+
+
+Berners-Lee, et al. Standards Track [Page 18]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ of reusing the existing registration process created and deployed for
+ DNS, thus obtaining a globally unique name without the cost of
+ deploying another registry. However, such use comes with its own
+ costs: domain name ownership may change over time for reasons not
+ anticipated by the URI producer. In other cases, the data within the
+ host component identifies a registered name that has nothing to do
+ with an Internet host. We use the name "host" for the ABNF rule
+ because that is its most common purpose, not its only purpose.
+
+ host = IP-literal / IPv4address / reg-name
+
+ The syntax rule for host is ambiguous because it does not completely
+ distinguish between an IPv4address and a reg-name. In order to
+ disambiguate the syntax, we apply the "first-match-wins" algorithm:
+ If host matches the rule for IPv4address, then it should be
+ considered an IPv4 address literal and not a reg-name. Although host
+ is case-insensitive, producers and normalizers should use lowercase
+ for registered names and hexadecimal addresses for the sake of
+ uniformity, while only using uppercase letters for percent-encodings.
+
+ A host identified by an Internet Protocol literal address, version 6
+ [RFC3513] or later, is distinguished by enclosing the IP literal
+ within square brackets ("[" and "]"). This is the only place where
+ square bracket characters are allowed in the URI syntax. In
+ anticipation of future, as-yet-undefined IP literal address formats,
+ an implementation may use an optional version flag to indicate such a
+ format explicitly rather than rely on heuristic determination.
+
+ IP-literal = "[" ( IPv6address / IPvFuture ) "]"
+
+ IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
+
+ The version flag does not indicate the IP version; rather, it
+ indicates future versions of the literal format. As such,
+ implementations must not provide the version flag for the existing
+ IPv4 and IPv6 literal address forms described below. If a URI
+ containing an IP-literal that starts with "v" (case-insensitive),
+ indicating that the version flag is present, is dereferenced by an
+ application that does not know the meaning of that version flag, then
+ the application should return an appropriate error for "address
+ mechanism not supported".
+
+ A host identified by an IPv6 literal address is represented inside
+ the square brackets without a preceding version flag. The ABNF
+ provided here is a translation of the text definition of an IPv6
+ literal address provided in [RFC3513]. This syntax does not support
+ IPv6 scoped addressing zone identifiers.
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 19]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ A 128-bit IPv6 address is divided into eight 16-bit pieces. Each
+ piece is represented numerically in case-insensitive hexadecimal,
+ using one to four hexadecimal digits (leading zeroes are permitted).
+ The eight encoded pieces are given most-significant first, separated
+ by colon characters. Optionally, the least-significant two pieces
+ may instead be represented in IPv4 address textual format. A
+ sequence of one or more consecutive zero-valued 16-bit pieces within
+ the address may be elided, omitting all their digits and leaving
+ exactly two consecutive colons in their place to mark the elision.
+
+ IPv6address = 6( h16 ":" ) ls32
+ / "::" 5( h16 ":" ) ls32
+ / [ h16 ] "::" 4( h16 ":" ) ls32
+ / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
+ / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
+ / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
+ / [ *4( h16 ":" ) h16 ] "::" ls32
+ / [ *5( h16 ":" ) h16 ] "::" h16
+ / [ *6( h16 ":" ) h16 ] "::"
+
+ ls32 = ( h16 ":" h16 ) / IPv4address
+ ; least-significant 32 bits of address
+
+ h16 = 1*4HEXDIG
+ ; 16 bits of address represented in hexadecimal
+
+ A host identified by an IPv4 literal address is represented in
+ dotted-decimal notation (a sequence of four decimal numbers in the
+ range 0 to 255, separated by "."), as described in [RFC1123] by
+ reference to [RFC0952]. Note that other forms of dotted notation may
+ be interpreted on some platforms, as described in Section 7.4, but
+ only the dotted-decimal form of four octets is allowed by this
+ grammar.
+
+ IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
+
+ dec-octet = DIGIT ; 0-9
+ / %x31-39 DIGIT ; 10-99
+ / "1" 2DIGIT ; 100-199
+ / "2" %x30-34 DIGIT ; 200-249
+ / "25" %x30-35 ; 250-255
+
+ A host identified by a registered name is a sequence of characters
+ usually intended for lookup within a locally defined host or service
+ name registry, though the URI's scheme-specific semantics may require
+ that a specific registry (or fixed name table) be used instead. The
+ most common name registry mechanism is the Domain Name System (DNS).
+ A registered name intended for lookup in the DNS uses the syntax
+
+
+
+Berners-Lee, et al. Standards Track [Page 20]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ defined in Section 3.5 of [RFC1034] and Section 2.1 of [RFC1123].
+ Such a name consists of a sequence of domain labels separated by ".",
+ each domain label starting and ending with an alphanumeric character
+ and possibly also containing "-" characters. The rightmost domain
+ label of a fully qualified domain name in DNS may be followed by a
+ single "." and should be if it is necessary to distinguish between
+ the complete domain name and some local domain.
+
+ reg-name = *( unreserved / pct-encoded / sub-delims )
+
+ If the URI scheme defines a default for host, then that default
+ applies when the host subcomponent is undefined or when the
+ registered name is empty (zero length). For example, the "file" URI
+ scheme is defined so that no authority, an empty host, and
+ "localhost" all mean the end-user's machine, whereas the "http"
+ scheme considers a missing authority or empty host invalid.
+
+ This specification does not mandate a particular registered name
+ lookup technology and therefore does not restrict the syntax of reg-
+ name beyond what is necessary for interoperability. Instead, it
+ delegates the issue of registered name syntax conformance to the
+ operating system of each application performing URI resolution, and
+ that operating system decides what it will allow for the purpose of
+ host identification. A URI resolution implementation might use DNS,
+ host tables, yellow pages, NetInfo, WINS, or any other system for
+ lookup of registered names. However, a globally scoped naming
+ system, such as DNS fully qualified domain names, is necessary for
+ URIs intended to have global scope. URI producers should use names
+ that conform to the DNS syntax, even when use of DNS is not
+ immediately apparent, and should limit these names to no more than
+ 255 characters in length.
+
+ The reg-name syntax allows percent-encoded octets in order to
+ represent non-ASCII registered names in a uniform way that is
+ independent of the underlying name resolution technology. Non-ASCII
+ characters must first be encoded according to UTF-8 [STD63], and then
+ each octet of the corresponding UTF-8 sequence must be percent-
+ encoded to be represented as URI characters. URI producing
+ applications must not use percent-encoding in host unless it is used
+ to represent a UTF-8 character sequence. When a non-ASCII registered
+ name represents an internationalized domain name intended for
+ resolution via the DNS, the name must be transformed to the IDNA
+ encoding [RFC3490] prior to name lookup. URI producers should
+ provide these registered names in the IDNA encoding, rather than a
+ percent-encoding, if they wish to maximize interoperability with
+ legacy URI resolvers.
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 21]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+3.2.3. Port
+
+ The port subcomponent of authority is designated by an optional port
+ number in decimal following the host and delimited from it by a
+ single colon (":") character.
+
+ port = *DIGIT
+
+ A scheme may define a default port. For example, the "http" scheme
+ defines a default port of "80", corresponding to its reserved TCP
+ port number. The type of port designated by the port number (e.g.,
+ TCP, UDP, SCTP) is defined by the URI scheme. URI producers and
+ normalizers should omit the port component and its ":" delimiter if
+ port is empty or if its value would be the same as that of the
+ scheme's default.
+
+3.3. Path
+
+ The path component contains data, usually organized in hierarchical
+ form, that, along with data in the non-hierarchical query component
+ (Section 3.4), serves to identify a resource within the scope of the
+ URI's scheme and naming authority (if any). The path is terminated
+ by the first question mark ("?") or number sign ("#") character, or
+ by the end of the URI.
+
+ If a URI contains an authority component, then the path component
+ must either be empty or begin with a slash ("/") character. If a URI
+ does not contain an authority component, then the path cannot begin
+ with two slash characters ("//"). In addition, a URI reference
+ (Section 4.1) may be a relative-path reference, in which case the
+ first path segment cannot contain a colon (":") character. The ABNF
+ requires five separate rules to disambiguate these cases, only one of
+ which will match the path substring within a given URI reference. We
+ use the generic term "path component" to describe the URI substring
+ matched by the parser to one of these rules.
+
+ path = path-abempty ; begins with "/" or is empty
+ / path-absolute ; begins with "/" but not "//"
+ / path-noscheme ; begins with a non-colon segment
+ / path-rootless ; begins with a segment
+ / path-empty ; zero characters
+
+ path-abempty = *( "/" segment )
+ path-absolute = "/" [ segment-nz *( "/" segment ) ]
+ path-noscheme = segment-nz-nc *( "/" segment )
+ path-rootless = segment-nz *( "/" segment )
+ path-empty = 0<pchar>
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 22]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ segment = *pchar
+ segment-nz = 1*pchar
+ segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
+ ; non-zero-length segment without any colon ":"
+
+ pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+
+ A path consists of a sequence of path segments separated by a slash
+ ("/") character. A path is always defined for a URI, though the
+ defined path may be empty (zero length). Use of the slash character
+ to indicate hierarchy is only required when a URI will be used as the
+ context for relative references. For example, the URI
+ <mailto:fred@example.com> has a path of "fred@example.com", whereas
+ the URI <foo://info.example.com?fred> has an empty path.
+
+ The path segments "." and "..", also known as dot-segments, are
+ defined for relative reference within the path name hierarchy. They
+ are intended for use at the beginning of a relative-path reference
+ (Section 4.2) to indicate relative position within the hierarchical
+ tree of names. This is similar to their role within some operating
+ systems' file directory structures to indicate the current directory
+ and parent directory, respectively. However, unlike in a file
+ system, these dot-segments are only interpreted within the URI path
+ hierarchy and are removed as part of the resolution process (Section
+ 5.2).
+
+ Aside from dot-segments in hierarchical paths, a path segment is
+ considered opaque by the generic syntax. URI producing applications
+ often use the reserved characters allowed in a segment to delimit
+ scheme-specific or dereference-handler-specific subcomponents. For
+ example, the semicolon (";") and equals ("=") reserved characters are
+ often used to delimit parameters and parameter values applicable to
+ that segment. The comma (",") reserved character is often used for
+ similar purposes. For example, one URI producer might use a segment
+ such as "name;v=1.1" to indicate a reference to version 1.1 of
+ "name", whereas another might use a segment such as "name,1.1" to
+ indicate the same. Parameter types may be defined by scheme-specific
+ semantics, but in most cases the syntax of a parameter is specific to
+ the implementation of the URI's dereferencing algorithm.
+
+3.4. Query
+
+ The query component contains non-hierarchical data that, along with
+ data in the path component (Section 3.3), serves to identify a
+ resource within the scope of the URI's scheme and naming authority
+ (if any). The query component is indicated by the first question
+ mark ("?") character and terminated by a number sign ("#") character
+ or by the end of the URI.
+
+
+
+Berners-Lee, et al. Standards Track [Page 23]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ query = *( pchar / "/" / "?" )
+
+ The characters slash ("/") and question mark ("?") may represent data
+ within the query component. Beware that some older, erroneous
+ implementations may not handle such data correctly when it is used as
+ the base URI for relative references (Section 5.1), apparently
+ because they fail to distinguish query data from path data when
+ looking for hierarchical separators. However, as query components
+ are often used to carry identifying information in the form of
+ "key=value" pairs and one frequently used value is a reference to
+ another URI, it is sometimes better for usability to avoid percent-
+ encoding those characters.
+
+3.5. Fragment
+
+ The fragment identifier component of a URI allows indirect
+ identification of a secondary resource by reference to a primary
+ resource and additional identifying information. The identified
+ secondary resource may be some portion or subset of the primary
+ resource, some view on representations of the primary resource, or
+ some other resource defined or described by those representations. A
+ fragment identifier component is indicated by the presence of a
+ number sign ("#") character and terminated by the end of the URI.
+
+ fragment = *( pchar / "/" / "?" )
+
+ The semantics of a fragment identifier are defined by the set of
+ representations that might result from a retrieval action on the
+ primary resource. The fragment's format and resolution is therefore
+ dependent on the media type [RFC2046] of a potentially retrieved
+ representation, even though such a retrieval is only performed if the
+ URI is dereferenced. If no such representation exists, then the
+ semantics of the fragment are considered unknown and are effectively
+ unconstrained. Fragment identifier semantics are independent of the
+ URI scheme and thus cannot be redefined by scheme specifications.
+
+ Individual media types may define their own restrictions on or
+ structures within the fragment identifier syntax for specifying
+ different types of subsets, views, or external references that are
+ identifiable as secondary resources by that media type. If the
+ primary resource has multiple representations, as is often the case
+ for resources whose representation is selected based on attributes of
+ the retrieval request (a.k.a., content negotiation), then whatever is
+ identified by the fragment should be consistent across all of those
+ representations. Each representation should either define the
+ fragment so that it corresponds to the same secondary resource,
+ regardless of how it is represented, or should leave the fragment
+ undefined (i.e., not found).
+
+
+
+Berners-Lee, et al. Standards Track [Page 24]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ As with any URI, use of a fragment identifier component does not
+ imply that a retrieval action will take place. A URI with a fragment
+ identifier may be used to refer to the secondary resource without any
+ implication that the primary resource is accessible or will ever be
+ accessed.
+
+ Fragment identifiers have a special role in information retrieval
+ systems as the primary form of client-side indirect referencing,
+ allowing an author to specifically identify aspects of an existing
+ resource that are only indirectly provided by the resource owner. As
+ such, the fragment identifier is not used in the scheme-specific
+ processing of a URI; instead, the fragment identifier is separated
+ from the rest of the URI prior to a dereference, and thus the
+ identifying information within the fragment itself is dereferenced
+ solely by the user agent, regardless of the URI scheme. Although
+ this separate handling is often perceived to be a loss of
+ information, particularly for accurate redirection of references as
+ resources move over time, it also serves to prevent information
+ providers from denying reference authors the right to refer to
+ information within a resource selectively. Indirect referencing also
+ provides additional flexibility and extensibility to systems that use
+ URIs, as new media types are easier to define and deploy than new
+ schemes of identification.
+
+ The characters slash ("/") and question mark ("?") are allowed to
+ represent data within the fragment identifier. Beware that some
+ older, erroneous implementations may not handle this data correctly
+ when it is used as the base URI for relative references (Section
+ 5.1).
+
+4. Usage
+
+ When applications make reference to a URI, they do not always use the
+ full form of reference defined by the "URI" syntax rule. To save
+ space and take advantage of hierarchical locality, many Internet
+ protocol elements and media type formats allow an abbreviation of a
+ URI, whereas others restrict the syntax to a particular form of URI.
+ We define the most common forms of reference syntax in this
+ specification because they impact and depend upon the design of the
+ generic syntax, requiring a uniform parsing algorithm in order to be
+ interpreted consistently.
+
+4.1. URI Reference
+
+ URI-reference is used to denote the most common usage of a resource
+ identifier.
+
+ URI-reference = URI / relative-ref
+
+
+
+Berners-Lee, et al. Standards Track [Page 25]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ A URI-reference is either a URI or a relative reference. If the
+ URI-reference's prefix does not match the syntax of a scheme followed
+ by its colon separator, then the URI-reference is a relative
+ reference.
+
+ A URI-reference is typically parsed first into the five URI
+ components, in order to determine what components are present and
+ whether the reference is relative. Then, each component is parsed
+ for its subparts and their validation. The ABNF of URI-reference,
+ along with the "first-match-wins" disambiguation rule, is sufficient
+ to define a validating parser for the generic syntax. Readers
+ familiar with regular expressions should see Appendix B for an
+ example of a non-validating URI-reference parser that will take any
+ given string and extract the URI components.
+
+4.2. Relative Reference
+
+ A relative reference takes advantage of the hierarchical syntax
+ (Section 1.2.3) to express a URI reference relative to the name space
+ of another hierarchical URI.
+
+ relative-ref = relative-part [ "?" query ] [ "#" fragment ]
+
+ relative-part = "//" authority path-abempty
+ / path-absolute
+ / path-noscheme
+ / path-empty
+
+ The URI referred to by a relative reference, also known as the target
+ URI, is obtained by applying the reference resolution algorithm of
+ Section 5.
+
+ A relative reference that begins with two slash characters is termed
+ a network-path reference; such references are rarely used. A
+ relative reference that begins with a single slash character is
+ termed an absolute-path reference. A relative reference that does
+ not begin with a slash character is termed a relative-path reference.
+
+ A path segment that contains a colon character (e.g., "this:that")
+ cannot be used as the first segment of a relative-path reference, as
+ it would be mistaken for a scheme name. Such a segment must be
+ preceded by a dot-segment (e.g., "./this:that") to make a relative-
+ path reference.
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 26]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+4.3. Absolute URI
+
+ Some protocol elements allow only the absolute form of a URI without
+ a fragment identifier. For example, defining a base URI for later
+ use by relative references calls for an absolute-URI syntax rule that
+ does not allow a fragment.
+
+ absolute-URI = scheme ":" hier-part [ "?" query ]
+
+ URI scheme specifications must define their own syntax so that all
+ strings matching their scheme-specific syntax will also match the
+ <absolute-URI> grammar. Scheme specifications will not define
+ fragment identifier syntax or usage, regardless of its applicability
+ to resources identifiable via that scheme, as fragment identification
+ is orthogonal to scheme definition. However, scheme specifications
+ are encouraged to include a wide range of examples, including
+ examples that show use of the scheme's URIs with fragment identifiers
+ when such usage is appropriate.
+
+4.4. Same-Document Reference
+
+ When a URI reference refers to a URI that is, aside from its fragment
+ component (if any), identical to the base URI (Section 5.1), that
+ reference is called a "same-document" reference. The most frequent
+ examples of same-document references are relative references that are
+ empty or include only the number sign ("#") separator followed by a
+ fragment identifier.
+
+ When a same-document reference is dereferenced for a retrieval
+ action, the target of that reference is defined to be within the same
+ entity (representation, document, or message) as the reference;
+ therefore, a dereference should not result in a new retrieval action.
+
+ Normalization of the base and target URIs prior to their comparison,
+ as described in Sections 6.2.2 and 6.2.3, is allowed but rarely
+ performed in practice. Normalization may increase the set of same-
+ document references, which may be of benefit to some caching
+ applications. As such, reference authors should not assume that a
+ slightly different, though equivalent, reference URI will (or will
+ not) be interpreted as a same-document reference by any given
+ application.
+
+4.5. Suffix Reference
+
+ The URI syntax is designed for unambiguous reference to resources and
+ extensibility via the URI scheme. However, as URI identification and
+ usage have become commonplace, traditional media (television, radio,
+ newspapers, billboards, etc.) have increasingly used a suffix of the
+
+
+
+Berners-Lee, et al. Standards Track [Page 27]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ URI as a reference, consisting of only the authority and path
+ portions of the URI, such as
+
+ www.w3.org/Addressing/
+
+ or simply a DNS registered name on its own. Such references are
+ primarily intended for human interpretation rather than for machines,
+ with the assumption that context-based heuristics are sufficient to
+ complete the URI (e.g., most registered names beginning with "www"
+ are likely to have a URI prefix of "http://"). Although there is no
+ standard set of heuristics for disambiguating a URI suffix, many
+ client implementations allow them to be entered by the user and
+ heuristically resolved.
+
+ Although this practice of using suffix references is common, it
+ should be avoided whenever possible and should never be used in
+ situations where long-term references are expected. The heuristics
+ noted above will change over time, particularly when a new URI scheme
+ becomes popular, and are often incorrect when used out of context.
+ Furthermore, they can lead to security issues along the lines of
+ those described in [RFC1535].
+
+ As a URI suffix has the same syntax as a relative-path reference, a
+ suffix reference cannot be used in contexts where a relative
+ reference is expected. As a result, suffix references are limited to
+ places where there is no defined base URI, such as dialog boxes and
+ off-line advertisements.
+
+5. Reference Resolution
+
+ This section defines the process of resolving a URI reference within
+ a context that allows relative references so that the result is a
+ string matching the <URI> syntax rule of Section 3.
+
+5.1. Establishing a Base URI
+
+ The term "relative" implies that a "base URI" exists against which
+ the relative reference is applied. Aside from fragment-only
+ references (Section 4.4), relative references are only usable when a
+ base URI is known. A base URI must be established by the parser
+ prior to parsing URI references that might be relative. A base URI
+ must conform to the <absolute-URI> syntax rule (Section 4.3). If the
+ base URI is obtained from a URI reference, then that reference must
+ be converted to absolute form and stripped of any fragment component
+ prior to its use as a base URI.
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 28]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ The base URI of a reference can be established in one of four ways,
+ discussed below in order of precedence. The order of precedence can
+ be thought of in terms of layers, where the innermost defined base
+ URI has the highest precedence. This can be visualized graphically
+ as follows:
+
+ .----------------------------------------------------------.
+ | .----------------------------------------------------. |
+ | | .----------------------------------------------. | |
+ | | | .----------------------------------------. | | |
+ | | | | .----------------------------------. | | | |
+ | | | | | <relative-reference> | | | | |
+ | | | | `----------------------------------' | | | |
+ | | | | (5.1.1) Base URI embedded in content | | | |
+ | | | `----------------------------------------' | | |
+ | | | (5.1.2) Base URI of the encapsulating entity | | |
+ | | | (message, representation, or none) | | |
+ | | `----------------------------------------------' | |
+ | | (5.1.3) URI used to retrieve the entity | |
+ | `----------------------------------------------------' |
+ | (5.1.4) Default Base URI (application-dependent) |
+ `----------------------------------------------------------'
+
+5.1.1. Base URI Embedded in Content
+
+ Within certain media types, a base URI for relative references can be
+ embedded within the content itself so that it can be readily obtained
+ by a parser. This can be useful for descriptive documents, such as
+ tables of contents, which may be transmitted to others through
+ protocols other than their usual retrieval context (e.g., email or
+ USENET news).
+
+ It is beyond the scope of this specification to specify how, for each
+ media type, a base URI can be embedded. The appropriate syntax, when
+ available, is described by the data format specification associated
+ with each media type.
+
+5.1.2. Base URI from the Encapsulating Entity
+
+ If no base URI is embedded, the base URI is defined by the
+ representation's retrieval context. For a document that is enclosed
+ within another entity, such as a message or archive, the retrieval
+ context is that entity. Thus, the default base URI of a
+ representation is the base URI of the entity in which the
+ representation is encapsulated.
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 29]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ A mechanism for embedding a base URI within MIME container types
+ (e.g., the message and multipart types) is defined by MHTML
+ [RFC2557]. Protocols that do not use the MIME message header syntax,
+ but that do allow some form of tagged metadata to be included within
+ messages, may define their own syntax for defining a base URI as part
+ of a message.
+
+5.1.3. Base URI from the Retrieval URI
+
+ If no base URI is embedded and the representation is not encapsulated
+ within some other entity, then, if a URI was used to retrieve the
+ representation, that URI shall be considered the base URI. Note that
+ if the retrieval was the result of a redirected request, the last URI
+ used (i.e., the URI that resulted in the actual retrieval of the
+ representation) is the base URI.
+
+5.1.4. Default Base URI
+
+ If none of the conditions described above apply, then the base URI is
+ defined by the context of the application. As this definition is
+ necessarily application-dependent, failing to define a base URI by
+ using one of the other methods may result in the same content being
+ interpreted differently by different types of applications.
+
+ A sender of a representation containing relative references is
+ responsible for ensuring that a base URI for those references can be
+ established. Aside from fragment-only references, relative
+ references can only be used reliably in situations where the base URI
+ is well defined.
+
+5.2. Relative Resolution
+
+ This section describes an algorithm for converting a URI reference
+ that might be relative to a given base URI into the parsed components
+ of the reference's target. The components can then be recomposed, as
+ described in Section 5.3, to form the target URI. This algorithm
+ provides definitive results that can be used to test the output of
+ other implementations. Applications may implement relative reference
+ resolution by using some other algorithm, provided that the results
+ match what would be given by this one.
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 30]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+5.2.1. Pre-parse the Base URI
+
+ The base URI (Base) is established according to the procedure of
+ Section 5.1 and parsed into the five main components described in
+ Section 3. Note that only the scheme component is required to be
+ present in a base URI; the other components may be empty or
+ undefined. A component is undefined if its associated delimiter does
+ not appear in the URI reference; the path component is never
+ undefined, though it may be empty.
+
+ Normalization of the base URI, as described in Sections 6.2.2 and
+ 6.2.3, is optional. A URI reference must be transformed to its
+ target URI before it can be normalized.
+
+5.2.2. Transform References
+
+ For each URI reference (R), the following pseudocode describes an
+ algorithm for transforming R into its target URI (T):
+
+ -- The URI reference is parsed into the five URI components
+ --
+ (R.scheme, R.authority, R.path, R.query, R.fragment) = parse(R);
+
+ -- A non-strict parser may ignore a scheme in the reference
+ -- if it is identical to the base URI's scheme.
+ --
+ if ((not strict) and (R.scheme == Base.scheme)) then
+ undefine(R.scheme);
+ endif;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 31]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ if defined(R.scheme) then
+ T.scheme = R.scheme;
+ T.authority = R.authority;
+ T.path = remove_dot_segments(R.path);
+ T.query = R.query;
+ else
+ if defined(R.authority) then
+ T.authority = R.authority;
+ T.path = remove_dot_segments(R.path);
+ T.query = R.query;
+ else
+ if (R.path == "") then
+ T.path = Base.path;
+ if defined(R.query) then
+ T.query = R.query;
+ else
+ T.query = Base.query;
+ endif;
+ else
+ if (R.path starts-with "/") then
+ T.path = remove_dot_segments(R.path);
+ else
+ T.path = merge(Base.path, R.path);
+ T.path = remove_dot_segments(T.path);
+ endif;
+ T.query = R.query;
+ endif;
+ T.authority = Base.authority;
+ endif;
+ T.scheme = Base.scheme;
+ endif;
+
+ T.fragment = R.fragment;
+
+5.2.3. Merge Paths
+
+ The pseudocode above refers to a "merge" routine for merging a
+ relative-path reference with the path of the base URI. This is
+ accomplished as follows:
+
+ o If the base URI has a defined authority component and an empty
+ path, then return a string consisting of "/" concatenated with the
+ reference's path; otherwise,
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 32]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ o return a string consisting of the reference's path component
+ appended to all but the last segment of the base URI's path (i.e.,
+ excluding any characters after the right-most "/" in the base URI
+ path, or excluding the entire base URI path if it does not contain
+ any "/" characters).
+
+5.2.4. Remove Dot Segments
+
+ The pseudocode also refers to a "remove_dot_segments" routine for
+ interpreting and removing the special "." and ".." complete path
+ segments from a referenced path. This is done after the path is
+ extracted from a reference, whether or not the path was relative, in
+ order to remove any invalid or extraneous dot-segments prior to
+ forming the target URI. Although there are many ways to accomplish
+ this removal process, we describe a simple method using two string
+ buffers.
+
+ 1. The input buffer is initialized with the now-appended path
+ components and the output buffer is initialized to the empty
+ string.
+
+ 2. While the input buffer is not empty, loop as follows:
+
+ A. If the input buffer begins with a prefix of "../" or "./",
+ then remove that prefix from the input buffer; otherwise,
+
+ B. if the input buffer begins with a prefix of "/./" or "/.",
+ where "." is a complete path segment, then replace that
+ prefix with "/" in the input buffer; otherwise,
+
+ C. if the input buffer begins with a prefix of "/../" or "/..",
+ where ".." is a complete path segment, then replace that
+ prefix with "/" in the input buffer and remove the last
+ segment and its preceding "/" (if any) from the output
+ buffer; otherwise,
+
+ D. if the input buffer consists only of "." or "..", then remove
+ that from the input buffer; otherwise,
+
+ E. move the first path segment in the input buffer to the end of
+ the output buffer, including the initial "/" character (if
+ any) and any subsequent characters up to, but not including,
+ the next "/" character or the end of the input buffer.
+
+ 3. Finally, the output buffer is returned as the result of
+ remove_dot_segments.
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 33]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ Note that dot-segments are intended for use in URI references to
+ express an identifier relative to the hierarchy of names in the base
+ URI. The remove_dot_segments algorithm respects that hierarchy by
+ removing extra dot-segments rather than treat them as an error or
+ leaving them to be misinterpreted by dereference implementations.
+
+ The following illustrates how the above steps are applied for two
+ examples of merged paths, showing the state of the two buffers after
+ each step.
+
+ STEP OUTPUT BUFFER INPUT BUFFER
+
+ 1 : /a/b/c/./../../g
+ 2E: /a /b/c/./../../g
+ 2E: /a/b /c/./../../g
+ 2E: /a/b/c /./../../g
+ 2B: /a/b/c /../../g
+ 2C: /a/b /../g
+ 2C: /a /g
+ 2E: /a/g
+
+ STEP OUTPUT BUFFER INPUT BUFFER
+
+ 1 : mid/content=5/../6
+ 2E: mid /content=5/../6
+ 2E: mid/content=5 /../6
+ 2C: mid /6
+ 2E: mid/6
+
+ Some applications may find it more efficient to implement the
+ remove_dot_segments algorithm by using two segment stacks rather than
+ strings.
+
+ Note: Beware that some older, erroneous implementations will fail
+ to separate a reference's query component from its path component
+ prior to merging the base and reference paths, resulting in an
+ interoperability failure if the query component contains the
+ strings "/../" or "/./".
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 34]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+5.3. Component Recomposition
+
+ Parsed URI components can be recomposed to obtain the corresponding
+ URI reference string. Using pseudocode, this would be:
+
+ result = ""
+
+ if defined(scheme) then
+ append scheme to result;
+ append ":" to result;
+ endif;
+
+ if defined(authority) then
+ append "//" to result;
+ append authority to result;
+ endif;
+
+ append path to result;
+
+ if defined(query) then
+ append "?" to result;
+ append query to result;
+ endif;
+
+ if defined(fragment) then
+ append "#" to result;
+ append fragment to result;
+ endif;
+
+ return result;
+
+ Note that we are careful to preserve the distinction between a
+ component that is undefined, meaning that its separator was not
+ present in the reference, and a component that is empty, meaning that
+ the separator was present and was immediately followed by the next
+ component separator or the end of the reference.
+
+5.4. Reference Resolution Examples
+
+ Within a representation with a well defined base URI of
+
+ http://a/b/c/d;p?q
+
+ a relative reference is transformed to its target URI as follows.
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 35]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+5.4.1. Normal Examples
+
+ "g:h" = "g:h"
+ "g" = "http://a/b/c/g"
+ "./g" = "http://a/b/c/g"
+ "g/" = "http://a/b/c/g/"
+ "/g" = "http://a/g"
+ "//g" = "http://g"
+ "?y" = "http://a/b/c/d;p?y"
+ "g?y" = "http://a/b/c/g?y"
+ "#s" = "http://a/b/c/d;p?q#s"
+ "g#s" = "http://a/b/c/g#s"
+ "g?y#s" = "http://a/b/c/g?y#s"
+ ";x" = "http://a/b/c/;x"
+ "g;x" = "http://a/b/c/g;x"
+ "g;x?y#s" = "http://a/b/c/g;x?y#s"
+ "" = "http://a/b/c/d;p?q"
+ "." = "http://a/b/c/"
+ "./" = "http://a/b/c/"
+ ".." = "http://a/b/"
+ "../" = "http://a/b/"
+ "../g" = "http://a/b/g"
+ "../.." = "http://a/"
+ "../../" = "http://a/"
+ "../../g" = "http://a/g"
+
+5.4.2. Abnormal Examples
+
+ Although the following abnormal examples are unlikely to occur in
+ normal practice, all URI parsers should be capable of resolving them
+ consistently. Each example uses the same base as that above.
+
+ Parsers must be careful in handling cases where there are more ".."
+ segments in a relative-path reference than there are hierarchical
+ levels in the base URI's path. Note that the ".." syntax cannot be
+ used to change the authority component of a URI.
+
+ "../../../g" = "http://a/g"
+ "../../../../g" = "http://a/g"
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 36]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ Similarly, parsers must remove the dot-segments "." and ".." when
+ they are complete components of a path, but not when they are only
+ part of a segment.
+
+ "/./g" = "http://a/g"
+ "/../g" = "http://a/g"
+ "g." = "http://a/b/c/g."
+ ".g" = "http://a/b/c/.g"
+ "g.." = "http://a/b/c/g.."
+ "..g" = "http://a/b/c/..g"
+
+ Less likely are cases where the relative reference uses unnecessary
+ or nonsensical forms of the "." and ".." complete path segments.
+
+ "./../g" = "http://a/b/g"
+ "./g/." = "http://a/b/c/g/"
+ "g/./h" = "http://a/b/c/g/h"
+ "g/../h" = "http://a/b/c/h"
+ "g;x=1/./y" = "http://a/b/c/g;x=1/y"
+ "g;x=1/../y" = "http://a/b/c/y"
+
+ Some applications fail to separate the reference's query and/or
+ fragment components from the path component before merging it with
+ the base path and removing dot-segments. This error is rarely
+ noticed, as typical usage of a fragment never includes the hierarchy
+ ("/") character and the query component is not normally used within
+ relative references.
+
+ "g?y/./x" = "http://a/b/c/g?y/./x"
+ "g?y/../x" = "http://a/b/c/g?y/../x"
+ "g#s/./x" = "http://a/b/c/g#s/./x"
+ "g#s/../x" = "http://a/b/c/g#s/../x"
+
+ Some parsers allow the scheme name to be present in a relative
+ reference if it is the same as the base URI scheme. This is
+ considered to be a loophole in prior specifications of partial URI
+ [RFC1630]. Its use should be avoided but is allowed for backward
+ compatibility.
+
+ "http:g" = "http:g" ; for strict parsers
+ / "http://a/b/c/g" ; for backward compatibility
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 37]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+6. Normalization and Comparison
+
+ One of the most common operations on URIs is simple comparison:
+ determining whether two URIs are equivalent without using the URIs to
+ access their respective resource(s). A comparison is performed every
+ time a response cache is accessed, a browser checks its history to
+ color a link, or an XML parser processes tags within a namespace.
+ Extensive normalization prior to comparison of URIs is often used by
+ spiders and indexing engines to prune a search space or to reduce
+ duplication of request actions and response storage.
+
+ URI comparison is performed for some particular purpose. Protocols
+ or implementations that compare URIs for different purposes will
+ often be subject to differing design trade-offs in regards to how
+ much effort should be spent in reducing aliased identifiers. This
+ section describes various methods that may be used to compare URIs,
+ the trade-offs between them, and the types of applications that might
+ use them.
+
+6.1. Equivalence
+
+ Because URIs exist to identify resources, presumably they should be
+ considered equivalent when they identify the same resource. However,
+ this definition of equivalence is not of much practical use, as there
+ is no way for an implementation to compare two resources unless it
+ has full knowledge or control of them. For this reason,
+ determination of equivalence or difference of URIs is based on string
+ comparison, perhaps augmented by reference to additional rules
+ provided by URI scheme definitions. We use the terms "different" and
+ "equivalent" to describe the possible outcomes of such comparisons,
+ but there are many application-dependent versions of equivalence.
+
+ Even though it is possible to determine that two URIs are equivalent,
+ URI comparison is not sufficient to determine whether two URIs
+ identify different resources. For example, an owner of two different
+ domain names could decide to serve the same resource from both,
+ resulting in two different URIs. Therefore, comparison methods are
+ designed to minimize false negatives while strictly avoiding false
+ positives.
+
+ In testing for equivalence, applications should not directly compare
+ relative references; the references should be converted to their
+ respective target URIs before comparison. When URIs are compared to
+ select (or avoid) a network action, such as retrieval of a
+ representation, fragment components (if any) should be excluded from
+ the comparison.
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 38]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+6.2. Comparison Ladder
+
+ A variety of methods are used in practice to test URI equivalence.
+ These methods fall into a range, distinguished by the amount of
+ processing required and the degree to which the probability of false
+ negatives is reduced. As noted above, false negatives cannot be
+ eliminated. In practice, their probability can be reduced, but this
+ reduction requires more processing and is not cost-effective for all
+ applications.
+
+ If this range of comparison practices is considered as a ladder, the
+ following discussion will climb the ladder, starting with practices
+ that are cheap but have a relatively higher chance of producing false
+ negatives, and proceeding to those that have higher computational
+ cost and lower risk of false negatives.
+
+6.2.1. Simple String Comparison
+
+ If two URIs, when considered as character strings, are identical,
+ then it is safe to conclude that they are equivalent. This type of
+ equivalence test has very low computational cost and is in wide use
+ in a variety of applications, particularly in the domain of parsing.
+
+ Testing strings for equivalence requires some basic precautions.
+ This procedure is often referred to as "bit-for-bit" or
+ "byte-for-byte" comparison, which is potentially misleading. Testing
+ strings for equality is normally based on pair comparison of the
+ characters that make up the strings, starting from the first and
+ proceeding until both strings are exhausted and all characters are
+ found to be equal, until a pair of characters compares unequal, or
+ until one of the strings is exhausted before the other.
+
+ This character comparison requires that each pair of characters be
+ put in comparable form. For example, should one URI be stored in a
+ byte array in EBCDIC encoding and the second in a Java String object
+ (UTF-16), bit-for-bit comparisons applied naively will produce
+ errors. It is better to speak of equality on a character-for-
+ character basis rather than on a byte-for-byte or bit-for-bit basis.
+ In practical terms, character-by-character comparisons should be done
+ codepoint-by-codepoint after conversion to a common character
+ encoding.
+
+ False negatives are caused by the production and use of URI aliases.
+ Unnecessary aliases can be reduced, regardless of the comparison
+ method, by consistently providing URI references in an already-
+ normalized form (i.e., a form identical to what would be produced
+ after normalization is applied, as described below).
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 39]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ Protocols and data formats often limit some URI comparisons to simple
+ string comparison, based on the theory that people and
+ implementations will, in their own best interest, be consistent in
+ providing URI references, or at least consistent enough to negate any
+ efficiency that might be obtained from further normalization.
+
+6.2.2. Syntax-Based Normalization
+
+ Implementations may use logic based on the definitions provided by
+ this specification to reduce the probability of false negatives.
+ This processing is moderately higher in cost than character-for-
+ character string comparison. For example, an application using this
+ approach could reasonably consider the following two URIs equivalent:
+
+ example://a/b/c/%7Bfoo%7D
+ eXAMPLE://a/./b/../b/%63/%7bfoo%7d
+
+ Web user agents, such as browsers, typically apply this type of URI
+ normalization when determining whether a cached response is
+ available. Syntax-based normalization includes such techniques as
+ case normalization, percent-encoding normalization, and removal of
+ dot-segments.
+
+6.2.2.1. Case Normalization
+
+ For all URIs, the hexadecimal digits within a percent-encoding
+ triplet (e.g., "%3a" versus "%3A") are case-insensitive and therefore
+ should be normalized to use uppercase letters for the digits A-F.
+
+ When a URI uses components of the generic syntax, the component
+ syntax equivalence rules always apply; namely, that the scheme and
+ host are case-insensitive and therefore should be normalized to
+ lowercase. For example, the URI <HTTP://www.EXAMPLE.com/> is
+ equivalent to <http://www.example.com/>. The other generic syntax
+ components are assumed to be case-sensitive unless specifically
+ defined otherwise by the scheme (see Section 6.2.3).
+
+6.2.2.2. Percent-Encoding Normalization
+
+ The percent-encoding mechanism (Section 2.1) is a frequent source of
+ variance among otherwise identical URIs. In addition to the case
+ normalization issue noted above, some URI producers percent-encode
+ octets that do not require percent-encoding, resulting in URIs that
+ are equivalent to their non-encoded counterparts. These URIs should
+ be normalized by decoding any percent-encoded octet that corresponds
+ to an unreserved character, as described in Section 2.3.
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 40]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+6.2.2.3. Path Segment Normalization
+
+ The complete path segments "." and ".." are intended only for use
+ within relative references (Section 4.1) and are removed as part of
+ the reference resolution process (Section 5.2). However, some
+ deployed implementations incorrectly assume that reference resolution
+ is not necessary when the reference is already a URI and thus fail to
+ remove dot-segments when they occur in non-relative paths. URI
+ normalizers should remove dot-segments by applying the
+ remove_dot_segments algorithm to the path, as described in
+ Section 5.2.4.
+
+6.2.3. Scheme-Based Normalization
+
+ The syntax and semantics of URIs vary from scheme to scheme, as
+ described by the defining specification for each scheme.
+ Implementations may use scheme-specific rules, at further processing
+ cost, to reduce the probability of false negatives. For example,
+ because the "http" scheme makes use of an authority component, has a
+ default port of "80", and defines an empty path to be equivalent to
+ "/", the following four URIs are equivalent:
+
+ http://example.com
+ http://example.com/
+ http://example.com:/
+ http://example.com:80/
+
+ In general, a URI that uses the generic syntax for authority with an
+ empty path should be normalized to a path of "/". Likewise, an
+ explicit ":port", for which the port is empty or the default for the
+ scheme, is equivalent to one where the port and its ":" delimiter are
+ elided and thus should be removed by scheme-based normalization. For
+ example, the second URI above is the normal form for the "http"
+ scheme.
+
+ Another case where normalization varies by scheme is in the handling
+ of an empty authority component or empty host subcomponent. For many
+ scheme specifications, an empty authority or host is considered an
+ error; for others, it is considered equivalent to "localhost" or the
+ end-user's host. When a scheme defines a default for authority and a
+ URI reference to that default is desired, the reference should be
+ normalized to an empty authority for the sake of uniformity, brevity,
+ and internationalization. If, however, either the userinfo or port
+ subcomponents are non-empty, then the host should be given explicitly
+ even if it matches the default.
+
+ Normalization should not remove delimiters when their associated
+ component is empty unless licensed to do so by the scheme
+
+
+
+Berners-Lee, et al. Standards Track [Page 41]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ specification. For example, the URI "http://example.com/?" cannot be
+ assumed to be equivalent to any of the examples above. Likewise, the
+ presence or absence of delimiters within a userinfo subcomponent is
+ usually significant to its interpretation. The fragment component is
+ not subject to any scheme-based normalization; thus, two URIs that
+ differ only by the suffix "#" are considered different regardless of
+ the scheme.
+
+ Some schemes define additional subcomponents that consist of case-
+ insensitive data, giving an implicit license to normalizers to
+ convert this data to a common case (e.g., all lowercase). For
+ example, URI schemes that define a subcomponent of path to contain an
+ Internet hostname, such as the "mailto" URI scheme, cause that
+ subcomponent to be case-insensitive and thus subject to case
+ normalization (e.g., "mailto:Joe@Example.COM" is equivalent to
+ "mailto:Joe@example.com", even though the generic syntax considers
+ the path component to be case-sensitive).
+
+ Other scheme-specific normalizations are possible.
+
+6.2.4. Protocol-Based Normalization
+
+ Substantial effort to reduce the incidence of false negatives is
+ often cost-effective for web spiders. Therefore, they implement even
+ more aggressive techniques in URI comparison. For example, if they
+ observe that a URI such as
+
+ http://example.com/data
+
+ redirects to a URI differing only in the trailing slash
+
+ http://example.com/data/
+
+ they will likely regard the two as equivalent in the future. This
+ kind of technique is only appropriate when equivalence is clearly
+ indicated by both the result of accessing the resources and the
+ common conventions of their scheme's dereference algorithm (in this
+ case, use of redirection by HTTP origin servers to avoid problems
+ with relative references).
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 42]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+7. Security Considerations
+
+ A URI does not in itself pose a security threat. However, as URIs
+ are often used to provide a compact set of instructions for access to
+ network resources, care must be taken to properly interpret the data
+ within a URI, to prevent that data from causing unintended access,
+ and to avoid including data that should not be revealed in plain
+ text.
+
+7.1. Reliability and Consistency
+
+ There is no guarantee that once a URI has been used to retrieve
+ information, the same information will be retrievable by that URI in
+ the future. Nor is there any guarantee that the information
+ retrievable via that URI in the future will be observably similar to
+ that retrieved in the past. The URI syntax does not constrain how a
+ given scheme or authority apportions its namespace or maintains it
+ over time. Such guarantees can only be obtained from the person(s)
+ controlling that namespace and the resource in question. A specific
+ URI scheme may define additional semantics, such as name persistence,
+ if those semantics are required of all naming authorities for that
+ scheme.
+
+7.2. Malicious Construction
+
+ It is sometimes possible to construct a URI so that an attempt to
+ perform a seemingly harmless, idempotent operation, such as the
+ retrieval of a representation, will in fact cause a possibly damaging
+ remote operation. The unsafe URI is typically constructed by
+ specifying a port number other than that reserved for the network
+ protocol in question. The client unwittingly contacts a site running
+ a different protocol service, and data within the URI contains
+ instructions that, when interpreted according to this other protocol,
+ cause an unexpected operation. A frequent example of such abuse has
+ been the use of a protocol-based scheme with a port component of
+ "25", thereby fooling user agent software into sending an unintended
+ or impersonating message via an SMTP server.
+
+ Applications should prevent dereference of a URI that specifies a TCP
+ port number within the "well-known port" range (0 - 1023) unless the
+ protocol being used to dereference that URI is compatible with the
+ protocol expected on that well-known port. Although IANA maintains a
+ registry of well-known ports, applications should make such
+ restrictions user-configurable to avoid preventing the deployment of
+ new services.
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 43]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ When a URI contains percent-encoded octets that match the delimiters
+ for a given resolution or dereference protocol (for example, CR and
+ LF characters for the TELNET protocol), these percent-encodings must
+ not be decoded before transmission across that protocol. Transfer of
+ the percent-encoding, which might violate the protocol, is less
+ harmful than allowing decoded octets to be interpreted as additional
+ operations or parameters, perhaps triggering an unexpected and
+ possibly harmful remote operation.
+
+7.3. Back-End Transcoding
+
+ When a URI is dereferenced, the data within it is often parsed by
+ both the user agent and one or more servers. In HTTP, for example, a
+ typical user agent will parse a URI into its five major components,
+ access the authority's server, and send it the data within the
+ authority, path, and query components. A typical server will take
+ that information, parse the path into segments and the query into
+ key/value pairs, and then invoke implementation-specific handlers to
+ respond to the request. As a result, a common security concern for
+ server implementations that handle a URI, either as a whole or split
+ into separate components, is proper interpretation of the octet data
+ represented by the characters and percent-encodings within that URI.
+
+ Percent-encoded octets must be decoded at some point during the
+ dereference process. Applications must split the URI into its
+ components and subcomponents prior to decoding the octets, as
+ otherwise the decoded octets might be mistaken for delimiters.
+ Security checks of the data within a URI should be applied after
+ decoding the octets. Note, however, that the "%00" percent-encoding
+ (NUL) may require special handling and should be rejected if the
+ application is not expecting to receive raw data within a component.
+
+ Special care should be taken when the URI path interpretation process
+ involves the use of a back-end file system or related system
+ functions. File systems typically assign an operational meaning to
+ special characters, such as the "/", "\", ":", "[", and "]"
+ characters, and to special device names like ".", "..", "...", "aux",
+ "lpt", etc. In some cases, merely testing for the existence of such
+ a name will cause the operating system to pause or invoke unrelated
+ system calls, leading to significant security concerns regarding
+ denial of service and unintended data transfer. It would be
+ impossible for this specification to list all such significant
+ characters and device names. Implementers should research the
+ reserved names and characters for the types of storage device that
+ may be attached to their applications and restrict the use of data
+ obtained from URI components accordingly.
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 44]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+7.4. Rare IP Address Formats
+
+ Although the URI syntax for IPv4address only allows the common
+ dotted-decimal form of IPv4 address literal, many implementations
+ that process URIs make use of platform-dependent system routines,
+ such as gethostbyname() and inet_aton(), to translate the string
+ literal to an actual IP address. Unfortunately, such system routines
+ often allow and process a much larger set of formats than those
+ described in Section 3.2.2.
+
+ For example, many implementations allow dotted forms of three
+ numbers, wherein the last part is interpreted as a 16-bit quantity
+ and placed in the right-most two bytes of the network address (e.g.,
+ a Class B network). Likewise, a dotted form of two numbers means
+ that the last part is interpreted as a 24-bit quantity and placed in
+ the right-most three bytes of the network address (Class A), and a
+ single number (without dots) is interpreted as a 32-bit quantity and
+ stored directly in the network address. Adding further to the
+ confusion, some implementations allow each dotted part to be
+ interpreted as decimal, octal, or hexadecimal, as specified in the C
+ language (i.e., a leading 0x or 0X implies hexadecimal; a leading 0
+ implies octal; otherwise, the number is interpreted as decimal).
+
+ These additional IP address formats are not allowed in the URI syntax
+ due to differences between platform implementations. However, they
+ can become a security concern if an application attempts to filter
+ access to resources based on the IP address in string literal format.
+ If this filtering is performed, literals should be converted to
+ numeric form and filtered based on the numeric value, and not on a
+ prefix or suffix of the string form.
+
+7.5. Sensitive Information
+
+ URI producers should not provide a URI that contains a username or
+ password that is intended to be secret. URIs are frequently
+ displayed by browsers, stored in clear text bookmarks, and logged by
+ user agent history and intermediary applications (proxies). A
+ password appearing within the userinfo component is deprecated and
+ should be considered an error (or simply ignored) except in those
+ rare cases where the 'password' parameter is intended to be public.
+
+7.6. Semantic Attacks
+
+ Because the userinfo subcomponent is rarely used and appears before
+ the host in the authority component, it can be used to construct a
+ URI intended to mislead a human user by appearing to identify one
+ (trusted) naming authority while actually identifying a different
+ authority hidden behind the noise. For example
+
+
+
+Berners-Lee, et al. Standards Track [Page 45]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm
+
+ might lead a human user to assume that the host is 'cnn.example.com',
+ whereas it is actually '10.0.0.1'. Note that a misleading userinfo
+ subcomponent could be much longer than the example above.
+
+ A misleading URI, such as that above, is an attack on the user's
+ preconceived notions about the meaning of a URI rather than an attack
+ on the software itself. User agents may be able to reduce the impact
+ of such attacks by distinguishing the various components of the URI
+ when they are rendered, such as by using a different color or tone to
+ render userinfo if any is present, though there is no panacea. More
+ information on URI-based semantic attacks can be found in [Siedzik].
+
+8. IANA Considerations
+
+ URI scheme names, as defined by <scheme> in Section 3.1, form a
+ registered namespace that is managed by IANA according to the
+ procedures defined in [BCP35]. No IANA actions are required by this
+ document.
+
+9. Acknowledgements
+
+ This specification is derived from RFC 2396 [RFC2396], RFC 1808
+ [RFC1808], and RFC 1738 [RFC1738]; the acknowledgements in those
+ documents still apply. It also incorporates the update (with
+ corrections) for IPv6 literals in the host syntax, as defined by
+ Robert M. Hinden, Brian E. Carpenter, and Larry Masinter in
+ [RFC2732]. In addition, contributions by Gisle Aas, Reese Anschultz,
+ Daniel Barclay, Tim Bray, Mike Brown, Rob Cameron, Jeremy Carroll,
+ Dan Connolly, Adam M. Costello, John Cowan, Jason Diamond, Martin
+ Duerst, Stefan Eissing, Clive D.W. Feather, Al Gilman, Tony Hammond,
+ Elliotte Harold, Pat Hayes, Henry Holtzman, Ian B. Jacobs, Michael
+ Kay, John C. Klensin, Graham Klyne, Dan Kohn, Bruce Lilly, Andrew
+ Main, Dave McAlpin, Ira McDonald, Michael Mealling, Ray Merkert,
+ Stephen Pollei, Julian Reschke, Tomas Rokicki, Miles Sabin, Kai
+ Schaetzl, Mark Thomson, Ronald Tschalaer, Norm Walsh, Marc Warne,
+ Stuart Williams, and Henry Zongaro are gratefully acknowledged.
+
+10. References
+
+10.1. Normative References
+
+ [ASCII] American National Standards Institute, "Coded Character
+ Set -- 7-bit American Standard Code for Information
+ Interchange", ANSI X3.4, 1986.
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 46]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ [RFC2234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 2234, November 1997.
+
+ [STD63] Yergeau, F., "UTF-8, a transformation format of
+ ISO 10646", STD 63, RFC 3629, November 2003.
+
+ [UCS] International Organization for Standardization,
+ "Information Technology - Universal Multiple-Octet Coded
+ Character Set (UCS)", ISO/IEC 10646:2003, December 2003.
+
+10.2. Informative References
+
+ [BCP19] Freed, N. and J. Postel, "IANA Charset Registration
+ Procedures", BCP 19, RFC 2978, October 2000.
+
+ [BCP35] Petke, R. and I. King, "Registration Procedures for URL
+ Scheme Names", BCP 35, RFC 2717, November 1999.
+
+ [RFC0952] Harrenstien, K., Stahl, M., and E. Feinler, "DoD Internet
+ host table specification", RFC 952, October 1985.
+
+ [RFC1034] Mockapetris, P., "Domain names - concepts and facilities",
+ STD 13, RFC 1034, November 1987.
+
+ [RFC1123] Braden, R., "Requirements for Internet Hosts - Application
+ and Support", STD 3, RFC 1123, October 1989.
+
+ [RFC1535] Gavron, E., "A Security Problem and Proposed Correction
+ With Widely Deployed DNS Software", RFC 1535,
+ October 1993.
+
+ [RFC1630] Berners-Lee, T., "Universal Resource Identifiers in WWW: A
+ Unifying Syntax for the Expression of Names and Addresses
+ of Objects on the Network as used in the World-Wide Web",
+ RFC 1630, June 1994.
+
+ [RFC1736] Kunze, J., "Functional Recommendations for Internet
+ Resource Locators", RFC 1736, February 1995.
+
+ [RFC1737] Sollins, K. and L. Masinter, "Functional Requirements for
+ Uniform Resource Names", RFC 1737, December 1994.
+
+ [RFC1738] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform
+ Resource Locators (URL)", RFC 1738, December 1994.
+
+ [RFC1808] Fielding, R., "Relative Uniform Resource Locators",
+ RFC 1808, June 1995.
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 47]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ [RFC2046] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part Two: Media Types", RFC 2046,
+ November 1996.
+
+ [RFC2141] Moats, R., "URN Syntax", RFC 2141, May 1997.
+
+ [RFC2396] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+ [RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, S., and D.
+ Jensen, "HTTP Extensions for Distributed Authoring --
+ WEBDAV", RFC 2518, February 1999.
+
+ [RFC2557] Palme, J., Hopmann, A., and N. Shelness, "MIME
+ Encapsulation of Aggregate Documents, such as HTML
+ (MHTML)", RFC 2557, March 1999.
+
+ [RFC2718] Masinter, L., Alvestrand, H., Zigmond, D., and R. Petke,
+ "Guidelines for new URL Schemes", RFC 2718, November 1999.
+
+ [RFC2732] Hinden, R., Carpenter, B., and L. Masinter, "Format for
+ Literal IPv6 Addresses in URL's", RFC 2732, December 1999.
+
+ [RFC3305] Mealling, M. and R. Denenberg, "Report from the Joint
+ W3C/IETF URI Planning Interest Group: Uniform Resource
+ Identifiers (URIs), URLs, and Uniform Resource Names
+ (URNs): Clarifications and Recommendations", RFC 3305,
+ August 2002.
+
+ [RFC3490] Faltstrom, P., Hoffman, P., and A. Costello,
+ "Internationalizing Domain Names in Applications (IDNA)",
+ RFC 3490, March 2003.
+
+ [RFC3513] Hinden, R. and S. Deering, "Internet Protocol Version 6
+ (IPv6) Addressing Architecture", RFC 3513, April 2003.
+
+ [Siedzik] Siedzik, R., "Semantic Attacks: What's in a URL?",
+ April 2001, <http://www.giac.org/practical/gsec/
+ Richard_Siedzik_GSEC.pdf>.
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 48]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+Appendix A. Collected ABNF for URI
+
+ URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+
+ hier-part = "//" authority path-abempty
+ / path-absolute
+ / path-rootless
+ / path-empty
+
+ URI-reference = URI / relative-ref
+
+ absolute-URI = scheme ":" hier-part [ "?" query ]
+
+ relative-ref = relative-part [ "?" query ] [ "#" fragment ]
+
+ relative-part = "//" authority path-abempty
+ / path-absolute
+ / path-noscheme
+ / path-empty
+
+ scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+
+ authority = [ userinfo "@" ] host [ ":" port ]
+ userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+ host = IP-literal / IPv4address / reg-name
+ port = *DIGIT
+
+ IP-literal = "[" ( IPv6address / IPvFuture ) "]"
+
+ IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
+
+ IPv6address = 6( h16 ":" ) ls32
+ / "::" 5( h16 ":" ) ls32
+ / [ h16 ] "::" 4( h16 ":" ) ls32
+ / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
+ / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
+ / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
+ / [ *4( h16 ":" ) h16 ] "::" ls32
+ / [ *5( h16 ":" ) h16 ] "::" h16
+ / [ *6( h16 ":" ) h16 ] "::"
+
+ h16 = 1*4HEXDIG
+ ls32 = ( h16 ":" h16 ) / IPv4address
+ IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 49]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ dec-octet = DIGIT ; 0-9
+ / %x31-39 DIGIT ; 10-99
+ / "1" 2DIGIT ; 100-199
+ / "2" %x30-34 DIGIT ; 200-249
+ / "25" %x30-35 ; 250-255
+
+ reg-name = *( unreserved / pct-encoded / sub-delims )
+
+ path = path-abempty ; begins with "/" or is empty
+ / path-absolute ; begins with "/" but not "//"
+ / path-noscheme ; begins with a non-colon segment
+ / path-rootless ; begins with a segment
+ / path-empty ; zero characters
+
+ path-abempty = *( "/" segment )
+ path-absolute = "/" [ segment-nz *( "/" segment ) ]
+ path-noscheme = segment-nz-nc *( "/" segment )
+ path-rootless = segment-nz *( "/" segment )
+ path-empty = 0<pchar>
+
+ segment = *pchar
+ segment-nz = 1*pchar
+ segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
+ ; non-zero-length segment without any colon ":"
+
+ pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+
+ query = *( pchar / "/" / "?" )
+
+ fragment = *( pchar / "/" / "?" )
+
+ pct-encoded = "%" HEXDIG HEXDIG
+
+ unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ reserved = gen-delims / sub-delims
+ gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+ sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ / "*" / "+" / "," / ";" / "="
+
+Appendix B. Parsing a URI Reference with a Regular Expression
+
+ As the "first-match-wins" algorithm is identical to the "greedy"
+ disambiguation method used by POSIX regular expressions, it is
+ natural and commonplace to use a regular expression for parsing the
+ potential five components of a URI reference.
+
+ The following line is the regular expression for breaking-down a
+ well-formed URI reference into its components.
+
+
+
+Berners-Lee, et al. Standards Track [Page 50]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+ 12 3 4 5 6 7 8 9
+
+ The numbers in the second line above are only to assist readability;
+ they indicate the reference points for each subexpression (i.e., each
+ paired parenthesis). We refer to the value matched for subexpression
+ <n> as $<n>. For example, matching the above expression to
+
+ http://www.ics.uci.edu/pub/ietf/uri/#Related
+
+ results in the following subexpression matches:
+
+ $1 = http:
+ $2 = http
+ $3 = //www.ics.uci.edu
+ $4 = www.ics.uci.edu
+ $5 = /pub/ietf/uri/
+ $6 = <undefined>
+ $7 = <undefined>
+ $8 = #Related
+ $9 = Related
+
+ where <undefined> indicates that the component is not present, as is
+ the case for the query component in the above example. Therefore, we
+ can determine the value of the five components as
+
+ scheme = $2
+ authority = $4
+ path = $5
+ query = $7
+ fragment = $9
+
+ Going in the opposite direction, we can recreate a URI reference from
+ its components by using the algorithm of Section 5.3.
+
+Appendix C. Delimiting a URI in Context
+
+ URIs are often transmitted through formats that do not provide a
+ clear context for their interpretation. For example, there are many
+ occasions when a URI is included in plain text; examples include text
+ sent in email, USENET news, and on printed paper. In such cases, it
+ is important to be able to delimit the URI from the rest of the text,
+ and in particular from punctuation marks that might be mistaken for
+ part of the URI.
+
+ In practice, URIs are delimited in a variety of ways, but usually
+ within double-quotes "http://example.com/", angle brackets
+ <http://example.com/>, or just by using whitespace:
+
+
+
+Berners-Lee, et al. Standards Track [Page 51]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ http://example.com/
+
+ These wrappers do not form part of the URI.
+
+ In some cases, extra whitespace (spaces, line-breaks, tabs, etc.) may
+ have to be added to break a long URI across lines. The whitespace
+ should be ignored when the URI is extracted.
+
+ No whitespace should be introduced after a hyphen ("-") character.
+ Because some typesetters and printers may (erroneously) introduce a
+ hyphen at the end of line when breaking it, the interpreter of a URI
+ containing a line break immediately after a hyphen should ignore all
+ whitespace around the line break and should be aware that the hyphen
+ may or may not actually be part of the URI.
+
+ Using <> angle brackets around each URI is especially recommended as
+ a delimiting style for a reference that contains embedded whitespace.
+
+ The prefix "URL:" (with or without a trailing space) was formerly
+ recommended as a way to help distinguish a URI from other bracketed
+ designators, though it is not commonly used in practice and is no
+ longer recommended.
+
+ For robustness, software that accepts user-typed URI should attempt
+ to recognize and strip both delimiters and embedded whitespace.
+
+ For example, the text
+
+ Yes, Jim, I found it under "http://www.w3.org/Addressing/",
+ but you can probably pick it up from <ftp://foo.example.
+ com/rfc/>. Note the warning in <http://www.ics.uci.edu/pub/
+ ietf/uri/historical.html#WARNING>.
+
+ contains the URI references
+
+ http://www.w3.org/Addressing/
+ ftp://foo.example.com/rfc/
+ http://www.ics.uci.edu/pub/ietf/uri/historical.html#WARNING
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 52]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+Appendix D. Changes from RFC 2396
+
+D.1. Additions
+
+ An ABNF rule for URI has been introduced to correspond to one common
+ usage of the term: an absolute URI with optional fragment.
+
+ IPv6 (and later) literals have been added to the list of possible
+ identifiers for the host portion of an authority component, as
+ described by [RFC2732], with the addition of "[" and "]" to the
+ reserved set and a version flag to anticipate future versions of IP
+ literals. Square brackets are now specified as reserved within the
+ authority component and are not allowed outside their use as
+ delimiters for an IP literal within host. In order to make this
+ change without changing the technical definition of the path, query,
+ and fragment components, those rules were redefined to directly
+ specify the characters allowed.
+
+ As [RFC2732] defers to [RFC3513] for definition of an IPv6 literal
+ address, which, unfortunately, lacks an ABNF description of
+ IPv6address, we created a new ABNF rule for IPv6address that matches
+ the text representations defined by Section 2.2 of [RFC3513].
+ Likewise, the definition of IPv4address has been improved in order to
+ limit each decimal octet to the range 0-255.
+
+ Section 6, on URI normalization and comparison, has been completely
+ rewritten and extended by using input from Tim Bray and discussion
+ within the W3C Technical Architecture Group.
+
+D.2. Modifications
+
+ The ad-hoc BNF syntax of RFC 2396 has been replaced with the ABNF of
+ [RFC2234]. This change required all rule names that formerly
+ included underscore characters to be renamed with a dash instead. In
+ addition, a number of syntax rules have been eliminated or simplified
+ to make the overall grammar more comprehensible. Specifications that
+ refer to the obsolete grammar rules may be understood by replacing
+ those rules according to the following table:
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 53]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ +----------------+--------------------------------------------------+
+ | obsolete rule | translation |
+ +----------------+--------------------------------------------------+
+ | absoluteURI | absolute-URI |
+ | relativeURI | relative-part [ "?" query ] |
+ | hier_part | ( "//" authority path-abempty / |
+ | | path-absolute ) [ "?" query ] |
+ | | |
+ | opaque_part | path-rootless [ "?" query ] |
+ | net_path | "//" authority path-abempty |
+ | abs_path | path-absolute |
+ | rel_path | path-rootless |
+ | rel_segment | segment-nz-nc |
+ | reg_name | reg-name |
+ | server | authority |
+ | hostport | host [ ":" port ] |
+ | hostname | reg-name |
+ | path_segments | path-abempty |
+ | param | *<pchar excluding ";"> |
+ | | |
+ | uric | unreserved / pct-encoded / ";" / "?" / ":" |
+ | | / "@" / "&" / "=" / "+" / "$" / "," / "/" |
+ | | |
+ | uric_no_slash | unreserved / pct-encoded / ";" / "?" / ":" |
+ | | / "@" / "&" / "=" / "+" / "$" / "," |
+ | | |
+ | mark | "-" / "_" / "." / "!" / "~" / "*" / "'" |
+ | | / "(" / ")" |
+ | | |
+ | escaped | pct-encoded |
+ | hex | HEXDIG |
+ | alphanum | ALPHA / DIGIT |
+ +----------------+--------------------------------------------------+
+
+ Use of the above obsolete rules for the definition of scheme-specific
+ syntax is deprecated.
+
+ Section 2, on characters, has been rewritten to explain what
+ characters are reserved, when they are reserved, and why they are
+ reserved, even when they are not used as delimiters by the generic
+ syntax. The mark characters that are typically unsafe to decode,
+ including the exclamation mark ("!"), asterisk ("*"), single-quote
+ ("'"), and open and close parentheses ("(" and ")"), have been moved
+ to the reserved set in order to clarify the distinction between
+ reserved and unreserved and, hopefully, to answer the most common
+ question of scheme designers. Likewise, the section on
+ percent-encoded characters has been rewritten, and URI normalizers
+ are now given license to decode any percent-encoded octets
+
+
+
+Berners-Lee, et al. Standards Track [Page 54]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ corresponding to unreserved characters. In general, the terms
+ "escaped" and "unescaped" have been replaced with "percent-encoded"
+ and "decoded", respectively, to reduce confusion with other forms of
+ escape mechanisms.
+
+ The ABNF for URI and URI-reference has been redesigned to make them
+ more friendly to LALR parsers and to reduce complexity. As a result,
+ the layout form of syntax description has been removed, along with
+ the uric, uric_no_slash, opaque_part, net_path, abs_path, rel_path,
+ path_segments, rel_segment, and mark rules. All references to
+ "opaque" URIs have been replaced with a better description of how the
+ path component may be opaque to hierarchy. The relativeURI rule has
+ been replaced with relative-ref to avoid unnecessary confusion over
+ whether they are a subset of URI. The ambiguity regarding the
+ parsing of URI-reference as a URI or a relative-ref with a colon in
+ the first segment has been eliminated through the use of five
+ separate path matching rules.
+
+ The fragment identifier has been moved back into the section on
+ generic syntax components and within the URI and relative-ref rules,
+ though it remains excluded from absolute-URI. The number sign ("#")
+ character has been moved back to the reserved set as a result of
+ reintegrating the fragment syntax.
+
+ The ABNF has been corrected to allow the path component to be empty.
+ This also allows an absolute-URI to consist of nothing after the
+ "scheme:", as is present in practice with the "dav:" namespace
+ [RFC2518] and with the "about:" scheme used internally by many WWW
+ browser implementations. The ambiguity regarding the boundary
+ between authority and path has been eliminated through the use of
+ five separate path matching rules.
+
+ Registry-based naming authorities that use the generic syntax are now
+ defined within the host rule. This change allows current
+ implementations, where whatever name provided is simply fed to the
+ local name resolution mechanism, to be consistent with the
+ specification. It also removes the need to re-specify DNS name
+ formats here. Furthermore, it allows the host component to contain
+ percent-encoded octets, which is necessary to enable
+ internationalized domain names to be provided in URIs, processed in
+ their native character encodings at the application layers above URI
+ processing, and passed to an IDNA library as a registered name in the
+ UTF-8 character encoding. The server, hostport, hostname,
+ domainlabel, toplabel, and alphanum rules have been removed.
+
+ The resolving relative references algorithm of [RFC2396] has been
+ rewritten with pseudocode for this revision to improve clarity and
+ fix the following issues:
+
+
+
+Berners-Lee, et al. Standards Track [Page 55]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ o [RFC2396] section 5.2, step 6a, failed to account for a base URI
+ with no path.
+
+ o Restored the behavior of [RFC1808] where, if the reference
+ contains an empty path and a defined query component, the target
+ URI inherits the base URI's path component.
+
+ o The determination of whether a URI reference is a same-document
+ reference has been decoupled from the URI parser, simplifying the
+ URI processing interface within applications in a way consistent
+ with the internal architecture of deployed URI processing
+ implementations. The determination is now based on comparison to
+ the base URI after transforming a reference to absolute form,
+ rather than on the format of the reference itself. This change
+ may result in more references being considered "same-document"
+ under this specification than there would be under the rules given
+ in RFC 2396, especially when normalization is used to reduce
+ aliases. However, it does not change the status of existing
+ same-document references.
+
+ o Separated the path merge routine into two routines: merge, for
+ describing combination of the base URI path with a relative-path
+ reference, and remove_dot_segments, for describing how to remove
+ the special "." and ".." segments from a composed path. The
+ remove_dot_segments algorithm is now applied to all URI reference
+ paths in order to match common implementations and to improve the
+ normalization of URIs in practice. This change only impacts the
+ parsing of abnormal references and same-scheme references wherein
+ the base URI has a non-hierarchical path.
+
+Index
+
+ A
+ ABNF 11
+ absolute 27
+ absolute-path 26
+ absolute-URI 27
+ access 9
+ authority 17, 18
+
+ B
+ base URI 28
+
+ C
+ character encoding 4
+ character 4
+ characters 8, 11
+ coded character set 4
+
+
+
+Berners-Lee, et al. Standards Track [Page 56]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ D
+ dec-octet 20
+ dereference 9
+ dot-segments 23
+
+ F
+ fragment 16, 24
+
+ G
+ gen-delims 13
+ generic syntax 6
+
+ H
+ h16 20
+ hier-part 16
+ hierarchical 10
+ host 18
+
+ I
+ identifier 5
+ IP-literal 19
+ IPv4 20
+ IPv4address 19, 20
+ IPv6 19
+ IPv6address 19, 20
+ IPvFuture 19
+
+ L
+ locator 7
+ ls32 20
+
+ M
+ merge 32
+
+ N
+ name 7
+ network-path 26
+
+ P
+ path 16, 22, 26
+ path-abempty 22
+ path-absolute 22
+ path-empty 22
+ path-noscheme 22
+ path-rootless 22
+ path-abempty 16, 22, 26
+ path-absolute 16, 22, 26
+ path-empty 16, 22, 26
+
+
+
+Berners-Lee, et al. Standards Track [Page 57]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ path-rootless 16, 22
+ pchar 23
+ pct-encoded 12
+ percent-encoding 12
+ port 22
+
+ Q
+ query 16, 23
+
+ R
+ reg-name 21
+ registered name 20
+ relative 10, 28
+ relative-path 26
+ relative-ref 26
+ remove_dot_segments 33
+ representation 9
+ reserved 12
+ resolution 9, 28
+ resource 5
+ retrieval 9
+
+ S
+ same-document 27
+ sameness 9
+ scheme 16, 17
+ segment 22, 23
+ segment-nz 23
+ segment-nz-nc 23
+ sub-delims 13
+ suffix 27
+
+ T
+ transcription 8
+
+ U
+ uniform 4
+ unreserved 13
+ URI grammar
+ absolute-URI 27
+ ALPHA 11
+ authority 18
+ CR 11
+ dec-octet 20
+ DIGIT 11
+ DQUOTE 11
+ fragment 24
+ gen-delims 13
+
+
+
+Berners-Lee, et al. Standards Track [Page 58]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+ h16 20
+ HEXDIG 11
+ hier-part 16
+ host 19
+ IP-literal 19
+ IPv4address 20
+ IPv6address 20
+ IPvFuture 19
+ LF 11
+ ls32 20
+ OCTET 11
+ path 22
+ path-abempty 22
+ path-absolute 22
+ path-empty 22
+ path-noscheme 22
+ path-rootless 22
+ pchar 23
+ pct-encoded 12
+ port 22
+ query 24
+ reg-name 21
+ relative-ref 26
+ reserved 13
+ scheme 17
+ segment 23
+ segment-nz 23
+ segment-nz-nc 23
+ SP 11
+ sub-delims 13
+ unreserved 13
+ URI 16
+ URI-reference 25
+ userinfo 18
+ URI 16
+ URI-reference 25
+ URL 7
+ URN 7
+ userinfo 18
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 59]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+Authors' Addresses
+
+ Tim Berners-Lee
+ World Wide Web Consortium
+ Massachusetts Institute of Technology
+ 77 Massachusetts Avenue
+ Cambridge, MA 02139
+ USA
+
+ Phone: +1-617-253-5702
+ Fax: +1-617-258-5999
+ EMail: timbl@w3.org
+ URI: http://www.w3.org/People/Berners-Lee/
+
+
+ Roy T. Fielding
+ Day Software
+ 5251 California Ave., Suite 110
+ Irvine, CA 92617
+ USA
+
+ Phone: +1-949-679-2960
+ Fax: +1-949-679-2972
+ EMail: fielding@gbiv.com
+ URI: http://roy.gbiv.com/
+
+
+ Larry Masinter
+ Adobe Systems Incorporated
+ 345 Park Ave
+ San Jose, CA 95110
+ USA
+
+ Phone: +1-408-536-3024
+ EMail: LMM@acm.org
+ URI: http://larry.masinter.net/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 60]
+\f
+RFC 3986 URI Generic Syntax January 2005
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2005).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the IETF's procedures with respect to rights in IETF Documents can
+ be found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+Berners-Lee, et al. Standards Track [Page 61]
+\f
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsoup
+Description: a glib-based HTTP library
+Version: @VERSION@
+Requires: glib-2.0 gobject-2.0 gio-2.0
+Requires.private: libxml-2.0
+Libs: -L${libdir} -lsoup-2.4
+Cflags: -I${includedir}/libsoup-2.4
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsoup
+Description: a glib-based HTTP library
+Version: @VERSION@
+Requires: libsoup-2.4
+Libs: -L${libdir} -lsoup-gnome-2.4
+Cflags: -I${includedir}/libsoup-gnome-2.4
--- /dev/null
+#!/bin/sh
+
+# Build zipfiles for libsoup on Win32. Separate runtime and developer
+# ones. After running make install, run this.
+
+ZIP=/tmp/libsoup-@VERSION@.zip
+DEVZIP=/tmp/libsoup-dev-@VERSION@.zip
+
+cd @prefix@
+rm $ZIP
+
+current_minus_age=`expr @SOUP_CURRENT@ - @SOUP_AGE@`
+
+zip $ZIP -@ <<EOF
+bin/libsoup-@SOUP_API_VERSION@-$current_minus_age.dll
+EOF
+
+rm $DEVZIP
+zip -r $DEVZIP -@ <<EOF
+include/libsoup-@SOUP_API_VERSION@
+lib/libsoup-@SOUP_API_VERSION@.dll.a
+lib/pkgconfig/libsoup-@SOUP_API_VERSION@.pc
+share/gtk-doc/html/libsoup
+EOF
--- /dev/null
+<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
+ xmlns:gnome="http://api.gnome.org/doap-extensions#"
+ xmlns="http://usefulinc.com/ns/doap#">
+
+ <name xml:lang="en">libsoup</name>
+ <shortdesc xml:lang="en">HTTP client/server library for GNOME</shortdesc>
+ <description>libsoup is an HTTP client/server library for GNOME. It uses GObjects and the glib main loop, to integrate well with GNOME applications.</description>
+
+ <homepage rdf:resource="http://live.gnome.org/LibSoup" />
+ <download-page rdf:resource="ftp://ftp.gnome.org/pub/GNOME/sources/libsoup/"/>
+ <bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=libsoup"/>
+ <mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/libsoup-list"/>
+
+ <maintainer>
+ <foaf:Person>
+ <foaf:name>Dan Winship</foaf:name>
+ <foaf:mbox rdf:resource="mailto:danw@gnome.org" />
+ <gnome:userid>danw</gnome:userid>
+ </foaf:Person>
+ </maintainer>
+</Project>
--- /dev/null
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+LIBWS2_32 = -lws2_32
+endif
+
+INCLUDES = \
+ -DG_LOG_DOMAIN=\"libsoup\" \
+ -I$(top_srcdir) \
+ $(SOUP_DEBUG_FLAGS) \
+ $(SOUP_MAINTAINER_FLAGS) \
+ $(GLIB_CFLAGS) \
+ $(XML_CFLAGS) \
+ $(SQLITE_CFLAGS) \
+ $(GNOME_KEYRING_CFLAGS)
+
+MARSHAL_GENERATED = soup-marshal.c soup-marshal.h
+MKENUMS_GENERATED = soup-enum-types.c soup-enum-types.h
+
+soup-marshal.h: soup-marshal.list
+ $(AM_V_GEN) ( $(GLIB_GENMARSHAL) --prefix=soup_marshal $(srcdir)/soup-marshal.list --header > soup-marshal.tmp \
+ && mv soup-marshal.tmp soup-marshal.h ) \
+ || ( rm -f soup-marshal.tmp && exit 1 )
+
+soup-marshal.c: soup-marshal.h
+ $(AM_V_GEN) ( (echo '#include "soup-marshal.h"'; $(GLIB_GENMARSHAL) --prefix=soup_marshal $(srcdir)/soup-marshal.list --body) > soup-marshal.tmp \
+ && mv soup-marshal.tmp soup-marshal.c ) \
+ || ( rm -f soup-marshal.tmp && exit 1 )
+
+soup-enum-types.h: $(soup_headers)
+ $(AM_V_GEN) ( cd $(srcdir) && $(GLIB_MKENUMS) --template soup-enum-types.h.tmpl \
+ $(soup_headers) ) > soup-enum-types.h.tmp \
+ && mv soup-enum-types.h.tmp soup-enum-types.h \
+ || rm -f soup-enum-type.h.tmp
+
+soup-enum-types.c: $(libsoupinclude_HEADERS)
+ $(AM_V_GEN) ( cd $(srcdir) && $(GLIB_MKENUMS) --template soup-enum-types.c.tmpl \
+ $(soup_headers) ) > soup-enum-types.c.tmp \
+ && mv soup-enum-types.c.tmp soup-enum-types.c \
+ || rm -f soup-enum-type.c.tmp
+
+BUILT_SOURCES = $(MARSHAL_GENERATED) $(MKENUMS_GENERATED)
+
+CLEANFILES = $(MARSHAL_GENERATED) $(MKENUMS_GENERATED)
+
+libsoupincludedir = $(includedir)/libsoup-2.4/libsoup
+
+soup_headers = \
+ soup.h \
+ soup-address.h \
+ soup-auth.h \
+ soup-auth-domain.h \
+ soup-auth-domain-basic.h \
+ soup-auth-domain-digest.h \
+ soup-cache.h \
+ soup-content-decoder.h \
+ soup-content-sniffer.h \
+ soup-cookie.h \
+ soup-cookie-jar.h \
+ soup-cookie-jar-text.h \
+ soup-date.h \
+ soup-form.h \
+ soup-headers.h \
+ soup-logger.h \
+ soup-message.h \
+ soup-message-body.h \
+ soup-message-headers.h \
+ soup-method.h \
+ soup-misc.h \
+ soup-multipart.h \
+ soup-password-manager.h \
+ soup-portability.h \
+ soup-proxy-resolver.h \
+ soup-proxy-resolver-default.h \
+ soup-proxy-uri-resolver.h \
+ soup-request.h \
+ soup-request-data.h \
+ soup-request-file.h \
+ soup-request-http.h \
+ soup-requester.h \
+ soup-server.h \
+ soup-session.h \
+ soup-session-async.h \
+ soup-session-feature.h \
+ soup-session-sync.h \
+ soup-socket.h \
+ soup-status.h \
+ soup-types.h \
+ soup-uri.h \
+ soup-value-utils.h \
+ soup-xmlrpc.h
+
+if SQLLITE_SUPPORT
+soup_headers += \
+ soup-cookie-jar-sqlite.h
+endif
+
+libsoupinclude_HEADERS = \
+ $(soup_headers) \
+ soup-enum-types.h
+
+lib_LTLIBRARIES = libsoup-2.4.la
+
+libsoup_2_4_la_LDFLAGS = \
+ -version-info $(SOUP_CURRENT):$(SOUP_REVISION):$(SOUP_AGE) -no-undefined
+
+libsoup_2_4_la_LIBADD = \
+ $(GLIB_LIBS) \
+ $(XML_LIBS) \
+ -lz \
+ $(LIBWS2_32)
+
+if SQLLITE_SUPPORT
+libsoup_2_4_la_LIBADD += \
+ $(SQLITE_LIBS)
+endif
+
+libsoup_2_4_la_SOURCES = \
+ $(BUILT_SOURCES) \
+ soup-address.c \
+ soup-auth.c \
+ soup-auth-basic.h \
+ soup-auth-basic.c \
+ soup-auth-digest.h \
+ soup-auth-digest.c \
+ soup-auth-ntlm.h \
+ soup-auth-ntlm.c \
+ soup-auth-domain.c \
+ soup-auth-domain-basic.c \
+ soup-auth-domain-digest.c \
+ soup-auth-manager.h \
+ soup-auth-manager.c \
+ soup-auth-manager-ntlm.h \
+ soup-auth-manager-ntlm.c \
+ soup-cache.c \
+ soup-cache-private.h \
+ soup-connection.h \
+ soup-connection.c \
+ soup-content-decoder.c \
+ soup-content-sniffer.c \
+ soup-cookie.c \
+ soup-cookie-jar.c \
+ soup-cookie-jar-text.c \
+ soup-date.c \
+ soup-directory-input-stream.h \
+ soup-directory-input-stream.c \
+ soup-form.c \
+ soup-headers.c \
+ soup-http-input-stream.h \
+ soup-http-input-stream.c \
+ soup-logger.c \
+ soup-message.c \
+ soup-message-body.c \
+ soup-message-client-io.c \
+ soup-message-headers.c \
+ soup-message-io.c \
+ soup-message-private.h \
+ soup-message-queue.h \
+ soup-message-queue.c \
+ soup-message-server-io.c \
+ soup-method.c \
+ soup-misc.c \
+ soup-misc-private.h \
+ soup-multipart.c \
+ soup-password-manager.c \
+ soup-path-map.h \
+ soup-path-map.c \
+ soup-proxy-resolver.c \
+ soup-proxy-resolver-default.c \
+ soup-proxy-resolver-static.h \
+ soup-proxy-resolver-static.c \
+ soup-proxy-uri-resolver.c \
+ soup-request.c \
+ soup-request-data.c \
+ soup-request-file.c \
+ soup-request-http.c \
+ soup-requester.c \
+ soup-server.c \
+ soup-session.c \
+ soup-session-async.c \
+ soup-session-feature.c \
+ soup-session-private.h \
+ soup-session-sync.c \
+ soup-socket.c \
+ soup-ssl.h \
+ soup-ssl.c \
+ soup-status.c \
+ soup-uri.c \
+ soup-value-utils.c \
+ soup-xmlrpc.c
+
+if SQLLITE_SUPPORT
+libsoup_2_4_la_SOURCES += \
+ soup-cookie-jar-sqlite.c
+endif
+
+if BUILD_LIBSOUP_GNOME
+
+if OS_WIN32
+soup_password_manager_gnome_files =
+else
+soup_password_manager_gnome_files = \
+ soup-password-manager-gnome.h \
+ soup-password-manager-gnome.c
+endif
+
+libsoupgnomeincludedir = $(includedir)/libsoup-gnome-2.4/libsoup
+
+libsoupgnomeinclude_HEADERS = \
+ soup-cookie-jar-sqlite.h\
+ soup-gnome.h \
+ soup-gnome-features.h
+
+lib_LTLIBRARIES += libsoup-gnome-2.4.la
+
+libsoup_gnome_2_4_la_LDFLAGS = $(libsoup_2_4_la_LDFLAGS)
+
+libsoup_gnome_2_4_la_LIBADD = \
+ libsoup-2.4.la \
+ $(GLIB_LIBS) \
+ $(SQLITE_LIBS) \
+ $(GNOME_KEYRING_LIBS)
+
+libsoup_gnome_2_4_la_SOURCES = \
+ soup-cookie-jar-sqlite.c \
+ soup-gnome-features.c \
+ soup-proxy-resolver-gnome.h \
+ soup-proxy-resolver-gnome.c \
+ $(soup_password_manager_gnome_files)
+
+endif
+
+#
+# Introspection support
+#
+include $(INTROSPECTION_MAKEFILE)
+INTROSPECTION_GIRS =
+INTROSPECTION_SCANNER_ARGS = --add-include-path=.
+INTROSPECTION_COMPILER_ARGS = --includedir=.
+
+if HAVE_INTROSPECTION
+
+# Core library
+gi_soup_files = \
+ $(filter-out soup.h soup-enum-types.% soup-marshal.% soup-proxy-resolver.h,\
+ $(soup_headers) $(filter-out %.h, $(libsoup_2_4_la_SOURCES)))
+gi_built_soup_files = soup-enum-types.h
+
+Soup-2.4.gir: libsoup-2.4.la
+Soup_2_4_gir_INCLUDES = Gio-2.0
+Soup_2_4_gir_CFLAGS = $(INCLUDES)
+Soup_2_4_gir_LIBS = libsoup-2.4.la
+Soup_2_4_gir_EXPORT_PACKAGES = libsoup-2.4
+Soup_2_4_gir_SCANNERFLAGS = --c-include "libsoup/soup.h"
+Soup_2_4_gir_FILES = \
+ $(addprefix $(srcdir)/, $(gi_soup_files)) \
+ $(foreach f,$(gi_built_soup_files), \
+ $(if $(shell test -f $(addprefix $(srcdir)/,$(f)) && echo yes), \
+ $(addprefix $(srcdir)/,$(f)), \
+ $(f)))
+
+INTROSPECTION_GIRS += Soup-2.4.gir
+
+if BUILD_LIBSOUP_GNOME
+
+# GNOME extensions
+gi_soup_gnome_files = $(filter-out soup-gnome.h,\
+ $(libsoupgnomeinclude_HEADERS) \
+ $(filter-out %.h, $(libsoup_gnome_2_4_la_SOURCES)))
+SoupGNOME-2.4.gir: libsoup-gnome-2.4.la Soup-2.4.gir
+SoupGNOME_2_4_gir_SCANNERFLAGS = \
+ --identifier-prefix=Soup \
+ --symbol-prefix=soup \
+ --c-include "libsoup/soup-gnome.h" \
+ --include-uninstalled=$(builddir)/Soup-2.4.gir
+SoupGNOME_2_4_gir_CFLAGS = $(INCLUDES)
+SoupGNOME_2_4_gir_LIBS = libsoup-gnome-2.4.la libsoup-2.4.la
+SoupGNOME_2_4_gir_FILES = $(addprefix $(srcdir)/,$(gi_soup_gnome_files))
+SoupGNOME_2_4_gir_EXPORT_PACKAGES = libsoup-gnome-2.4
+
+INTROSPECTION_GIRS += SoupGNOME-2.4.gir
+
+endif
+
+girdir = $(datadir)/gir-1.0
+gir_DATA = $(INTROSPECTION_GIRS)
+
+typelibdir = $(libdir)/girepository-1.0
+typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+
+CLEANFILES += $(gir_DATA) $(typelib_DATA)
+
+endif
+
+EXTRA_DIST= \
+ soup-marshal.list \
+ soup-enum-types.h.tmpl \
+ soup-enum-types.c.tmpl
--- /dev/null
+/*
+ * TIZEN.h
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ */
+
+#ifndef TIZEN_H
+#define TIZEN_H
+
+#define ENABLE(TIZEN_FEATURE) (defined ENABLE_##TIZEN_FEATURE && ENABLE_##TIZEN_FEATURE)
+
+#define ENABLE_TIZEN_FIX_PACK_ENTRY 1
+#define ENABLE_TIZEN_FIX_CONTENT_SNIFFER_PATTERN 1
+#define ENABLE_TIZEN_FIX_PAUSE_MESSAGE 1
+
+#endif //#ifndef TIZEN_H
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-address.c: Internet address handing
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <gio/gio.h>
+
+#include "soup-address.h"
+#include "soup-enum-types.h"
+#include "soup-marshal.h"
+#include "soup-misc.h"
+
+/**
+ * SECTION:soup-address
+ * @short_description: DNS support
+ *
+ * #SoupAddress represents the address of a TCP connection endpoint:
+ * both the IP address and the port. (It is somewhat like an
+ * object-oriented version of struct sockaddr.)
+ **/
+
+enum {
+ PROP_0,
+
+ PROP_NAME,
+ PROP_FAMILY,
+ PROP_PORT,
+ PROP_PHYSICAL,
+ PROP_SOCKADDR,
+
+ LAST_PROP
+};
+
+typedef struct {
+ struct sockaddr_storage *sockaddr;
+ int n_addrs, offset;
+
+ char *name, *physical;
+ guint port;
+
+ GMutex *lock;
+ GSList *async_lookups;
+} SoupAddressPrivate;
+#define SOUP_ADDRESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_ADDRESS, SoupAddressPrivate))
+
+/* sockaddr generic macros */
+#define SOUP_SIN(priv) ((struct sockaddr_in *)priv->sockaddr)
+#define SOUP_SIN6(priv) ((struct sockaddr_in6 *)priv->sockaddr)
+
+/* sockaddr family macros */
+#define SOUP_ADDRESS_GET_FAMILY(priv) (priv->sockaddr->ss_family)
+#define SOUP_ADDRESS_SET_FAMILY(priv, family) \
+ (priv->sockaddr->ss_family = family)
+#define SOUP_ADDRESS_FAMILY_IS_VALID(family) \
+ (family == AF_INET || family == AF_INET6)
+#define SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE(family) \
+ (family == AF_INET ? sizeof (struct sockaddr_in) : \
+ sizeof (struct sockaddr_in6))
+#define SOUP_ADDRESS_FAMILY_DATA_SIZE(family) \
+ (family == AF_INET ? sizeof (struct in_addr) : \
+ sizeof (struct in6_addr))
+
+/* sockaddr port macros */
+#define SOUP_ADDRESS_PORT_IS_VALID(port) (port >= 0 && port <= 65535)
+#define SOUP_ADDRESS_GET_PORT(priv) \
+ (priv->sockaddr->ss_family == AF_INET ? \
+ SOUP_SIN(priv)->sin_port : \
+ SOUP_SIN6(priv)->sin6_port)
+#define SOUP_ADDRESS_SET_PORT(priv, port) \
+ G_STMT_START { \
+ if (priv->sockaddr->ss_family == AF_INET) \
+ SOUP_SIN(priv)->sin_port = port; \
+ else \
+ SOUP_SIN6(priv)->sin6_port = port; \
+ } G_STMT_END
+
+/* sockaddr data macros */
+#define SOUP_ADDRESS_GET_DATA(priv) \
+ (priv->sockaddr->ss_family == AF_INET ? \
+ (gpointer)&SOUP_SIN(priv)->sin_addr : \
+ (gpointer)&SOUP_SIN6(priv)->sin6_addr)
+#define SOUP_ADDRESS_SET_DATA(priv, data, length) \
+ memcpy (SOUP_ADDRESS_GET_DATA (priv), data, length)
+
+
+static GObject *constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties);
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void soup_address_connectable_iface_init (GSocketConnectableIface *connectable_iface);
+static GSocketAddressEnumerator *soup_address_connectable_enumerate (GSocketConnectable *connectable);
+
+G_DEFINE_TYPE_WITH_CODE (SoupAddress, soup_address, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
+ soup_address_connectable_iface_init))
+
+static void
+soup_address_init (SoupAddress *addr)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ priv->lock = g_mutex_new ();
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAddress *addr = SOUP_ADDRESS (object);
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ if (priv->sockaddr)
+ g_free (priv->sockaddr);
+ if (priv->name)
+ g_free (priv->name);
+ if (priv->physical)
+ g_free (priv->physical);
+
+ g_mutex_free (priv->lock);
+
+ G_OBJECT_CLASS (soup_address_parent_class)->finalize (object);
+}
+
+static void
+soup_address_class_init (SoupAddressClass *address_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (address_class);
+
+ g_type_class_add_private (address_class, sizeof (SoupAddressPrivate));
+
+ /* virtual method override */
+ object_class->constructor = constructor;
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /* properties */
+ /**
+ * SOUP_ADDRESS_NAME:
+ *
+ * Alias for the #SoupAddress:name property. (The hostname for
+ * this address.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_NAME,
+ g_param_spec_string (SOUP_ADDRESS_NAME,
+ "Name",
+ "Hostname for this address",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_ADDRESS_FAMILY:
+ *
+ * Alias for the #SoupAddress:family property. (The
+ * #SoupAddressFamily for this address.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_FAMILY,
+ g_param_spec_enum (SOUP_ADDRESS_FAMILY,
+ "Family",
+ "Address family for this address",
+ SOUP_TYPE_ADDRESS_FAMILY,
+ SOUP_ADDRESS_FAMILY_INVALID,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_ADDRESS_PORT:
+ *
+ * An alias for the #SoupAddress:port property. (The port for
+ * this address.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_PORT,
+ g_param_spec_int (SOUP_ADDRESS_PORT,
+ "Port",
+ "Port for this address",
+ -1, 65535, -1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_ADDRESS_PHYSICAL:
+ *
+ * An alias for the #SoupAddress:physical property. (The
+ * stringified IP address for this address.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_PHYSICAL,
+ g_param_spec_string (SOUP_ADDRESS_PHYSICAL,
+ "Physical address",
+ "IP address for this address",
+ NULL,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_ADDRESS_SOCKADDR:
+ *
+ * An alias for the #SoupAddress:sockaddr property. (A pointer
+ * to the struct sockaddr for this address.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SOCKADDR,
+ g_param_spec_pointer (SOUP_ADDRESS_SOCKADDR,
+ "sockaddr",
+ "struct sockaddr for this address",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+soup_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
+{
+ connectable_iface->enumerate = soup_address_connectable_enumerate;
+}
+
+static GObject *
+constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GObject *addr;
+ SoupAddressPrivate *priv;
+
+ addr = G_OBJECT_CLASS (soup_address_parent_class)->constructor (
+ type, n_construct_properties, construct_properties);
+ if (!addr)
+ return NULL;
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ if (!priv->name && !priv->sockaddr) {
+ g_object_unref (addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (object);
+ SoupAddressFamily family;
+ struct sockaddr *sa;
+ int len, port;
+
+ /* This is a mess because the properties are mostly orthogonal,
+ * but g_object_constructor wants to set a default value for each
+ * of them.
+ */
+
+ switch (prop_id) {
+ case PROP_NAME:
+ priv->name = g_value_dup_string (value);
+ break;
+
+ case PROP_FAMILY:
+ family = g_value_get_enum (value);
+ if (family == SOUP_ADDRESS_FAMILY_INVALID)
+ return;
+ g_return_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (family));
+ g_return_if_fail (priv->sockaddr == NULL);
+
+ priv->sockaddr = g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (family));
+ SOUP_ADDRESS_SET_FAMILY (priv, family);
+ SOUP_ADDRESS_SET_PORT (priv, htons (priv->port));
+ priv->n_addrs = 1;
+ break;
+
+ case PROP_PORT:
+ port = g_value_get_int (value);
+ if (port == -1)
+ return;
+ g_return_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port));
+
+ priv->port = port;
+ if (priv->sockaddr)
+ SOUP_ADDRESS_SET_PORT (priv, htons (port));
+ break;
+
+ case PROP_SOCKADDR:
+ sa = g_value_get_pointer (value);
+ if (!sa)
+ return;
+ g_return_if_fail (priv->sockaddr == NULL);
+
+ len = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (sa->sa_family);
+ priv->sockaddr = g_memdup (sa, len);
+ priv->n_addrs = 1;
+ priv->port = ntohs (SOUP_ADDRESS_GET_PORT (priv));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, priv->name);
+ break;
+ case PROP_FAMILY:
+ if (priv->sockaddr)
+ g_value_set_enum (value, SOUP_ADDRESS_GET_FAMILY (priv));
+ else
+ g_value_set_enum (value, 0);
+ break;
+ case PROP_PORT:
+ g_value_set_int (value, priv->port);
+ break;
+ case PROP_PHYSICAL:
+ g_value_set_string (value, soup_address_get_physical (SOUP_ADDRESS (object)));
+ break;
+ case PROP_SOCKADDR:
+ g_value_set_pointer (value, priv->sockaddr);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_address_new:
+ * @name: a hostname or physical address
+ * @port: a port number
+ *
+ * Creates a #SoupAddress from @name and @port. The #SoupAddress's IP
+ * address may not be available right away; the caller can call
+ * soup_address_resolve_async() or soup_address_resolve_sync() to
+ * force a DNS resolution.
+ *
+ * Return value: a #SoupAddress
+ **/
+SoupAddress *
+soup_address_new (const char *name, guint port)
+{
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port), NULL);
+
+ return g_object_new (SOUP_TYPE_ADDRESS,
+ SOUP_ADDRESS_NAME, name,
+ SOUP_ADDRESS_PORT, port,
+ NULL);
+}
+
+/**
+ * soup_address_new_from_sockaddr:
+ * @sa: a pointer to a sockaddr
+ * @len: size of @sa
+ *
+ * Returns a #SoupAddress equivalent to @sa (or %NULL if @sa's
+ * address family isn't supported)
+ *
+ * Return value: (allow-none): the new #SoupAddress
+ **/
+SoupAddress *
+soup_address_new_from_sockaddr (struct sockaddr *sa, int len)
+{
+ g_return_val_if_fail (sa != NULL, NULL);
+ g_return_val_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (sa->sa_family), NULL);
+ g_return_val_if_fail (len == SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (sa->sa_family), NULL);
+
+ return g_object_new (SOUP_TYPE_ADDRESS,
+ SOUP_ADDRESS_SOCKADDR, sa,
+ NULL);
+}
+
+/**
+ * SoupAddressFamily:
+ * @SOUP_ADDRESS_FAMILY_INVALID: an invalid %SoupAddress
+ * @SOUP_ADDRESS_FAMILY_IPV4: an IPv4 address
+ * @SOUP_ADDRESS_FAMILY_IPV6: an IPv6 address
+ *
+ * The supported address families.
+ **/
+
+/**
+ * SOUP_ADDRESS_ANY_PORT:
+ *
+ * This can be passed to any #SoupAddress method that expects a port,
+ * to indicate that you don't care what port is used.
+ **/
+
+/**
+ * soup_address_new_any:
+ * @family: the address family
+ * @port: the port number (usually %SOUP_ADDRESS_ANY_PORT)
+ *
+ * Returns a #SoupAddress corresponding to the "any" address
+ * for @family (or %NULL if @family isn't supported), suitable for
+ * passing to soup_socket_server_new().
+ *
+ * Return value: (allow-none): the new #SoupAddress
+ **/
+SoupAddress *
+soup_address_new_any (SoupAddressFamily family, guint port)
+{
+ g_return_val_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (family), NULL);
+ g_return_val_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port), NULL);
+
+ return g_object_new (SOUP_TYPE_ADDRESS,
+ SOUP_ADDRESS_FAMILY, family,
+ SOUP_ADDRESS_PORT, port,
+ NULL);
+}
+
+/**
+ * soup_address_get_name:
+ * @addr: a #SoupAddress
+ *
+ * Returns the hostname associated with @addr.
+ *
+ * This method is not thread-safe; if you call it while @addr is being
+ * resolved in another thread, it may return garbage. You can use
+ * soup_address_is_resolved() to safely test whether or not an address
+ * is resolved before fetching its name or address.
+ *
+ * Return value: (allow-none): the hostname, or %NULL if it is not known.
+ **/
+const char *
+soup_address_get_name (SoupAddress *addr)
+{
+ g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
+
+ return SOUP_ADDRESS_GET_PRIVATE (addr)->name;
+}
+
+/**
+ * soup_address_get_sockaddr:
+ * @addr: a #SoupAddress
+ * @len: return location for sockaddr length
+ *
+ * Returns the sockaddr associated with @addr, with its length in
+ * *@len. If the sockaddr is not yet known, returns %NULL.
+ *
+ * This method is not thread-safe; if you call it while @addr is being
+ * resolved in another thread, it may return garbage. You can use
+ * soup_address_is_resolved() to safely test whether or not an address
+ * is resolved before fetching its name or address.
+ *
+ * Return value: (allow-none) (transfer none): the sockaddr, or %NULL
+ **/
+struct sockaddr *
+soup_address_get_sockaddr (SoupAddress *addr, int *len)
+{
+ SoupAddressPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ if (priv->sockaddr && len)
+ *len = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (SOUP_ADDRESS_GET_FAMILY (priv));
+ return (struct sockaddr *)priv->sockaddr;
+}
+
+/**
+ * soup_address_get_gsockaddr:
+ * @addr: a #SoupAddress
+ *
+ * Creates a new #GSocketAddress corresponding to @addr (which is assumed
+ * to only have one socket address associated with it).
+ *
+ * Return value: (transfer full): a new #GSocketAddress
+ *
+ * Since: 2.32
+ */
+GSocketAddress *
+soup_address_get_gsockaddr (SoupAddress *addr)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ return g_socket_address_new_from_native (priv->sockaddr,
+ SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (SOUP_ADDRESS_GET_FAMILY (priv)));
+}
+
+static GInetAddress *
+soup_address_make_inet_address (SoupAddress *addr)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ GSocketAddress *gsa;
+ GInetAddress *gia;
+
+ gsa = g_socket_address_new_from_native (priv->sockaddr,
+ SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (SOUP_ADDRESS_GET_FAMILY (priv)));
+ gia = g_inet_socket_address_get_address ((GInetSocketAddress *)gsa);
+ g_object_ref (gia);
+ g_object_unref (gsa);
+ return gia;
+}
+
+/**
+ * soup_address_get_physical:
+ * @addr: a #SoupAddress
+ *
+ * Returns the physical address associated with @addr as a string.
+ * (Eg, "127.0.0.1"). If the address is not yet known, returns %NULL.
+ *
+ * This method is not thread-safe; if you call it while @addr is being
+ * resolved in another thread, it may return garbage. You can use
+ * soup_address_is_resolved() to safely test whether or not an address
+ * is resolved before fetching its name or address.
+ *
+ * Return value: (allow-none): the physical address, or %NULL
+ **/
+const char *
+soup_address_get_physical (SoupAddress *addr)
+{
+ SoupAddressPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ if (!priv->sockaddr)
+ return NULL;
+
+ if (!priv->physical) {
+ GInetAddress *gia;
+
+ gia = soup_address_make_inet_address (addr);
+ priv->physical = g_inet_address_to_string (gia);
+ g_object_unref (gia);
+ }
+
+ return priv->physical;
+}
+
+/**
+ * soup_address_get_port:
+ * @addr: a #SoupAddress
+ *
+ * Returns the port associated with @addr.
+ *
+ * Return value: the port
+ **/
+guint
+soup_address_get_port (SoupAddress *addr)
+{
+ g_return_val_if_fail (SOUP_IS_ADDRESS (addr), 0);
+
+ return SOUP_ADDRESS_GET_PRIVATE (addr)->port;
+}
+
+
+static guint
+update_addrs (SoupAddress *addr, GList *addrs, GError *error)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ GInetAddress *gia;
+ GSocketAddress *gsa;
+ int i;
+
+ if (error) {
+ if (error->domain == G_IO_ERROR &&
+ error->code == G_IO_ERROR_CANCELLED)
+ return SOUP_STATUS_CANCELLED;
+ else
+ return SOUP_STATUS_CANT_RESOLVE;
+ } else if (!addrs)
+ return SOUP_STATUS_CANT_RESOLVE;
+ else if (priv->sockaddr)
+ return SOUP_STATUS_OK;
+
+ priv->n_addrs = g_list_length (addrs);
+ priv->sockaddr = g_new (struct sockaddr_storage, priv->n_addrs);
+ for (i = 0; addrs; addrs = addrs->next, i++) {
+ gia = addrs->data;
+ gsa = g_inet_socket_address_new (gia, priv->port);
+
+ if (!g_socket_address_to_native (gsa, &priv->sockaddr[i],
+ sizeof (struct sockaddr_storage),
+ NULL)) {
+ /* can't happen: We know the address format is supported
+ * and the buffer is large enough
+ */
+ g_warn_if_reached ();
+ }
+ g_object_unref (gsa);
+ }
+
+ return SOUP_STATUS_OK;
+}
+
+static guint
+update_name (SoupAddress *addr, const char *name, GError *error)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ if (error) {
+ if (error->domain == G_IO_ERROR &&
+ error->code == G_IO_ERROR_CANCELLED)
+ return SOUP_STATUS_CANCELLED;
+ else
+ return SOUP_STATUS_CANT_RESOLVE;
+ } else if (!name)
+ return SOUP_STATUS_CANT_RESOLVE;
+ else if (priv->name)
+ return SOUP_STATUS_OK;
+
+ priv->name = g_strdup (name);
+ return SOUP_STATUS_OK;
+}
+
+typedef struct {
+ SoupAddressCallback callback;
+ gpointer callback_data;
+} SoupAddressResolveAsyncData;
+
+static void
+complete_resolve_async (SoupAddress *addr, guint status)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ SoupAddressResolveAsyncData *res_data;
+ GSList *lookups, *l;
+ GSource *current_source;
+ GMainContext *current_context;
+
+ lookups = priv->async_lookups;
+ priv->async_lookups = NULL;
+
+ /* Awful hack; to make soup_socket_connect_async() with an
+ * non-default async_context work correctly, we need to ensure
+ * that the non-default context (which we're now running in)
+ * is the thread-default when the callbacks are run...
+ */
+ current_source = g_main_current_source ();
+ if (current_source && !g_source_is_destroyed (current_source))
+ current_context = g_source_get_context (current_source);
+ else
+ current_context = NULL;
+ g_main_context_push_thread_default (current_context);
+
+ for (l = lookups; l; l = l->next) {
+ res_data = l->data;
+
+ if (res_data->callback) {
+ res_data->callback (addr, status,
+ res_data->callback_data);
+ }
+ g_slice_free (SoupAddressResolveAsyncData, res_data);
+ }
+ g_slist_free (lookups);
+
+ g_main_context_pop_thread_default (current_context);
+
+ g_object_unref (addr);
+}
+
+static void
+lookup_resolved (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GResolver *resolver = G_RESOLVER (source);
+ SoupAddress *addr = user_data;
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ GError *error = NULL;
+ guint status;
+
+ if (!priv->sockaddr) {
+ GList *addrs;
+
+ addrs = g_resolver_lookup_by_name_finish (resolver, result,
+ &error);
+ status = update_addrs (addr, addrs, error);
+ g_resolver_free_addresses (addrs);
+ } else if (!priv->name) {
+ char *name;
+
+ name = g_resolver_lookup_by_address_finish (resolver, result,
+ &error);
+ status = update_name (addr, name, error);
+ g_free (name);
+ } else
+ status = SOUP_STATUS_OK;
+
+ /* For the enumerator impl, below */
+ g_object_ref (addr);
+ g_object_set_data (G_OBJECT (addr), "async-resolved-error", error);
+
+ complete_resolve_async (addr, status);
+
+ g_object_set_data (G_OBJECT (addr), "async-resolved-error", NULL);
+ g_object_unref (addr);
+ if (error)
+ g_error_free (error);
+}
+
+static gboolean
+idle_complete_resolve (gpointer addr)
+{
+ complete_resolve_async (addr, SOUP_STATUS_OK);
+ return FALSE;
+}
+
+/**
+ * SoupAddressCallback:
+ * @addr: the #SoupAddress that was resolved
+ * @status: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or
+ * %SOUP_STATUS_CANCELLED
+ * @user_data: the user data that was passed to
+ * soup_address_resolve_async()
+ *
+ * The callback function passed to soup_address_resolve_async().
+ **/
+
+/**
+ * soup_address_resolve_async:
+ * @addr: a #SoupAddress
+ * @async_context: (allow-none): the #GMainContext to call @callback from
+ * @cancellable: a #GCancellable object, or %NULL
+ * @callback: (scope async): callback to call with the result
+ * @user_data: data for @callback
+ *
+ * Asynchronously resolves the missing half of @addr (its IP address
+ * if it was created with soup_address_new(), or its hostname if it
+ * was created with soup_address_new_from_sockaddr() or
+ * soup_address_new_any().)
+ *
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * resolution. @callback will still be invoked in this case, with a
+ * status of %SOUP_STATUS_CANCELLED.
+ *
+ * It is safe to call this more than once on a given address, from the
+ * same thread, with the same @async_context (and doing so will not
+ * result in redundant DNS queries being made). But it is not safe to
+ * call from multiple threads, or with different @async_contexts, or
+ * mixed with calls to soup_address_resolve_sync().
+ **/
+void
+soup_address_resolve_async (SoupAddress *addr, GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupAddressCallback callback, gpointer user_data)
+{
+ SoupAddressPrivate *priv;
+ SoupAddressResolveAsyncData *res_data;
+ GResolver *resolver;
+ gboolean already_started;
+
+ g_return_if_fail (SOUP_IS_ADDRESS (addr));
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ g_return_if_fail (priv->name || priv->sockaddr);
+
+ /* We don't need to do locking here because the async case is
+ * not intended to be thread-safe.
+ */
+
+ if (priv->name && priv->sockaddr && !callback)
+ return;
+
+ res_data = g_slice_new0 (SoupAddressResolveAsyncData);
+ res_data->callback = callback;
+ res_data->callback_data = user_data;
+
+ already_started = priv->async_lookups != NULL;
+ priv->async_lookups = g_slist_prepend (priv->async_lookups, res_data);
+
+ if (already_started)
+ return;
+
+ g_object_ref (addr);
+
+ if (priv->name && priv->sockaddr) {
+ soup_add_completion (async_context, idle_complete_resolve, addr);
+ return;
+ }
+
+ resolver = g_resolver_get_default ();
+ if (async_context)
+ g_main_context_push_thread_default (async_context);
+
+ if (priv->name) {
+ g_resolver_lookup_by_name_async (resolver, priv->name,
+ cancellable,
+ lookup_resolved, addr);
+ } else {
+ GInetAddress *gia;
+
+ gia = soup_address_make_inet_address (addr);
+ g_resolver_lookup_by_address_async (resolver, gia,
+ cancellable,
+ lookup_resolved, addr);
+ g_object_unref (gia);
+ }
+
+ if (async_context)
+ g_main_context_pop_thread_default (async_context);
+ g_object_unref (resolver);
+}
+
+static guint
+resolve_sync_internal (SoupAddress *addr, GCancellable *cancellable, GError **error)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ GResolver *resolver;
+ guint status;
+ GError *my_err = NULL;
+
+ resolver = g_resolver_get_default ();
+
+ /* We could optimize this to avoid multiple lookups the same
+ * way _resolve_async does, but we don't currently. So, first
+ * lock the mutex to ensure we have a consistent view of
+ * priv->sockaddr and priv->name, unlock it around the
+ * blocking op, and then re-lock it to modify @addr.
+ */
+ g_mutex_lock (priv->lock);
+ if (!priv->sockaddr) {
+ GList *addrs;
+
+ g_mutex_unlock (priv->lock);
+ addrs = g_resolver_lookup_by_name (resolver, priv->name,
+ cancellable, &my_err);
+ g_mutex_lock (priv->lock);
+
+ status = update_addrs (addr, addrs, my_err);
+ g_resolver_free_addresses (addrs);
+ } else if (!priv->name) {
+ GInetAddress *gia;
+ char *name;
+
+ g_mutex_unlock (priv->lock);
+ gia = soup_address_make_inet_address (addr);
+ name = g_resolver_lookup_by_address (resolver, gia,
+ cancellable, &my_err);
+ g_object_unref (gia);
+ g_mutex_lock (priv->lock);
+
+ status = update_name (addr, name, my_err);
+ g_free (name);
+ } else
+ status = SOUP_STATUS_OK;
+ g_mutex_unlock (priv->lock);
+
+ if (my_err)
+ g_propagate_error (error, my_err);
+ g_object_unref (resolver);
+
+ return status;
+}
+
+/**
+ * soup_address_resolve_sync:
+ * @addr: a #SoupAddress
+ * @cancellable: a #GCancellable object, or %NULL
+ *
+ * Synchronously resolves the missing half of @addr, as with
+ * soup_address_resolve_async().
+ *
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * resolution. soup_address_resolve_sync() will then return a status
+ * of %SOUP_STATUS_CANCELLED.
+ *
+ * It is safe to call this more than once, even from different
+ * threads, but it is not safe to mix calls to
+ * soup_address_resolve_sync() with calls to
+ * soup_address_resolve_async() on the same address.
+ *
+ * Return value: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or
+ * %SOUP_STATUS_CANCELLED.
+ **/
+guint
+soup_address_resolve_sync (SoupAddress *addr, GCancellable *cancellable)
+{
+ SoupAddressPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_ADDRESS (addr), SOUP_STATUS_MALFORMED);
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ g_return_val_if_fail (priv->name || priv->sockaddr, SOUP_STATUS_MALFORMED);
+
+ return resolve_sync_internal (addr, cancellable, NULL);
+}
+
+/**
+ * soup_address_is_resolved:
+ * @addr: a #SoupAddress
+ *
+ * Tests if @addr has already been resolved. Unlike the other
+ * #SoupAddress "get" methods, this is safe to call when @addr might
+ * be being resolved in another thread.
+ *
+ * Return value: %TRUE if @addr has been resolved.
+ **/
+gboolean
+soup_address_is_resolved (SoupAddress *addr)
+{
+ SoupAddressPrivate *priv;
+ gboolean resolved;
+
+ g_return_val_if_fail (SOUP_IS_ADDRESS (addr), FALSE);
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ g_mutex_lock (priv->lock);
+ resolved = priv->sockaddr && priv->name;
+ g_mutex_unlock (priv->lock);
+
+ return resolved;
+}
+
+/**
+ * soup_address_hash_by_name:
+ * @addr: (type Soup.Address): a #SoupAddress
+ *
+ * A hash function (for #GHashTable) that corresponds to
+ * soup_address_equal_by_name(), qv
+ *
+ * Return value: the named-based hash value for @addr.
+ *
+ * Since: 2.26
+ **/
+guint
+soup_address_hash_by_name (gconstpointer addr)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ g_return_val_if_fail (priv->name != NULL, 0);
+ return g_str_hash (priv->name);
+}
+
+/**
+ * soup_address_equal_by_name:
+ * @addr1: (type Soup.Address): a #SoupAddress with a resolved name
+ * @addr2: (type Soup.Address): another #SoupAddress with a resolved
+ * name
+ *
+ * Tests if @addr1 and @addr2 have the same "name". This method can be
+ * used with soup_address_hash_by_name() to create a #GHashTable that
+ * hashes on address "names".
+ *
+ * Comparing by name normally means comparing the addresses by their
+ * hostnames. But if the address was originally created using an IP
+ * address literal, then it will be compared by that instead.
+ *
+ * In particular, if "www.example.com" has the IP address 10.0.0.1,
+ * and @addr1 was created with the name "www.example.com" and @addr2
+ * was created with the name "10.0.0.1", then they will compare as
+ * unequal for purposes of soup_address_equal_by_name().
+ *
+ * This would be used to distinguish hosts in situations where
+ * different virtual hosts on the same IP address should be considered
+ * different. Eg, for purposes of HTTP authentication or cookies, two
+ * hosts with the same IP address but different names are considered
+ * to be different hosts.
+ *
+ * See also soup_address_equal_by_ip(), which compares by IP address
+ * rather than by name.
+ *
+ * Return value: whether or not @addr1 and @addr2 have the same name
+ *
+ * Since: 2.26
+ **/
+gboolean
+soup_address_equal_by_name (gconstpointer addr1, gconstpointer addr2)
+{
+ SoupAddressPrivate *priv1 = SOUP_ADDRESS_GET_PRIVATE (addr1);
+ SoupAddressPrivate *priv2 = SOUP_ADDRESS_GET_PRIVATE (addr2);
+
+ g_return_val_if_fail (priv1->name != NULL, FALSE);
+ g_return_val_if_fail (priv2->name != NULL, FALSE);
+ return !g_ascii_strcasecmp (priv1->name, priv2->name);
+}
+
+/**
+ * soup_address_hash_by_ip:
+ * @addr: (type Soup.Address): a #SoupAddress
+ *
+ * A hash function (for #GHashTable) that corresponds to
+ * soup_address_equal_by_ip(), qv
+ *
+ * Return value: the IP-based hash value for @addr.
+ *
+ * Since: 2.26
+ **/
+guint
+soup_address_hash_by_ip (gconstpointer addr)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ guint hash;
+
+ g_return_val_if_fail (priv->sockaddr != NULL, 0);
+
+ memcpy (&hash, SOUP_ADDRESS_GET_DATA (priv),
+ MIN (sizeof (hash), SOUP_ADDRESS_FAMILY_DATA_SIZE (priv->sockaddr->ss_family)));
+ return hash;
+}
+
+/**
+ * soup_address_equal_by_ip:
+ * @addr1: (type Soup.Address): a #SoupAddress with a resolved IP
+ * address
+ * @addr2: (type Soup.Address): another #SoupAddress with a resolved
+ * IP address
+ *
+ * Tests if @addr1 and @addr2 have the same IP address. This method
+ * can be used with soup_address_hash_by_ip() to create a
+ * #GHashTable that hashes on IP address.
+ *
+ * This would be used to distinguish hosts in situations where
+ * different virtual hosts on the same IP address should be considered
+ * the same. Eg, if "www.example.com" and "www.example.net" have the
+ * same IP address, then a single #SoupConnection can be used to talk
+ * to either of them.
+ *
+ * See also soup_address_equal_by_name(), which compares by name
+ * rather than by IP address.
+ *
+ * Return value: whether or not @addr1 and @addr2 have the same IP
+ * address.
+ *
+ * Since: 2.26
+ **/
+gboolean
+soup_address_equal_by_ip (gconstpointer addr1, gconstpointer addr2)
+{
+ SoupAddressPrivate *priv1 = SOUP_ADDRESS_GET_PRIVATE (addr1);
+ SoupAddressPrivate *priv2 = SOUP_ADDRESS_GET_PRIVATE (addr2);
+ int size;
+
+ g_return_val_if_fail (priv1->sockaddr != NULL, FALSE);
+ g_return_val_if_fail (priv2->sockaddr != NULL, FALSE);
+
+ size = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (priv1->sockaddr->ss_family);
+ return (priv1->sockaddr->ss_family ==
+ priv2->sockaddr->ss_family &&
+ !memcmp (priv1->sockaddr, priv2->sockaddr, size));
+}
+
+
+#define SOUP_TYPE_ADDRESS_ADDRESS_ENUMERATOR (_soup_address_address_enumerator_get_type ())
+#define SOUP_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_ADDRESS_ADDRESS_ENUMERATOR, SoupAddressAddressEnumerator))
+
+typedef struct {
+ GSocketAddressEnumerator parent_instance;
+
+ SoupAddress *addr;
+ int orig_offset;
+ int n;
+} SoupAddressAddressEnumerator;
+
+typedef struct {
+ GSocketAddressEnumeratorClass parent_class;
+
+} SoupAddressAddressEnumeratorClass;
+
+GType _soup_address_address_enumerator_get_type (void);
+G_DEFINE_TYPE (SoupAddressAddressEnumerator, _soup_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
+
+static void
+soup_address_address_enumerator_finalize (GObject *object)
+{
+ SoupAddressAddressEnumerator *addr_enum =
+ SOUP_ADDRESS_ADDRESS_ENUMERATOR (object);
+
+ g_object_unref (addr_enum->addr);
+
+ G_OBJECT_CLASS (_soup_address_address_enumerator_parent_class)->finalize (object);
+}
+
+static GSocketAddress *
+next_address (SoupAddressAddressEnumerator *addr_enum)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
+ struct sockaddr_storage *ss;
+ int next_addr;
+
+ /* If there are two addresses but the first one is unusable
+ * (eg, it's IPv6 and we can only do IPv4), then we don't want to
+ * try the bad one every time. So we use priv->offset to remember
+ * the offset of the first usable address (ie, the first address
+ * that we weren't called again after returning).
+ */
+ next_addr = (addr_enum->orig_offset + addr_enum->n) % priv->n_addrs;
+ priv->offset = next_addr;
+
+ if (addr_enum->n >= priv->n_addrs)
+ return NULL;
+ addr_enum->n++;
+
+ ss = &priv->sockaddr[next_addr];
+ return g_socket_address_new_from_native (ss, SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (ss->ss_family));
+}
+
+static GSocketAddress *
+soup_address_address_enumerator_next (GSocketAddressEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupAddressAddressEnumerator *addr_enum =
+ SOUP_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
+
+ if (!priv->sockaddr) {
+ if (resolve_sync_internal (addr_enum->addr, cancellable, error) != SOUP_STATUS_OK)
+ return NULL;
+ }
+
+ return next_address (addr_enum);
+}
+
+static void
+got_addresses (SoupAddress *addr, guint status, gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ GError *error;
+
+ error = g_object_get_data (G_OBJECT (addr), "async-resolved-error");
+ if (error)
+ g_simple_async_result_set_from_error (simple, error);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+soup_address_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupAddressAddressEnumerator *addr_enum =
+ SOUP_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (G_OBJECT (enumerator),
+ callback, user_data,
+ soup_address_address_enumerator_next_async);
+
+ if (!priv->sockaddr) {
+ soup_address_resolve_async (addr_enum->addr, NULL, cancellable,
+ got_addresses, simple);
+ } else {
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+}
+
+static GSocketAddress *
+soup_address_address_enumerator_next_finish (GSocketAddressEnumerator *enumerator,
+ GAsyncResult *result,
+ GError **error)
+{
+ SoupAddressAddressEnumerator *addr_enum =
+ SOUP_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+ else
+ return next_address (addr_enum);
+}
+
+static void
+_soup_address_address_enumerator_init (SoupAddressAddressEnumerator *enumerator)
+{
+}
+
+static void
+_soup_address_address_enumerator_class_init (SoupAddressAddressEnumeratorClass *addrenum_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (addrenum_class);
+ GSocketAddressEnumeratorClass *enumerator_class =
+ G_SOCKET_ADDRESS_ENUMERATOR_CLASS (addrenum_class);
+
+ enumerator_class->next = soup_address_address_enumerator_next;
+ enumerator_class->next_async = soup_address_address_enumerator_next_async;
+ enumerator_class->next_finish = soup_address_address_enumerator_next_finish;
+ object_class->finalize = soup_address_address_enumerator_finalize;
+}
+
+static GSocketAddressEnumerator *
+soup_address_connectable_enumerate (GSocketConnectable *connectable)
+{
+ SoupAddressAddressEnumerator *addr_enum;
+ SoupAddressPrivate *priv;
+
+ addr_enum = g_object_new (SOUP_TYPE_ADDRESS_ADDRESS_ENUMERATOR, NULL);
+ addr_enum->addr = g_object_ref (connectable);
+
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
+ addr_enum->orig_offset = priv->offset;
+
+ return (GSocketAddressEnumerator *)addr_enum;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_ADDRESS_H
+#define SOUP_ADDRESS_H
+
+#include <sys/types.h>
+
+#include <libsoup/soup-portability.h>
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_ADDRESS (soup_address_get_type ())
+#define SOUP_ADDRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_ADDRESS, SoupAddress))
+#define SOUP_ADDRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_ADDRESS, SoupAddressClass))
+#define SOUP_IS_ADDRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_ADDRESS))
+#define SOUP_IS_ADDRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_ADDRESS))
+#define SOUP_ADDRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_ADDRESS, SoupAddressClass))
+
+struct _SoupAddress {
+ GObject parent;
+
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupAddressClass;
+
+#define SOUP_ADDRESS_NAME "name"
+#define SOUP_ADDRESS_FAMILY "family"
+#define SOUP_ADDRESS_PORT "port"
+#define SOUP_ADDRESS_PHYSICAL "physical"
+#define SOUP_ADDRESS_SOCKADDR "sockaddr"
+
+typedef enum {
+ SOUP_ADDRESS_FAMILY_INVALID = -1,
+
+ SOUP_ADDRESS_FAMILY_IPV4 = G_SOCKET_FAMILY_IPV4,
+ SOUP_ADDRESS_FAMILY_IPV6 = G_SOCKET_FAMILY_IPV6
+} SoupAddressFamily;
+
+#define SOUP_ADDRESS_ANY_PORT 0
+
+typedef void (*SoupAddressCallback) (SoupAddress *addr,
+ guint status,
+ gpointer user_data);
+
+GType soup_address_get_type (void);
+
+SoupAddress *soup_address_new (const char *name,
+ guint port);
+SoupAddress *soup_address_new_from_sockaddr (struct sockaddr *sa,
+ int len);
+SoupAddress *soup_address_new_any (SoupAddressFamily family,
+ guint port);
+
+void soup_address_resolve_async (SoupAddress *addr,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupAddressCallback callback,
+ gpointer user_data);
+guint soup_address_resolve_sync (SoupAddress *addr,
+ GCancellable *cancellable);
+
+const char *soup_address_get_name (SoupAddress *addr);
+const char *soup_address_get_physical (SoupAddress *addr);
+guint soup_address_get_port (SoupAddress *addr);
+struct sockaddr *soup_address_get_sockaddr (SoupAddress *addr,
+ int *len);
+GSocketAddress *soup_address_get_gsockaddr (SoupAddress *addr);
+gboolean soup_address_is_resolved (SoupAddress *addr);
+
+guint soup_address_hash_by_name (gconstpointer addr);
+gboolean soup_address_equal_by_name (gconstpointer addr1,
+ gconstpointer addr2);
+guint soup_address_hash_by_ip (gconstpointer addr);
+gboolean soup_address_equal_by_ip (gconstpointer addr1,
+ gconstpointer addr2);
+
+
+G_END_DECLS
+
+#endif /* SOUP_ADDRESS_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-basic.c: HTTP Basic Authentication
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-basic.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-uri.h"
+
+static gboolean update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params);
+static GSList *get_protection_space (SoupAuth *auth, SoupURI *source_uri);
+static void authenticate (SoupAuth *auth, const char *username, const char *password);
+static gboolean is_authenticated (SoupAuth *auth);
+static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
+
+typedef struct {
+ char *token;
+} SoupAuthBasicPrivate;
+#define SOUP_AUTH_BASIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_BASIC, SoupAuthBasicPrivate))
+
+G_DEFINE_TYPE (SoupAuthBasic, soup_auth_basic, SOUP_TYPE_AUTH)
+
+static void
+soup_auth_basic_init (SoupAuthBasic *basic)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthBasicPrivate *priv = SOUP_AUTH_BASIC_GET_PRIVATE (object);
+
+ g_free (priv->token);
+
+ G_OBJECT_CLASS (soup_auth_basic_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_basic_class_init (SoupAuthBasicClass *auth_basic_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_basic_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (auth_basic_class);
+
+ g_type_class_add_private (auth_basic_class, sizeof (SoupAuthBasicPrivate));
+
+ auth_class->scheme_name = "Basic";
+ auth_class->strength = 1;
+
+ auth_class->update = update;
+ auth_class->get_protection_space = get_protection_space;
+ auth_class->authenticate = authenticate;
+ auth_class->is_authenticated = is_authenticated;
+ auth_class->get_authorization = get_authorization;
+
+ object_class->finalize = finalize;
+}
+
+
+static gboolean
+update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params)
+{
+ SoupAuthBasicPrivate *priv = SOUP_AUTH_BASIC_GET_PRIVATE (auth);
+
+ /* If we're updating a pre-existing auth, the
+ * username/password must be bad now, so forget it.
+ * Other than that, there's nothing to do here.
+ */
+ if (priv->token) {
+ memset (priv->token, 0, strlen (priv->token));
+ g_free (priv->token);
+ priv->token = NULL;
+ }
+
+ return TRUE;
+}
+
+static GSList *
+get_protection_space (SoupAuth *auth, SoupURI *source_uri)
+{
+ char *space, *p;
+
+ space = g_strdup (source_uri->path);
+
+ /* Strip query and filename component */
+ p = strrchr (space, '/');
+ if (p && p != space && p[1])
+ *p = '\0';
+
+ return g_slist_prepend (NULL, space);
+}
+
+static void
+authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+ SoupAuthBasicPrivate *priv = SOUP_AUTH_BASIC_GET_PRIVATE (auth);
+ char *user_pass;
+ int len;
+
+ user_pass = g_strdup_printf ("%s:%s", username, password);
+ len = strlen (user_pass);
+
+ if (priv->token) {
+ memset (priv->token, 0, strlen (priv->token));
+ g_free (priv->token);
+ }
+ priv->token = g_base64_encode ((guchar *)user_pass, len);
+
+ memset (user_pass, 0, len);
+ g_free (user_pass);
+}
+
+static gboolean
+is_authenticated (SoupAuth *auth)
+{
+ return SOUP_AUTH_BASIC_GET_PRIVATE (auth)->token != NULL;
+}
+
+static char *
+get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+ SoupAuthBasicPrivate *priv = SOUP_AUTH_BASIC_GET_PRIVATE (auth);
+
+ return g_strdup_printf ("Basic %s", priv->token);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_AUTH_BASIC_H
+#define SOUP_AUTH_BASIC_H 1
+
+#include "soup-auth.h"
+
+#define SOUP_AUTH_BASIC(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_BASIC, SoupAuthBasic))
+#define SOUP_AUTH_BASIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_BASIC, SoupAuthBasicClass))
+#define SOUP_IS_AUTH_BASIC(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_BASIC))
+#define SOUP_IS_AUTH_BASIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_BASIC))
+#define SOUP_AUTH_BASIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_BASIC, SoupAuthBasicClass))
+
+typedef struct {
+ SoupAuth parent;
+
+} SoupAuthBasic;
+
+typedef struct {
+ SoupAuthClass parent_class;
+
+} SoupAuthBasicClass;
+
+#endif /*SOUP_AUTH_BASIC_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-digest.c: HTTP Digest Authentication
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "soup-auth-digest.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-misc.h"
+#include "soup-uri.h"
+
+static gboolean update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params);
+static GSList *get_protection_space (SoupAuth *auth, SoupURI *source_uri);
+static void authenticate (SoupAuth *auth, const char *username, const char *password);
+static gboolean is_authenticated (SoupAuth *auth);
+static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
+
+typedef struct {
+ char *user;
+ char hex_urp[33];
+ char hex_a1[33];
+
+ /* These are provided by the server */
+ char *nonce;
+ char *opaque;
+ SoupAuthDigestQop qop_options;
+ SoupAuthDigestAlgorithm algorithm;
+ char *domain;
+
+ /* These are generated by the client */
+ char *cnonce;
+ int nc;
+ SoupAuthDigestQop qop;
+} SoupAuthDigestPrivate;
+#define SOUP_AUTH_DIGEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigestPrivate))
+
+static void recompute_hex_a1 (SoupAuthDigestPrivate *priv);
+
+G_DEFINE_TYPE (SoupAuthDigest, soup_auth_digest, SOUP_TYPE_AUTH)
+
+static void
+soup_auth_digest_init (SoupAuthDigest *digest)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (object);
+
+ if (priv->user)
+ g_free (priv->user);
+ if (priv->nonce)
+ g_free (priv->nonce);
+ if (priv->domain)
+ g_free (priv->domain);
+ if (priv->cnonce)
+ g_free (priv->cnonce);
+
+ memset (priv->hex_urp, 0, sizeof (priv->hex_urp));
+ memset (priv->hex_a1, 0, sizeof (priv->hex_a1));
+
+ G_OBJECT_CLASS (soup_auth_digest_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_digest_class_init (SoupAuthDigestClass *auth_digest_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_digest_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (auth_digest_class);
+
+ g_type_class_add_private (auth_digest_class, sizeof (SoupAuthDigestPrivate));
+
+ auth_class->scheme_name = "Digest";
+ auth_class->strength = 5;
+
+ auth_class->get_protection_space = get_protection_space;
+ auth_class->update = update;
+ auth_class->authenticate = authenticate;
+ auth_class->is_authenticated = is_authenticated;
+ auth_class->get_authorization = get_authorization;
+
+ object_class->finalize = finalize;
+}
+
+SoupAuthDigestAlgorithm
+soup_auth_digest_parse_algorithm (const char *algorithm)
+{
+ if (!algorithm || !g_ascii_strcasecmp (algorithm, "MD5"))
+ return SOUP_AUTH_DIGEST_ALGORITHM_MD5;
+ else if (!g_ascii_strcasecmp (algorithm, "MD5-sess"))
+ return SOUP_AUTH_DIGEST_ALGORITHM_MD5_SESS;
+ else
+ return -1;
+}
+
+char *
+soup_auth_digest_get_algorithm (SoupAuthDigestAlgorithm algorithm)
+{
+ if (algorithm == SOUP_AUTH_DIGEST_ALGORITHM_MD5)
+ return g_strdup ("MD5");
+ else if (algorithm == SOUP_AUTH_DIGEST_ALGORITHM_MD5_SESS)
+ return g_strdup ("MD5-sess");
+ else
+ return NULL;
+}
+
+SoupAuthDigestQop
+soup_auth_digest_parse_qop (const char *qop)
+{
+ GSList *qop_values, *iter;
+ SoupAuthDigestQop out = 0;
+
+ g_return_val_if_fail (qop != NULL, 0);
+
+ qop_values = soup_header_parse_list (qop);
+ for (iter = qop_values; iter; iter = iter->next) {
+ if (!g_ascii_strcasecmp (iter->data, "auth"))
+ out |= SOUP_AUTH_DIGEST_QOP_AUTH;
+ else if (!g_ascii_strcasecmp (iter->data, "auth-int"))
+ out |= SOUP_AUTH_DIGEST_QOP_AUTH_INT;
+ }
+ soup_header_free_list (qop_values);
+
+ return out;
+}
+
+char *
+soup_auth_digest_get_qop (SoupAuthDigestQop qop)
+{
+ GString *out;
+
+ out = g_string_new (NULL);
+ if (qop & SOUP_AUTH_DIGEST_QOP_AUTH)
+ g_string_append (out, "auth");
+ if (qop & SOUP_AUTH_DIGEST_QOP_AUTH_INT) {
+ if (qop & SOUP_AUTH_DIGEST_QOP_AUTH)
+ g_string_append (out, ",");
+ g_string_append (out, "auth-int");
+ }
+
+ return g_string_free (out, FALSE);
+}
+
+static gboolean
+update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params)
+{
+ SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
+ const char *stale, *qop;
+ guint qop_options;
+ gboolean ok = TRUE;
+
+ g_free (priv->domain);
+ g_free (priv->nonce);
+ g_free (priv->opaque);
+
+ priv->nc = 1;
+
+ priv->domain = g_strdup (g_hash_table_lookup (auth_params, "domain"));
+ priv->nonce = g_strdup (g_hash_table_lookup (auth_params, "nonce"));
+ priv->opaque = g_strdup (g_hash_table_lookup (auth_params, "opaque"));
+
+ qop = g_hash_table_lookup (auth_params, "qop");
+ if (qop) {
+ qop_options = soup_auth_digest_parse_qop (qop);
+ /* We only support auth */
+ if (!(qop_options & SOUP_AUTH_DIGEST_QOP_AUTH))
+ ok = FALSE;
+ priv->qop = SOUP_AUTH_DIGEST_QOP_AUTH;
+ } else
+ priv->qop = 0;
+
+ priv->algorithm = soup_auth_digest_parse_algorithm (g_hash_table_lookup (auth_params, "algorithm"));
+ if (priv->algorithm == -1)
+ ok = FALSE;
+
+ stale = g_hash_table_lookup (auth_params, "stale");
+ if (stale && !g_ascii_strcasecmp (stale, "TRUE") && *priv->hex_urp)
+ recompute_hex_a1 (priv);
+ else {
+ g_free (priv->user);
+ priv->user = NULL;
+ g_free (priv->cnonce);
+ priv->cnonce = NULL;
+ memset (priv->hex_urp, 0, sizeof (priv->hex_urp));
+ memset (priv->hex_a1, 0, sizeof (priv->hex_a1));
+ }
+
+ return ok;
+}
+
+static GSList *
+get_protection_space (SoupAuth *auth, SoupURI *source_uri)
+{
+ SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
+ GSList *space = NULL;
+ SoupURI *uri;
+ char **dvec, *d, *dir, *slash;
+ int dix;
+
+ if (!priv->domain || !*priv->domain) {
+ /* If no domain directive, the protection space is the
+ * whole server.
+ */
+ return g_slist_prepend (NULL, g_strdup (""));
+ }
+
+ dvec = g_strsplit (priv->domain, " ", 0);
+ for (dix = 0; dvec[dix] != NULL; dix++) {
+ d = dvec[dix];
+ if (*d == '/')
+ dir = g_strdup (d);
+ else {
+ uri = soup_uri_new (d);
+ if (uri && uri->scheme == source_uri->scheme &&
+ uri->port == source_uri->port &&
+ !strcmp (uri->host, source_uri->host))
+ dir = g_strdup (uri->path);
+ else
+ dir = NULL;
+ if (uri)
+ soup_uri_free (uri);
+ }
+
+ if (dir) {
+ slash = strrchr (dir, '/');
+ if (slash && !slash[1])
+ *slash = '\0';
+
+ space = g_slist_prepend (space, dir);
+ }
+ }
+ g_strfreev (dvec);
+
+ return space;
+}
+
+void
+soup_auth_digest_compute_hex_urp (const char *username,
+ const char *realm,
+ const char *password,
+ char hex_urp[33])
+{
+ GChecksum *checksum;
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (guchar *)username, strlen (username));
+ g_checksum_update (checksum, (guchar *)":", 1);
+ g_checksum_update (checksum, (guchar *)realm, strlen (realm));
+ g_checksum_update (checksum, (guchar *)":", 1);
+ g_checksum_update (checksum, (guchar *)password, strlen (password));
+ strncpy (hex_urp, g_checksum_get_string (checksum), 33);
+ g_checksum_free (checksum);
+}
+
+void
+soup_auth_digest_compute_hex_a1 (const char *hex_urp,
+ SoupAuthDigestAlgorithm algorithm,
+ const char *nonce,
+ const char *cnonce,
+ char hex_a1[33])
+{
+ if (algorithm == SOUP_AUTH_DIGEST_ALGORITHM_MD5) {
+ /* In MD5, A1 is just user:realm:password, so hex_A1
+ * is just hex_urp.
+ */
+ /* You'd think you could say "sizeof (hex_a1)" here,
+ * but you'd be wrong.
+ */
+ memcpy (hex_a1, hex_urp, 33);
+ } else {
+ GChecksum *checksum;
+
+ /* In MD5-sess, A1 is hex_urp:nonce:cnonce */
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (guchar *)hex_urp, strlen (hex_urp));
+ g_checksum_update (checksum, (guchar *)":", 1);
+ g_checksum_update (checksum, (guchar *)nonce, strlen (nonce));
+ g_checksum_update (checksum, (guchar *)":", 1);
+ g_checksum_update (checksum, (guchar *)cnonce, strlen (cnonce));
+ strncpy (hex_a1, g_checksum_get_string (checksum), 33);
+ g_checksum_free (checksum);
+ }
+}
+
+static void
+recompute_hex_a1 (SoupAuthDigestPrivate *priv)
+{
+ soup_auth_digest_compute_hex_a1 (priv->hex_urp,
+ priv->algorithm,
+ priv->nonce,
+ priv->cnonce,
+ priv->hex_a1);
+}
+
+static void
+authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+ SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
+ char *bgen;
+
+ /* Create client nonce */
+ bgen = g_strdup_printf ("%p:%lu:%lu",
+ auth,
+ (unsigned long) getpid (),
+ (unsigned long) time (0));
+ priv->cnonce = g_base64_encode ((guchar *)bgen, strlen (bgen));
+ g_free (bgen);
+
+ priv->user = g_strdup (username);
+
+ /* compute "URP" (user:realm:password) */
+ soup_auth_digest_compute_hex_urp (username, auth->realm,
+ password ? password : "",
+ priv->hex_urp);
+
+ /* And compute A1 from that */
+ recompute_hex_a1 (priv);
+}
+
+static gboolean
+is_authenticated (SoupAuth *auth)
+{
+ return SOUP_AUTH_DIGEST_GET_PRIVATE (auth)->cnonce != NULL;
+}
+
+void
+soup_auth_digest_compute_response (const char *method,
+ const char *uri,
+ const char *hex_a1,
+ SoupAuthDigestQop qop,
+ const char *nonce,
+ const char *cnonce,
+ int nc,
+ char response[33])
+{
+ char hex_a2[33];
+ GChecksum *checksum;
+
+ /* compute A2 */
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (guchar *)method, strlen (method));
+ g_checksum_update (checksum, (guchar *)":", 1);
+ g_checksum_update (checksum, (guchar *)uri, strlen (uri));
+ strncpy (hex_a2, g_checksum_get_string (checksum), 33);
+ g_checksum_free (checksum);
+
+ /* compute KD */
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (guchar *)hex_a1, strlen (hex_a1));
+ g_checksum_update (checksum, (guchar *)":", 1);
+ g_checksum_update (checksum, (guchar *)nonce, strlen (nonce));
+ g_checksum_update (checksum, (guchar *)":", 1);
+
+ if (qop) {
+ char tmp[9];
+
+ snprintf (tmp, 9, "%.8x", nc);
+ g_checksum_update (checksum, (guchar *)tmp, strlen (tmp));
+ g_checksum_update (checksum, (guchar *)":", 1);
+ g_checksum_update (checksum, (guchar *)cnonce, strlen (cnonce));
+ g_checksum_update (checksum, (guchar *)":", 1);
+
+ if (!(qop & SOUP_AUTH_DIGEST_QOP_AUTH))
+ g_warn_if_reached ();
+ g_checksum_update (checksum, (guchar *)"auth", strlen ("auth"));
+ g_checksum_update (checksum, (guchar *)":", 1);
+ }
+
+ g_checksum_update (checksum, (guchar *)hex_a2, 32);
+ strncpy (response, g_checksum_get_string (checksum), 33);
+ g_checksum_free (checksum);
+}
+
+static void
+authentication_info_cb (SoupMessage *msg, gpointer data)
+{
+ SoupAuth *auth = data;
+ SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
+ const char *header;
+ GHashTable *auth_params;
+ char *nextnonce;
+
+ if (auth != soup_message_get_auth (msg))
+ return;
+
+ header = soup_message_headers_get_one (msg->response_headers,
+ soup_auth_is_for_proxy (auth) ?
+ "Proxy-Authentication-Info" :
+ "Authentication-Info");
+ g_return_if_fail (header != NULL);
+
+ auth_params = soup_header_parse_param_list (header);
+ if (!auth_params)
+ return;
+
+ nextnonce = g_strdup (g_hash_table_lookup (auth_params, "nextnonce"));
+ if (nextnonce) {
+ g_free (priv->nonce);
+ priv->nonce = nextnonce;
+ }
+
+ soup_header_free_param_list (auth_params);
+}
+
+static char *
+get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+ SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
+ char response[33], *token;
+ char *url, *algorithm;
+ GString *out;
+ SoupURI *uri;
+
+ uri = soup_message_get_uri (msg);
+ g_return_val_if_fail (uri != NULL, NULL);
+ url = soup_uri_to_string (uri, TRUE);
+
+ soup_auth_digest_compute_response (msg->method, url, priv->hex_a1,
+ priv->qop, priv->nonce,
+ priv->cnonce, priv->nc,
+ response);
+
+ out = g_string_new ("Digest ");
+
+ soup_header_g_string_append_param_quoted (out, "username", priv->user);
+ g_string_append (out, ", ");
+ soup_header_g_string_append_param_quoted (out, "realm", auth->realm);
+ g_string_append (out, ", ");
+ soup_header_g_string_append_param_quoted (out, "nonce", priv->nonce);
+ g_string_append (out, ", ");
+ soup_header_g_string_append_param_quoted (out, "uri", url);
+ g_string_append (out, ", ");
+ algorithm = soup_auth_digest_get_algorithm (priv->algorithm);
+ g_string_append_printf (out, "algorithm=%s", algorithm);
+ g_free (algorithm);
+ g_string_append (out, ", ");
+ soup_header_g_string_append_param_quoted (out, "response", response);
+
+ if (priv->opaque) {
+ g_string_append (out, ", ");
+ soup_header_g_string_append_param_quoted (out, "opaque", priv->opaque);
+ }
+
+ if (priv->qop) {
+ char *qop = soup_auth_digest_get_qop (priv->qop);
+
+ g_string_append (out, ", ");
+ soup_header_g_string_append_param_quoted (out, "cnonce", priv->cnonce);
+ g_string_append_printf (out, ", nc=%.8x, qop=%s",
+ priv->nc, qop);
+ g_free (qop);
+ }
+
+ g_free (url);
+
+ priv->nc++;
+
+ token = g_string_free (out, FALSE);
+
+ soup_message_add_header_handler (msg,
+ "got_headers",
+ soup_auth_is_for_proxy (auth) ?
+ "Proxy-Authentication-Info" :
+ "Authentication-Info",
+ G_CALLBACK (authentication_info_cb),
+ auth);
+ return token;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_AUTH_DIGEST_H
+#define SOUP_AUTH_DIGEST_H 1
+
+#include "soup-auth.h"
+
+#define SOUP_AUTH_DIGEST(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigest))
+#define SOUP_AUTH_DIGEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigestClass))
+#define SOUP_IS_AUTH_DIGEST(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_DIGEST))
+#define SOUP_IS_AUTH_DIGEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_DIGEST))
+#define SOUP_AUTH_DIGEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigestClass))
+
+typedef struct {
+ SoupAuth parent;
+
+} SoupAuthDigest;
+
+typedef struct {
+ SoupAuthClass parent_class;
+
+} SoupAuthDigestClass;
+
+/* Utility routines (also used by SoupAuthDomainDigest) */
+
+typedef enum {
+ SOUP_AUTH_DIGEST_ALGORITHM_NONE,
+ SOUP_AUTH_DIGEST_ALGORITHM_MD5,
+ SOUP_AUTH_DIGEST_ALGORITHM_MD5_SESS
+} SoupAuthDigestAlgorithm;
+
+typedef enum {
+ SOUP_AUTH_DIGEST_QOP_AUTH = 1 << 0,
+ SOUP_AUTH_DIGEST_QOP_AUTH_INT = 1 << 1
+} SoupAuthDigestQop;
+
+SoupAuthDigestAlgorithm soup_auth_digest_parse_algorithm (const char *algorithm);
+char *soup_auth_digest_get_algorithm (SoupAuthDigestAlgorithm algorithm);
+
+SoupAuthDigestQop soup_auth_digest_parse_qop (const char *qop);
+char *soup_auth_digest_get_qop (SoupAuthDigestQop qop);
+
+void soup_auth_digest_compute_hex_urp (const char *username,
+ const char *realm,
+ const char *password,
+ char hex_urp[33]);
+void soup_auth_digest_compute_hex_a1 (const char *hex_urp,
+ SoupAuthDigestAlgorithm algorithm,
+ const char *nonce,
+ const char *cnonce,
+ char hex_a1[33]);
+void soup_auth_digest_compute_response (const char *method,
+ const char *uri,
+ const char *hex_a1,
+ SoupAuthDigestQop qop,
+ const char *nonce,
+ const char *cnonce,
+ int nc,
+ char response[33]);
+
+#endif /*SOUP_AUTH_DIGEST_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-domain-basic.c: HTTP Basic Authentication (server-side)
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-domain-basic.h"
+#include "soup-headers.h"
+#include "soup-marshal.h"
+#include "soup-message.h"
+
+/**
+ * SECTION:soup-auth-domain-basic
+ * @short_description: Server-side "Basic" authentication
+ *
+ * #SoupAuthDomainBasic handles the server side of HTTP "Basic" (ie,
+ * cleartext password) authentication.
+ **/
+
+enum {
+ PROP_0,
+
+ PROP_AUTH_CALLBACK,
+ PROP_AUTH_DATA,
+
+ LAST_PROP
+};
+
+typedef struct {
+ SoupAuthDomainBasicAuthCallback auth_callback;
+ gpointer auth_data;
+ GDestroyNotify auth_dnotify;
+} SoupAuthDomainBasicPrivate;
+
+#define SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasicPrivate))
+
+G_DEFINE_TYPE (SoupAuthDomainBasic, soup_auth_domain_basic, SOUP_TYPE_AUTH_DOMAIN)
+
+static char *accepts (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *header);
+static char *challenge (SoupAuthDomain *domain,
+ SoupMessage *msg);
+static gboolean check_password (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ const char *password);
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+soup_auth_domain_basic_init (SoupAuthDomainBasic *basic)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthDomainBasicPrivate *priv =
+ SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
+
+ if (priv->auth_dnotify)
+ priv->auth_dnotify (priv->auth_data);
+
+ G_OBJECT_CLASS (soup_auth_domain_basic_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_domain_basic_class_init (SoupAuthDomainBasicClass *basic_class)
+{
+ SoupAuthDomainClass *auth_domain_class =
+ SOUP_AUTH_DOMAIN_CLASS (basic_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (basic_class);
+
+ g_type_class_add_private (basic_class, sizeof (SoupAuthDomainBasicPrivate));
+
+ auth_domain_class->accepts = accepts;
+ auth_domain_class->challenge = challenge;
+ auth_domain_class->check_password = check_password;
+
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /**
+ * SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK:
+ *
+ * Alias for the #SoupAuthDomainBasic:auth-callback property.
+ * (The #SoupAuthDomainBasicAuthCallback.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_AUTH_CALLBACK,
+ g_param_spec_pointer (SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK,
+ "Authentication callback",
+ "Password-checking callback",
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA:
+ *
+ * Alias for the #SoupAuthDomainBasic:auth-data property.
+ * (The data to pass to the #SoupAuthDomainBasicAuthCallback.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_AUTH_DATA,
+ g_param_spec_pointer (SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA,
+ "Authentication callback data",
+ "Data to pass to authentication callback",
+ G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupAuthDomainBasicPrivate *priv =
+ SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_AUTH_CALLBACK:
+ priv->auth_callback = g_value_get_pointer (value);
+ break;
+ case PROP_AUTH_DATA:
+ if (priv->auth_dnotify) {
+ priv->auth_dnotify (priv->auth_data);
+ priv->auth_dnotify = NULL;
+ }
+ priv->auth_data = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupAuthDomainBasicPrivate *priv =
+ SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_AUTH_CALLBACK:
+ g_value_set_pointer (value, priv->auth_callback);
+ break;
+ case PROP_AUTH_DATA:
+ g_value_set_pointer (value, priv->auth_data);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_auth_domain_basic_new:
+ * @optname1: name of first option, or %NULL
+ * @...: option name/value pairs
+ *
+ * Creates a #SoupAuthDomainBasic. You must set the
+ * %SOUP_AUTH_DOMAIN_REALM parameter, to indicate the realm name to be
+ * returned with the authentication challenge to the client. Other
+ * parameters are optional.
+ *
+ * Return value: the new #SoupAuthDomain
+ **/
+SoupAuthDomain *
+soup_auth_domain_basic_new (const char *optname1, ...)
+{
+ SoupAuthDomain *domain;
+ va_list ap;
+
+ va_start (ap, optname1);
+ domain = (SoupAuthDomain *)g_object_new_valist (SOUP_TYPE_AUTH_DOMAIN_BASIC,
+ optname1, ap);
+ va_end (ap);
+
+ g_return_val_if_fail (soup_auth_domain_get_realm (domain) != NULL, NULL);
+
+ return domain;
+}
+
+/**
+ * SoupAuthDomainBasicAuthCallback:
+ * @domain: the domain
+ * @msg: the message being authenticated
+ * @username: the username provided by the client
+ * @password: the password provided by the client
+ * @user_data: the data passed to soup_auth_domain_basic_set_auth_callback()
+ *
+ * Callback used by #SoupAuthDomainBasic for authentication purposes.
+ * The application should verify that @username and @password and valid
+ * and return %TRUE or %FALSE.
+ *
+ * If you are maintaining your own password database (rather than
+ * using the password to authenticate against some other system like
+ * PAM or a remote server), you should make sure you know what you are
+ * doing. In particular, don't store cleartext passwords, or
+ * easily-computed hashes of cleartext passwords, even if you don't
+ * care that much about the security of your server, because users
+ * will frequently use the same password for multiple sites, and so
+ * compromising any site with a cleartext (or easily-cracked) password
+ * database may give attackers access to other more-interesting sites
+ * as well.
+ *
+ * Return value: %TRUE if @username and @password are valid
+ **/
+
+/**
+ * soup_auth_domain_basic_set_auth_callback:
+ * @domain: the domain
+ * @callback: the callback
+ * @user_data: data to pass to @auth_callback
+ * @dnotify: destroy notifier to free @user_data when @domain
+ * is destroyed
+ *
+ * Sets the callback that @domain will use to authenticate incoming
+ * requests. For each request containing authorization, @domain will
+ * invoke the callback, and then either accept or reject the request
+ * based on @callback's return value.
+ *
+ * You can also set the auth callback by setting the
+ * %SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK and
+ * %SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA properties, which can also be
+ * used to set the callback at construct time.
+ **/
+void
+soup_auth_domain_basic_set_auth_callback (SoupAuthDomain *domain,
+ SoupAuthDomainBasicAuthCallback callback,
+ gpointer user_data,
+ GDestroyNotify dnotify)
+{
+ SoupAuthDomainBasicPrivate *priv =
+ SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (domain);
+
+ if (priv->auth_dnotify)
+ priv->auth_dnotify (priv->auth_data);
+
+ priv->auth_callback = callback;
+ priv->auth_data = user_data;
+ priv->auth_dnotify = dnotify;
+
+ g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK);
+ g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA);
+}
+
+static void
+pw_free (char *pw)
+{
+ memset (pw, 0, strlen (pw));
+ g_free (pw);
+}
+
+static gboolean
+parse_basic (SoupMessage *msg, const char *header,
+ char **username, char **password)
+{
+ char *decoded, *colon;
+ gsize len, plen;
+
+ if (strncmp (header, "Basic ", 6) != 0)
+ return FALSE;
+
+ decoded = (char *)g_base64_decode (header + 6, &len);
+ if (!decoded)
+ return FALSE;
+
+ colon = memchr (decoded, ':', len);
+ if (!colon) {
+ pw_free (decoded);
+ return FALSE;
+ }
+ *colon = '\0';
+ plen = len - (colon - decoded) - 1;
+
+ *password = g_strndup (colon + 1, plen);
+ memset (colon + 1, 0, plen);
+ *username = decoded;
+ return TRUE;
+}
+
+static char *
+accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
+{
+ SoupAuthDomainBasicPrivate *priv =
+ SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (domain);
+ char *username, *password;
+ gboolean ok = FALSE;
+
+ if (!parse_basic (msg, header, &username, &password))
+ return NULL;
+
+ if (priv->auth_callback) {
+ ok = priv->auth_callback (domain, msg, username, password,
+ priv->auth_data);
+ } else {
+ ok = soup_auth_domain_try_generic_auth_callback (
+ domain, msg, username);
+ }
+
+ pw_free (password);
+
+ if (ok)
+ return username;
+ else {
+ g_free (username);
+ return NULL;
+ }
+}
+
+static char *
+challenge (SoupAuthDomain *domain, SoupMessage *msg)
+{
+ GString *challenge;
+
+ challenge = g_string_new ("Basic ");
+ soup_header_g_string_append_param (challenge, "realm", soup_auth_domain_get_realm (domain));
+ return g_string_free (challenge, FALSE);
+}
+
+static gboolean
+check_password (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ const char *password)
+{
+ const char *header;
+ char *msg_username, *msg_password;
+ gboolean ok;
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Authorization");
+ if (!parse_basic (msg, header, &msg_username, &msg_password))
+ return FALSE;
+
+ ok = (!strcmp (username, msg_username) &&
+ !strcmp (password, msg_password));
+ g_free (msg_username);
+ pw_free (msg_password);
+
+ return ok;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifndef SOUP_AUTH_DOMAIN_BASIC_H
+#define SOUP_AUTH_DOMAIN_BASIC_H 1
+
+#include <libsoup/soup-auth-domain.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_AUTH_DOMAIN_BASIC (soup_auth_domain_basic_get_type ())
+#define SOUP_AUTH_DOMAIN_BASIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasic))
+#define SOUP_AUTH_DOMAIN_BASIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasicClass))
+#define SOUP_IS_AUTH_DOMAIN_BASIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN_BASIC))
+#define SOUP_IS_AUTH_DOMAIN_BASIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN_BASIC))
+#define SOUP_AUTH_DOMAIN_BASIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasicClass))
+
+typedef struct {
+ SoupAuthDomain parent;
+
+} SoupAuthDomainBasic;
+
+typedef struct {
+ SoupAuthDomainClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupAuthDomainBasicClass;
+
+#define SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK "auth-callback"
+#define SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA "auth-data"
+
+GType soup_auth_domain_basic_get_type (void);
+
+SoupAuthDomain *soup_auth_domain_basic_new (const char *optname1,
+ ...) G_GNUC_NULL_TERMINATED;
+
+typedef gboolean (*SoupAuthDomainBasicAuthCallback) (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ const char *password,
+ gpointer user_data);
+
+void soup_auth_domain_basic_set_auth_callback (SoupAuthDomain *domain,
+ SoupAuthDomainBasicAuthCallback callback,
+ gpointer user_data,
+ GDestroyNotify dnotify);
+
+G_END_DECLS
+
+#endif /* SOUP_AUTH_DOMAIN_BASIC_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-domain-digest.c: HTTP Digest Authentication (server-side)
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "soup-auth-domain-digest.h"
+#include "soup-auth-digest.h"
+#include "soup-headers.h"
+#include "soup-marshal.h"
+#include "soup-message.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-auth-domain-digest
+ * @short_description: Server-side "Digest" authentication
+ *
+ * #SoupAuthDomainBasic handles the server side of HTTP "Digest"
+ * authentication.
+ **/
+
+enum {
+ PROP_0,
+
+ PROP_AUTH_CALLBACK,
+ PROP_AUTH_DATA,
+
+ LAST_PROP
+};
+
+typedef struct {
+ SoupAuthDomainDigestAuthCallback auth_callback;
+ gpointer auth_data;
+ GDestroyNotify auth_dnotify;
+
+} SoupAuthDomainDigestPrivate;
+
+#define SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DOMAIN_DIGEST, SoupAuthDomainDigestPrivate))
+
+G_DEFINE_TYPE (SoupAuthDomainDigest, soup_auth_domain_digest, SOUP_TYPE_AUTH_DOMAIN)
+
+static char *accepts (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *header);
+static char *challenge (SoupAuthDomain *domain,
+ SoupMessage *msg);
+static gboolean check_password (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ const char *password);
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+soup_auth_domain_digest_init (SoupAuthDomainDigest *digest)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthDomainDigestPrivate *priv =
+ SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (object);
+
+ if (priv->auth_dnotify)
+ priv->auth_dnotify (priv->auth_data);
+
+ G_OBJECT_CLASS (soup_auth_domain_digest_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_domain_digest_class_init (SoupAuthDomainDigestClass *digest_class)
+{
+ SoupAuthDomainClass *auth_domain_class =
+ SOUP_AUTH_DOMAIN_CLASS (digest_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (digest_class);
+
+ g_type_class_add_private (digest_class, sizeof (SoupAuthDomainDigestPrivate));
+
+ auth_domain_class->accepts = accepts;
+ auth_domain_class->challenge = challenge;
+ auth_domain_class->check_password = check_password;
+
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /**
+ * SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK:
+ *
+ * Alias for the #SoupAuthDomainDigest:auth-callback property.
+ * (The #SoupAuthDomainDigestAuthCallback.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_AUTH_CALLBACK,
+ g_param_spec_pointer (SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK,
+ "Authentication callback",
+ "Password-finding callback",
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA:
+ *
+ * Alias for the #SoupAuthDomainDigest:auth-callback property.
+ * (The #SoupAuthDomainDigestAuthCallback.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_AUTH_DATA,
+ g_param_spec_pointer (SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA,
+ "Authentication callback data",
+ "Data to pass to authentication callback",
+ G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupAuthDomainDigestPrivate *priv =
+ SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_AUTH_CALLBACK:
+ priv->auth_callback = g_value_get_pointer (value);
+ break;
+ case PROP_AUTH_DATA:
+ if (priv->auth_dnotify) {
+ priv->auth_dnotify (priv->auth_data);
+ priv->auth_dnotify = NULL;
+ }
+ priv->auth_data = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupAuthDomainDigestPrivate *priv =
+ SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_AUTH_CALLBACK:
+ g_value_set_pointer (value, priv->auth_callback);
+ break;
+ case PROP_AUTH_DATA:
+ g_value_set_pointer (value, priv->auth_data);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_auth_domain_digest_new:
+ * @optname1: name of first option, or %NULL
+ * @...: option name/value pairs
+ *
+ * Creates a #SoupAuthDomainDigest. You must set the
+ * %SOUP_AUTH_DOMAIN_REALM parameter, to indicate the realm name to be
+ * returned with the authentication challenge to the client. Other
+ * parameters are optional.
+ *
+ * Return value: the new #SoupAuthDomain
+ **/
+SoupAuthDomain *
+soup_auth_domain_digest_new (const char *optname1, ...)
+{
+ SoupAuthDomain *domain;
+ va_list ap;
+
+ va_start (ap, optname1);
+ domain = (SoupAuthDomain *)g_object_new_valist (SOUP_TYPE_AUTH_DOMAIN_DIGEST,
+ optname1, ap);
+ va_end (ap);
+
+ g_return_val_if_fail (soup_auth_domain_get_realm (domain) != NULL, NULL);
+
+ return domain;
+}
+
+/**
+ * SoupAuthDomainDigestAuthCallback:
+ * @domain: the domain
+ * @msg: the message being authenticated
+ * @username: the username provided by the client
+ * @user_data: the data passed to soup_auth_domain_digest_set_auth_callback()
+ *
+ * Callback used by #SoupAuthDomainDigest for authentication purposes.
+ * The application should look up @username in its password database,
+ * and return the corresponding encoded password (see
+ * soup_auth_domain_digest_encode_password()).
+ *
+ * Return value: the encoded password, or %NULL if @username is not a
+ * valid user. @domain will free the password when it is done with it.
+ **/
+
+/**
+ * soup_auth_domain_digest_set_auth_callback:
+ * @domain: the domain
+ * @callback: the callback
+ * @user_data: data to pass to @auth_callback
+ * @dnotify: destroy notifier to free @user_data when @domain
+ * is destroyed
+ *
+ * Sets the callback that @domain will use to authenticate incoming
+ * requests. For each request containing authorization, @domain will
+ * invoke the callback, and then either accept or reject the request
+ * based on @callback's return value.
+ *
+ * You can also set the auth callback by setting the
+ * %SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK and
+ * %SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA properties, which can also be
+ * used to set the callback at construct time.
+ **/
+void
+soup_auth_domain_digest_set_auth_callback (SoupAuthDomain *domain,
+ SoupAuthDomainDigestAuthCallback callback,
+ gpointer user_data,
+ GDestroyNotify dnotify)
+{
+ SoupAuthDomainDigestPrivate *priv =
+ SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (domain);
+
+ if (priv->auth_dnotify)
+ priv->auth_dnotify (priv->auth_data);
+
+ priv->auth_callback = callback;
+ priv->auth_data = user_data;
+ priv->auth_dnotify = dnotify;
+
+ g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK);
+ g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA);
+}
+
+static gboolean
+check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg,
+ GHashTable *params, const char *username,
+ const char *hex_urp)
+{
+ const char *uri, *qop, *realm, *msg_username;
+ const char *nonce, *nc, *cnonce, *response;
+ char hex_a1[33], computed_response[33];
+ int nonce_count;
+ SoupURI *dig_uri, *req_uri;
+
+ msg_username = g_hash_table_lookup (params, "username");
+ if (!msg_username || strcmp (msg_username, username) != 0)
+ return FALSE;
+
+ /* Check uri */
+ uri = g_hash_table_lookup (params, "uri");
+ if (!uri)
+ return FALSE;
+
+ req_uri = soup_message_get_uri (msg);
+ dig_uri = soup_uri_new (uri);
+ if (dig_uri) {
+ if (!soup_uri_equal (dig_uri, req_uri)) {
+ soup_uri_free (dig_uri);
+ return FALSE;
+ }
+ soup_uri_free (dig_uri);
+ } else {
+ char *req_path;
+
+ req_path = soup_uri_to_string (req_uri, TRUE);
+ if (strcmp (uri, req_path) != 0) {
+ g_free (req_path);
+ return FALSE;
+ }
+ g_free (req_path);
+ }
+
+ /* Check qop; we only support "auth" for now */
+ qop = g_hash_table_lookup (params, "qop");
+ if (!qop || strcmp (qop, "auth") != 0)
+ return FALSE;
+
+ /* Check realm */
+ realm = g_hash_table_lookup (params, "realm");
+ if (!realm || strcmp (realm, soup_auth_domain_get_realm (domain)) != 0)
+ return FALSE;
+
+ nonce = g_hash_table_lookup (params, "nonce");
+ if (!nonce)
+ return FALSE;
+ nc = g_hash_table_lookup (params, "nc");
+ if (!nc)
+ return FALSE;
+ nonce_count = strtoul (nc, NULL, 16);
+ if (nonce_count <= 0)
+ return FALSE;
+ cnonce = g_hash_table_lookup (params, "cnonce");
+ if (!cnonce)
+ return FALSE;
+ response = g_hash_table_lookup (params, "response");
+ if (!response)
+ return FALSE;
+
+ soup_auth_digest_compute_hex_a1 (hex_urp,
+ SOUP_AUTH_DIGEST_ALGORITHM_MD5,
+ nonce, cnonce, hex_a1);
+ soup_auth_digest_compute_response (msg->method, uri,
+ hex_a1,
+ SOUP_AUTH_DIGEST_QOP_AUTH,
+ nonce, cnonce, nonce_count,
+ computed_response);
+ return strcmp (response, computed_response) == 0;
+}
+
+static char *
+accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
+{
+ SoupAuthDomainDigestPrivate *priv =
+ SOUP_AUTH_DOMAIN_DIGEST_GET_PRIVATE (domain);
+ GHashTable *params;
+ const char *username;
+ gboolean accept = FALSE;
+ char *ret_user;
+
+ if (strncmp (header, "Digest ", 7) != 0)
+ return NULL;
+
+ params = soup_header_parse_param_list (header + 7);
+ if (!params)
+ return NULL;
+
+ username = g_hash_table_lookup (params, "username");
+ if (!username) {
+ soup_header_free_param_list (params);
+ return NULL;
+ }
+
+ if (priv->auth_callback) {
+ char *hex_urp;
+
+ hex_urp = priv->auth_callback (domain, msg, username,
+ priv->auth_data);
+ if (hex_urp) {
+ accept = check_hex_urp (domain, msg, params,
+ username, hex_urp);
+ g_free (hex_urp);
+ } else
+ accept = FALSE;
+ } else {
+ accept = soup_auth_domain_try_generic_auth_callback (
+ domain, msg, username);
+ }
+
+ ret_user = accept ? g_strdup (username) : NULL;
+ soup_header_free_param_list (params);
+ return ret_user;
+}
+
+static char *
+challenge (SoupAuthDomain *domain, SoupMessage *msg)
+{
+ GString *str;
+
+ str = g_string_new ("Digest ");
+ soup_header_g_string_append_param_quoted (str, "realm", soup_auth_domain_get_realm (domain));
+ g_string_append_printf (str, ", nonce=\"%lu%lu\"",
+ (unsigned long) msg,
+ (unsigned long) time (0));
+ g_string_append_printf (str, ", qop=\"auth\"");
+ g_string_append_printf (str, ", algorithm=MD5");
+
+ return g_string_free (str, FALSE);
+}
+
+/**
+ * soup_auth_domain_digest_encode_password:
+ * @username: a username
+ * @realm: an auth realm name
+ * @password: the password for @username in @realm
+ *
+ * Encodes the username/realm/password triplet for Digest
+ * authentication. (That is, it returns a stringified MD5 hash of
+ * @username, @realm, and @password concatenated together). This is
+ * the form that is needed as the return value of
+ * #SoupAuthDomainDigest's auth handler.
+ *
+ * For security reasons, you should store the encoded hash, rather
+ * than storing the cleartext password itself and calling this method
+ * only when you need to verify it. This way, if your server is
+ * compromised, the attackers will not gain access to cleartext
+ * passwords which might also be usable at other sites. (Note also
+ * that the encoded password returned by this method is identical to
+ * the encoded password stored in an Apache .htdigest file.)
+ *
+ * Return value: the encoded password
+ **/
+char *
+soup_auth_domain_digest_encode_password (const char *username,
+ const char *realm,
+ const char *password)
+{
+ char hex_urp[33];
+
+ soup_auth_digest_compute_hex_urp (username, realm, password, hex_urp);
+ return g_strdup (hex_urp);
+}
+
+static gboolean
+check_password (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ const char *password)
+{
+ const char *header;
+ GHashTable *params;
+ const char *msg_username;
+ char hex_urp[33];
+ gboolean accept;
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Authorization");
+ if (strncmp (header, "Digest ", 7) != 0)
+ return FALSE;
+
+ params = soup_header_parse_param_list (header + 7);
+ if (!params)
+ return FALSE;
+
+ msg_username = g_hash_table_lookup (params, "username");
+ if (!msg_username || strcmp (msg_username, username) != 0) {
+ soup_header_free_param_list (params);
+ return FALSE;
+ }
+
+ soup_auth_digest_compute_hex_urp (username,
+ soup_auth_domain_get_realm (domain),
+ password, hex_urp);
+ accept = check_hex_urp (domain, msg, params, username, hex_urp);
+ soup_header_free_param_list (params);
+ return accept;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifndef SOUP_AUTH_DOMAIN_DIGEST_H
+#define SOUP_AUTH_DOMAIN_DIGEST_H 1
+
+#include <libsoup/soup-auth-domain.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_AUTH_DOMAIN_DIGEST (soup_auth_domain_digest_get_type ())
+#define SOUP_AUTH_DOMAIN_DIGEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH_DOMAIN_DIGEST, SoupAuthDomainDigest))
+#define SOUP_AUTH_DOMAIN_DIGEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DOMAIN_DIGEST, SoupAuthDomainDigestClass))
+#define SOUP_IS_AUTH_DOMAIN_DIGEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN_DIGEST))
+#define SOUP_IS_AUTH_DOMAIN_DIGEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN_DIGEST))
+#define SOUP_AUTH_DOMAIN_DIGEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_DOMAIN_DIGEST, SoupAuthDomainDigestClass))
+
+typedef struct {
+ SoupAuthDomain parent;
+
+} SoupAuthDomainDigest;
+
+typedef struct {
+ SoupAuthDomainClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupAuthDomainDigestClass;
+
+#define SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK "auth-callback"
+#define SOUP_AUTH_DOMAIN_DIGEST_AUTH_DATA "auth-data"
+
+GType soup_auth_domain_digest_get_type (void);
+
+SoupAuthDomain *soup_auth_domain_digest_new (const char *optname1,
+ ...) G_GNUC_NULL_TERMINATED;
+
+typedef char * (*SoupAuthDomainDigestAuthCallback) (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ gpointer user_data);
+
+void soup_auth_domain_digest_set_auth_callback (SoupAuthDomain *domain,
+ SoupAuthDomainDigestAuthCallback callback,
+ gpointer user_data,
+ GDestroyNotify dnotify);
+
+char *soup_auth_domain_digest_encode_password (const char *username,
+ const char *realm,
+ const char *password);
+
+G_END_DECLS
+
+#endif /* SOUP_AUTH_DOMAIN_DIGEST_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-domain.c: HTTP Authentication Domain (server-side)
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-domain.h"
+#include "soup-message.h"
+#include "soup-path-map.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-auth-domain
+ * @short_description: Server-side authentication
+ * @see_also: #SoupServer
+ *
+ * A #SoupAuthDomain manages authentication for all or part of a
+ * #SoupServer. To make a server require authentication, first create
+ * an appropriate subclass of #SoupAuthDomain, and then add it to the
+ * server with soup_server_add_auth_domain().
+ *
+ * In order for an auth domain to have any effect, you must add one or
+ * more paths to it (via soup_auth_domain_add_path() or the
+ * %SOUP_AUTH_DOMAIN_ADD_PATH property). To require authentication for
+ * all ordinary requests, add the path "/". (Note that this does not
+ * include the special "*" URI (eg, "OPTIONS *"), which must be added
+ * as a separate path if you want to cover it.)
+ *
+ * If you need greater control over which requests should and
+ * shouldn't be authenticated, add paths covering everything you
+ * <emphasis>might</emphasis> want authenticated, and then use a
+ * filter (soup_auth_domain_set_filter()) to bypass authentication for
+ * those requests that don't need it.
+ **/
+
+enum {
+ PROP_0,
+
+ PROP_REALM,
+ PROP_PROXY,
+ PROP_ADD_PATH,
+ PROP_REMOVE_PATH,
+ PROP_FILTER,
+ PROP_FILTER_DATA,
+ PROP_GENERIC_AUTH_CALLBACK,
+ PROP_GENERIC_AUTH_DATA,
+
+ LAST_PROP
+};
+
+typedef struct {
+ char *realm;
+ gboolean proxy;
+ SoupPathMap *paths;
+
+ SoupAuthDomainFilter filter;
+ gpointer filter_data;
+ GDestroyNotify filter_dnotify;
+
+ SoupAuthDomainGenericAuthCallback auth_callback;
+ gpointer auth_data;
+ GDestroyNotify auth_dnotify;
+
+} SoupAuthDomainPrivate;
+
+#define SOUP_AUTH_DOMAIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomainPrivate))
+
+G_DEFINE_ABSTRACT_TYPE (SoupAuthDomain, soup_auth_domain, G_TYPE_OBJECT)
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+soup_auth_domain_init (SoupAuthDomain *domain)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+ priv->paths = soup_path_map_new (NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (object);
+
+ g_free (priv->realm);
+ soup_path_map_free (priv->paths);
+
+ if (priv->filter_dnotify)
+ priv->filter_dnotify (priv->filter_data);
+ if (priv->auth_dnotify)
+ priv->auth_dnotify (priv->auth_data);
+
+ G_OBJECT_CLASS (soup_auth_domain_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_domain_class_init (SoupAuthDomainClass *auth_domain_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (auth_domain_class);
+
+ g_type_class_add_private (auth_domain_class, sizeof (SoupAuthDomainPrivate));
+
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /**
+ * SOUP_AUTH_DOMAIN_REALM:
+ *
+ * Alias for the #SoupAuthDomain:realm property. (The realm of
+ * this auth domain.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REALM,
+ g_param_spec_string (SOUP_AUTH_DOMAIN_REALM,
+ "Realm",
+ "The realm of this auth domain",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_AUTH_DOMAIN_PROXY:
+ *
+ * Alias for the #SoupAuthDomain:proxy property. (Whether or
+ * not this is a proxy auth domain.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_PROXY,
+ g_param_spec_boolean (SOUP_AUTH_DOMAIN_PROXY,
+ "Proxy",
+ "Whether or not this is a proxy auth domain",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_AUTH_DOMAIN_ADD_PATH:
+ *
+ * Alias for the #SoupAuthDomain:add-path property. (Shortcut
+ * for calling soup_auth_domain_add_path().)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_ADD_PATH,
+ g_param_spec_string (SOUP_AUTH_DOMAIN_ADD_PATH,
+ "Add a path",
+ "Add a path covered by this auth domain",
+ NULL,
+ G_PARAM_WRITABLE));
+ /**
+ * SOUP_AUTH_DOMAIN_REMOVE_PATH:
+ *
+ * Alias for the #SoupAuthDomain:remove-path property.
+ * (Shortcut for calling soup_auth_domain_remove_path().)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REMOVE_PATH,
+ g_param_spec_string (SOUP_AUTH_DOMAIN_REMOVE_PATH,
+ "Remove a path",
+ "Remove a path covered by this auth domain",
+ NULL,
+ G_PARAM_WRITABLE));
+ /**
+ * SOUP_AUTH_DOMAIN_FILTER:
+ *
+ * Alias for the #SoupAuthDomain:filter property. (The
+ * #SoupAuthDomainFilter for the domain.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_FILTER,
+ g_param_spec_pointer (SOUP_AUTH_DOMAIN_FILTER,
+ "Filter",
+ "A filter for deciding whether or not to require authentication",
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_AUTH_DOMAIN_FILTER_DATA:
+ *
+ * Alias for the #SoupAuthDomain:filter-data property. (Data
+ * to pass to the #SoupAuthDomainFilter.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_FILTER_DATA,
+ g_param_spec_pointer (SOUP_AUTH_DOMAIN_FILTER_DATA,
+ "Filter data",
+ "Data to pass to filter",
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK:
+ *
+ * Alias for the #SoupAuthDomain:auth-callback property.
+ * (The #SoupAuthDomainGenericAuthCallback.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_GENERIC_AUTH_CALLBACK,
+ g_param_spec_pointer (SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK,
+ "Generic authentication callback",
+ "An authentication callback that can be used with any SoupAuthDomain subclass",
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA:
+ *
+ * Alias for the #SoupAuthDomain:auth-data property.
+ * (The data to pass to the #SoupAuthDomainGenericAuthCallback.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_GENERIC_AUTH_DATA,
+ g_param_spec_pointer (SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA,
+ "Authentication callback data",
+ "Data to pass to auth callback",
+ G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupAuthDomain *auth_domain = SOUP_AUTH_DOMAIN (object);
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_REALM:
+ g_free (priv->realm);
+ priv->realm = g_value_dup_string (value);
+ break;
+ case PROP_PROXY:
+ priv->proxy = g_value_get_boolean (value);
+ break;
+ case PROP_ADD_PATH:
+ soup_auth_domain_add_path (auth_domain,
+ g_value_get_string (value));
+ break;
+ case PROP_REMOVE_PATH:
+ soup_auth_domain_remove_path (auth_domain,
+ g_value_get_string (value));
+ break;
+ case PROP_FILTER:
+ priv->filter = g_value_get_pointer (value);
+ break;
+ case PROP_FILTER_DATA:
+ if (priv->filter_dnotify) {
+ priv->filter_dnotify (priv->filter_data);
+ priv->filter_dnotify = NULL;
+ }
+ priv->filter_data = g_value_get_pointer (value);
+ break;
+ case PROP_GENERIC_AUTH_CALLBACK:
+ priv->auth_callback = g_value_get_pointer (value);
+ break;
+ case PROP_GENERIC_AUTH_DATA:
+ if (priv->auth_dnotify) {
+ priv->auth_dnotify (priv->auth_data);
+ priv->auth_dnotify = NULL;
+ }
+ priv->auth_data = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_REALM:
+ g_value_set_string (value, priv->realm);
+ break;
+ case PROP_PROXY:
+ g_value_set_boolean (value, priv->proxy);
+ break;
+ case PROP_FILTER:
+ g_value_set_pointer (value, priv->filter);
+ break;
+ case PROP_FILTER_DATA:
+ g_value_set_pointer (value, priv->filter_data);
+ break;
+ case PROP_GENERIC_AUTH_CALLBACK:
+ g_value_set_pointer (value, priv->auth_callback);
+ break;
+ case PROP_GENERIC_AUTH_DATA:
+ g_value_set_pointer (value, priv->auth_data);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_auth_domain_add_path:
+ * @domain: a #SoupAuthDomain
+ * @path: the path to add to @domain
+ *
+ * Adds @path to @domain, such that requests under @path on @domain's
+ * server will require authentication (unless overridden by
+ * soup_auth_domain_remove_path() or soup_auth_domain_set_filter()).
+ *
+ * You can also add paths by setting the %SOUP_AUTH_DOMAIN_ADD_PATH
+ * property, which can also be used to add one or more paths at
+ * construct time.
+ **/
+void
+soup_auth_domain_add_path (SoupAuthDomain *domain, const char *path)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+ /* "" should not match "*" */
+ if (!*path)
+ path = "/";
+
+ soup_path_map_add (priv->paths, path, GINT_TO_POINTER (TRUE));
+}
+
+/**
+ * soup_auth_domain_remove_path:
+ * @domain: a #SoupAuthDomain
+ * @path: the path to remove from @domain
+ *
+ * Removes @path from @domain, such that requests under @path on
+ * @domain's server will NOT require authentication.
+ *
+ * This is not simply an undo-er for soup_auth_domain_add_path(); it
+ * can be used to "carve out" a subtree that does not require
+ * authentication inside a hierarchy that does. Note also that unlike
+ * with soup_auth_domain_add_path(), this cannot be overridden by
+ * adding a filter, as filters can only bypass authentication that
+ * would otherwise be required, not require it where it would
+ * otherwise be unnecessary.
+ *
+ * You can also remove paths by setting the
+ * %SOUP_AUTH_DOMAIN_REMOVE_PATH property, which can also be used to
+ * remove one or more paths at construct time.
+ **/
+void
+soup_auth_domain_remove_path (SoupAuthDomain *domain, const char *path)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+ /* "" should not match "*" */
+ if (!*path)
+ path = "/";
+
+ soup_path_map_add (priv->paths, path, GINT_TO_POINTER (FALSE));
+}
+
+/**
+ * SoupAuthDomainFilter:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ * @user_data: the data passed to soup_auth_domain_set_filter()
+ *
+ * The prototype for a #SoupAuthDomain filter; see
+ * soup_auth_domain_set_filter() for details.
+ *
+ * Return value: %TRUE if @msg requires authentication, %FALSE if not.
+ **/
+
+/**
+ * soup_auth_domain_set_filter:
+ * @domain: a #SoupAuthDomain
+ * @filter: the auth filter for @domain
+ * @filter_data: data to pass to @filter
+ * @dnotify: destroy notifier to free @filter_data when @domain
+ * is destroyed
+ *
+ * Adds @filter as an authentication filter to @domain. The filter
+ * gets a chance to bypass authentication for certain requests that
+ * would otherwise require it. Eg, it might check the message's path
+ * in some way that is too complicated to do via the other methods, or
+ * it might check the message's method, and allow GETs but not PUTs.
+ *
+ * The filter function returns %TRUE if the request should still
+ * require authentication, or %FALSE if authentication is unnecessary
+ * for this request.
+ *
+ * To help prevent security holes, your filter should return %TRUE by
+ * default, and only return %FALSE under specifically-tested
+ * circumstances, rather than the other way around. Eg, in the example
+ * above, where you want to authenticate PUTs but not GETs, you should
+ * check if the method is GET and return %FALSE in that case, and then
+ * return %TRUE for all other methods (rather than returning %TRUE for
+ * PUT and %FALSE for all other methods). This way if it turned out
+ * (now or later) that some paths supported additional methods besides
+ * GET and PUT, those methods would default to being NOT allowed for
+ * unauthenticated users.
+ *
+ * You can also set the filter by setting the %SOUP_AUTH_DOMAIN_FILTER
+ * and %SOUP_AUTH_DOMAIN_FILTER_DATA properties, which can also be
+ * used to set the filter at construct time.
+ **/
+void
+soup_auth_domain_set_filter (SoupAuthDomain *domain,
+ SoupAuthDomainFilter filter,
+ gpointer filter_data,
+ GDestroyNotify dnotify)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+ if (priv->filter_dnotify)
+ priv->filter_dnotify (priv->filter_data);
+
+ priv->filter = filter;
+ priv->filter_data = filter_data;
+ priv->filter_dnotify = dnotify;
+
+ g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_FILTER);
+ g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_FILTER_DATA);
+}
+
+/**
+ * soup_auth_domain_get_realm:
+ * @domain: a #SoupAuthDomain
+ *
+ * Gets the realm name associated with @domain
+ *
+ * Return value: @domain's realm
+ **/
+const char *
+soup_auth_domain_get_realm (SoupAuthDomain *domain)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+ return priv->realm;
+}
+
+/**
+ * SoupAuthDomainGenericAuthCallback:
+ * @domain: a #SoupAuthDomain
+ * @msg: the #SoupMessage being authenticated
+ * @username: the username from @msg
+ * @user_data: the data passed to
+ * soup_auth_domain_set_generic_auth_callback()
+ *
+ * The prototype for a #SoupAuthDomain generic authentication callback.
+ *
+ * The callback should look up the user's password, call
+ * soup_auth_domain_check_password(), and use the return value from
+ * that method as its own return value.
+ *
+ * In general, for security reasons, it is preferable to use the
+ * auth-domain-specific auth callbacks (eg,
+ * #SoupAuthDomainBasicAuthCallback and
+ * #SoupAuthDomainDigestAuthCallback), because they don't require
+ * keeping a cleartext password database. Most users will use the same
+ * password for many different sites, meaning if any site with a
+ * cleartext password database is compromised, accounts on other
+ * servers might be compromised as well. For many of the cases where
+ * #SoupServer is used, this is not really relevant, but it may still
+ * be worth considering.
+ *
+ * Return value: %TRUE if @msg is authenticated, %FALSE if not.
+ **/
+
+/**
+ * soup_auth_domain_set_generic_auth_callback:
+ * @domain: a #SoupAuthDomain
+ * @auth_callback: the auth callback
+ * @auth_data: data to pass to @auth_callback
+ * @dnotify: destroy notifier to free @auth_data when @domain
+ * is destroyed
+ *
+ * Sets @auth_callback as an authentication-handling callback for
+ * @domain. Whenever a request comes in to @domain which cannot be
+ * authenticated via a domain-specific auth callback (eg,
+ * #SoupAuthDomainDigestAuthCallback), the generic auth callback
+ * will be invoked. See #SoupAuthDomainGenericAuthCallback for information
+ * on what the callback should do.
+ **/
+void
+soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain,
+ SoupAuthDomainGenericAuthCallback auth_callback,
+ gpointer auth_data,
+ GDestroyNotify dnotify)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+ if (priv->auth_dnotify)
+ priv->auth_dnotify (priv->auth_data);
+
+ priv->auth_callback = auth_callback;
+ priv->auth_data = auth_data;
+ priv->auth_dnotify = dnotify;
+
+ g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK);
+ g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA);
+}
+
+gboolean
+soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+
+ if (priv->auth_callback)
+ return priv->auth_callback (domain, msg, username, priv->auth_data);
+ else
+ return FALSE;
+}
+
+/**
+ * soup_auth_domain_check_password:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ * @username: a username
+ * @password: a password
+ *
+ * Checks if @msg authenticates to @domain via @username and
+ * @password. This would normally be called from a
+ * #SoupAuthDomainGenericAuthCallback.
+ *
+ * Return value: whether or not the message is authenticated
+ **/
+gboolean
+soup_auth_domain_check_password (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ const char *password)
+{
+ return SOUP_AUTH_DOMAIN_GET_CLASS (domain)->check_password (domain, msg,
+ username,
+ password);
+}
+
+/**
+ * soup_auth_domain_covers:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ *
+ * Checks if @domain requires @msg to be authenticated (according to
+ * its paths and filter function). This does not actually look at
+ * whether @msg <emphasis>is</emphasis> authenticated, merely whether
+ * or not it needs to be.
+ *
+ * This is used by #SoupServer internally and is probably of no use to
+ * anyone else.
+ *
+ * Return value: %TRUE if @domain requires @msg to be authenticated
+ **/
+gboolean
+soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+ const char *path;
+
+ if (!priv->proxy) {
+ path = soup_message_get_uri (msg)->path;
+ if (!soup_path_map_lookup (priv->paths, path))
+ return FALSE;
+ }
+
+ if (priv->filter && !priv->filter (domain, msg, priv->filter_data))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/**
+ * soup_auth_domain_accepts:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ *
+ * Checks if @msg contains appropriate authorization for @domain to
+ * accept it. Mirroring soup_auth_domain_covers(), this does not check
+ * whether or not @domain <emphasis>cares</emphasis> if @msg is
+ * authorized.
+ *
+ * This is used by #SoupServer internally and is probably of no use to
+ * anyone else.
+ *
+ * Return value: the username that @msg has authenticated as, if in
+ * fact it has authenticated. %NULL otherwise.
+ **/
+char *
+soup_auth_domain_accepts (SoupAuthDomain *domain, SoupMessage *msg)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+ const char *header;
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ priv->proxy ?
+ "Proxy-Authorization" :
+ "Authorization");
+ if (!header)
+ return NULL;
+ return SOUP_AUTH_DOMAIN_GET_CLASS (domain)->accepts (domain, msg, header);
+}
+
+/**
+ * soup_auth_domain_challenge:
+ * @domain: a #SoupAuthDomain
+ * @msg: a #SoupMessage
+ *
+ * Adds a "WWW-Authenticate" or "Proxy-Authenticate" header to @msg,
+ * requesting that the client authenticate, and sets @msg's status
+ * accordingly.
+ *
+ * This is used by #SoupServer internally and is probably of no use to
+ * anyone else.
+ **/
+void
+soup_auth_domain_challenge (SoupAuthDomain *domain, SoupMessage *msg)
+{
+ SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
+ char *challenge;
+
+ challenge = SOUP_AUTH_DOMAIN_GET_CLASS (domain)->challenge (domain, msg);
+ soup_message_set_status (msg, priv->proxy ?
+ SOUP_STATUS_PROXY_UNAUTHORIZED :
+ SOUP_STATUS_UNAUTHORIZED);
+ soup_message_headers_append (msg->response_headers,
+ priv->proxy ?
+ "Proxy-Authenticate" :
+ "WWW-Authenticate",
+ challenge);
+ g_free (challenge);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifndef SOUP_AUTH_DOMAIN_H
+#define SOUP_AUTH_DOMAIN_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_AUTH_DOMAIN (soup_auth_domain_get_type ())
+#define SOUP_AUTH_DOMAIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomain))
+#define SOUP_AUTH_DOMAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomainClass))
+#define SOUP_IS_AUTH_DOMAIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN))
+#define SOUP_IS_AUTH_DOMAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH_DOMAIN))
+#define SOUP_AUTH_DOMAIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_DOMAIN, SoupAuthDomainClass))
+
+struct _SoupAuthDomain {
+ GObject parent;
+
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ char * (*accepts) (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *header);
+ char * (*challenge) (SoupAuthDomain *domain,
+ SoupMessage *msg);
+ gboolean (*check_password) (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ const char *password);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupAuthDomainClass;
+
+#define SOUP_AUTH_DOMAIN_REALM "realm"
+#define SOUP_AUTH_DOMAIN_PROXY "proxy"
+#define SOUP_AUTH_DOMAIN_ADD_PATH "add-path"
+#define SOUP_AUTH_DOMAIN_REMOVE_PATH "remove-path"
+#define SOUP_AUTH_DOMAIN_FILTER "filter"
+#define SOUP_AUTH_DOMAIN_FILTER_DATA "filter-data"
+#define SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK "generic-auth-callback"
+#define SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA "generic-auth-data"
+
+typedef gboolean (*SoupAuthDomainFilter) (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ gpointer user_data);
+
+typedef gboolean (*SoupAuthDomainGenericAuthCallback) (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ gpointer user_data);
+
+GType soup_auth_domain_get_type (void);
+
+void soup_auth_domain_add_path (SoupAuthDomain *domain,
+ const char *path);
+void soup_auth_domain_remove_path (SoupAuthDomain *domain,
+ const char *path);
+
+void soup_auth_domain_set_filter (SoupAuthDomain *domain,
+ SoupAuthDomainFilter filter,
+ gpointer filter_data,
+ GDestroyNotify dnotify);
+
+const char *soup_auth_domain_get_realm (SoupAuthDomain *domain);
+
+void soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain,
+ SoupAuthDomainGenericAuthCallback auth_callback,
+ gpointer auth_data,
+ GDestroyNotify dnotify);
+gboolean soup_auth_domain_check_password (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username,
+ const char *password);
+
+gboolean soup_auth_domain_covers (SoupAuthDomain *domain,
+ SoupMessage *msg);
+char *soup_auth_domain_accepts (SoupAuthDomain *domain,
+ SoupMessage *msg);
+void soup_auth_domain_challenge (SoupAuthDomain *domain,
+ SoupMessage *msg);
+
+/* protected */
+gboolean soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain,
+ SoupMessage *msg,
+ const char *username);
+
+G_END_DECLS
+
+#endif /* SOUP_AUTH_DOMAIN_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-manager-ntlm.c: NTLM auth manager
+ *
+ * Copyright (C) 2001-2007 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <string.h>
+
+#ifdef USE_NTLM_AUTH
+#include <stdlib.h>
+#include <errno.h>
+#endif
+#include "soup-auth-manager-ntlm.h"
+#include "soup-auth-ntlm.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-misc.h"
+#include "soup-session.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+static void soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static SoupSessionFeatureInterface *soup_auth_manager_parent_feature_interface;
+
+static void attach (SoupSessionFeature *feature, SoupSession *session);
+static void request_queued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg);
+static void request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket);
+static void request_unqueued (SoupSessionFeature *feature,
+ SoupSession *session, SoupMessage *msg);
+static gboolean add_feature (SoupSessionFeature *feature, GType type);
+static gboolean remove_feature (SoupSessionFeature *feature, GType type);
+static gboolean has_feature (SoupSessionFeature *feature, GType type);
+
+G_DEFINE_TYPE_WITH_CODE (SoupAuthManagerNTLM, soup_auth_manager_ntlm, SOUP_TYPE_AUTH_MANAGER,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_auth_manager_ntlm_session_feature_init))
+
+typedef enum {
+ SOUP_NTLM_NEW,
+#ifdef USE_NTLM_AUTH
+ SOUP_NTLM_SENT_SSO_REQUEST,
+ SOUP_NTLM_RECEIVED_SSO_CHALLENGE,
+ SOUP_NTLM_SENT_SSO_RESPONSE,
+ SOUP_NTLM_SSO_FAILED,
+#endif
+ SOUP_NTLM_SENT_REQUEST,
+ SOUP_NTLM_RECEIVED_CHALLENGE,
+ SOUP_NTLM_SENT_RESPONSE,
+ SOUP_NTLM_FAILED
+} SoupNTLMState;
+
+typedef struct {
+ SoupSocket *socket;
+ SoupNTLMState state;
+ char *response_header;
+
+ char *nonce, *domain;
+ SoupAuth *auth;
+#ifdef USE_NTLM_AUTH
+ char *challenge_header;
+ int fd_in;
+ int fd_out;
+#endif
+} SoupNTLMConnection;
+
+typedef struct {
+ gboolean use_ntlm;
+
+ SoupSession *session;
+ GHashTable *connections_by_msg;
+ GHashTable *connections_by_id;
+#ifdef USE_NTLM_AUTH
+ gboolean ntlm_auth_accessible;
+#endif
+} SoupAuthManagerNTLMPrivate;
+#define SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMPrivate))
+
+static char *soup_ntlm_request (void);
+static gboolean soup_ntlm_parse_challenge (const char *challenge,
+ char **nonce,
+ char **default_domain);
+static char *soup_ntlm_response (const char *nonce,
+ const char *user,
+ const char *password,
+ const char *host,
+ const char *domain);
+#ifdef USE_NTLM_AUTH
+static void sso_ntlm_close (SoupNTLMConnection *conn);
+#endif
+
+static void
+soup_auth_manager_ntlm_init (SoupAuthManagerNTLM *ntlm)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
+
+ priv->connections_by_id = g_hash_table_new (NULL, NULL);
+ priv->connections_by_msg = g_hash_table_new (NULL, NULL);
+#ifdef USE_NTLM_AUTH
+ priv->ntlm_auth_accessible = (access (NTLM_AUTH, X_OK) == 0);
+#endif
+}
+
+static void
+free_ntlm_connection (SoupNTLMConnection *conn)
+{
+ g_free (conn->response_header);
+ g_free (conn->nonce);
+ g_free (conn->domain);
+ if (conn->auth)
+ g_object_unref (conn->auth);
+#ifdef USE_NTLM_AUTH
+ g_free (conn->challenge_header);
+ sso_ntlm_close (conn);
+#endif
+ g_slice_free (SoupNTLMConnection, conn);
+}
+
+static void
+free_ntlm_connection_foreach (gpointer key, gpointer value, gpointer user_data)
+{
+ free_ntlm_connection (value);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (object);
+
+ g_hash_table_foreach (priv->connections_by_id,
+ free_ntlm_connection_foreach, NULL);
+ g_hash_table_destroy (priv->connections_by_id);
+ g_hash_table_destroy (priv->connections_by_msg);
+
+ G_OBJECT_CLASS (soup_auth_manager_ntlm_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_manager_ntlm_class_init (SoupAuthManagerNTLMClass *auth_manager_ntlm_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_ntlm_class);
+
+ g_type_class_add_private (auth_manager_ntlm_class, sizeof (SoupAuthManagerNTLMPrivate));
+
+ object_class->finalize = finalize;
+}
+
+static void
+soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ soup_auth_manager_parent_feature_interface =
+ g_type_interface_peek_parent (feature_interface);
+
+ feature_interface->attach = attach;
+ feature_interface->request_queued = request_queued;
+ feature_interface->request_started = request_started;
+ feature_interface->request_unqueued = request_unqueued;
+ feature_interface->add_feature = add_feature;
+ feature_interface->remove_feature = remove_feature;
+ feature_interface->has_feature = has_feature;
+}
+
+static void
+attach (SoupSessionFeature *manager, SoupSession *session)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (manager);
+
+ /* FIXME: should support multiple sessions */
+ priv->session = session;
+
+ soup_auth_manager_parent_feature_interface->attach (manager, session);
+}
+
+static void
+delete_conn (SoupSocket *socket, gpointer user_data)
+{
+ SoupAuthManagerNTLMPrivate *priv = user_data;
+ SoupNTLMConnection *conn;
+
+ conn = g_hash_table_lookup (priv->connections_by_id, socket);
+ if (conn)
+ free_ntlm_connection (conn);
+ g_hash_table_remove (priv->connections_by_id, socket);
+ g_signal_handlers_disconnect_by_func (socket, delete_conn, priv);
+}
+
+static SoupNTLMConnection *
+get_connection (SoupAuthManagerNTLMPrivate *priv, SoupSocket *socket)
+{
+ SoupNTLMConnection *conn;
+
+ conn = g_hash_table_lookup (priv->connections_by_id, socket);
+ if (conn)
+ return conn;
+
+ conn = g_slice_new0 (SoupNTLMConnection);
+ conn->socket = socket;
+ conn->state = SOUP_NTLM_NEW;
+#ifdef USE_NTLM_AUTH
+ conn->fd_in = -1;
+ conn->fd_out = -1;
+#endif
+ g_hash_table_insert (priv->connections_by_id, socket, conn);
+
+ g_signal_connect (socket, "disconnected",
+ G_CALLBACK (delete_conn), priv);
+ return conn;
+}
+
+static void
+unset_conn (SoupMessage *msg, gpointer user_data)
+{
+ SoupAuthManagerNTLMPrivate *priv = user_data;
+
+ g_hash_table_remove (priv->connections_by_msg, msg);
+ g_signal_handlers_disconnect_by_func (msg, unset_conn, priv);
+}
+
+static SoupNTLMConnection *
+set_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg,
+ SoupNTLMConnection *conn)
+{
+ if (!g_hash_table_lookup (priv->connections_by_msg, msg)) {
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (unset_conn), priv);
+ g_signal_connect (msg, "restarted",
+ G_CALLBACK (unset_conn), priv);
+ }
+ g_hash_table_insert (priv->connections_by_msg, msg, conn);
+
+ return conn;
+}
+
+static SoupNTLMConnection *
+get_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg)
+{
+ return g_hash_table_lookup (priv->connections_by_msg, msg);
+}
+
+#ifdef USE_NTLM_AUTH
+static void
+sso_ntlm_close (SoupNTLMConnection *conn)
+{
+ if (conn->fd_in != -1) {
+ close (conn->fd_in);
+ conn->fd_in = -1;
+ }
+
+ if (conn->fd_out != -1) {
+ close (conn->fd_out);
+ conn->fd_out = -1;
+ }
+}
+
+static gboolean
+sso_ntlm_initiate (SoupNTLMConnection *conn, SoupAuthManagerNTLMPrivate *priv)
+{
+ char *username = NULL, *slash, *domain = NULL;
+ char *argv[9];
+ gboolean ret;
+
+ /* Return if ntlm_auth execution process exist already */
+ if (conn->fd_in != -1 && conn->fd_out != -1)
+ return TRUE;
+ else
+ /* Clean all sso data before re-initiate */
+ sso_ntlm_close (conn);
+
+ if (!priv->ntlm_auth_accessible)
+ goto done;
+
+ username = getenv ("NTLMUSER");
+ if (!username)
+ username = getenv ("USER");
+ if (!username)
+ goto done;
+
+ slash = strpbrk (username, "\\/");
+ if (slash) {
+ domain = g_strdup (username);
+ slash = domain + (slash - username);
+ *slash = '\0';
+ username = slash + 1;
+ }
+
+ argv[0] = NTLM_AUTH;
+ argv[1] = "--helper-protocol";
+ argv[2] = "ntlmssp-client-1";
+ argv[3] = "--use-cached-creds";
+ argv[4] = "--username";
+ argv[5] = username;
+ argv[6] = domain ? "--domain" : NULL;
+ argv[7] = domain;
+ argv[8] = NULL;
+ /* Spawn child process */
+ ret = g_spawn_async_with_pipes (NULL, argv, NULL,
+ G_SPAWN_FILE_AND_ARGV_ZERO,
+ NULL, NULL,
+ NULL, &conn->fd_in, &conn->fd_out,
+ NULL, NULL);
+ if (!ret)
+ goto done;
+ g_free (domain);
+ return TRUE;
+done:
+ g_free (domain);
+ return FALSE;
+}
+
+static char *
+sso_ntlm_response (SoupNTLMConnection *conn, const char *input, SoupNTLMState conn_state)
+{
+ ssize_t size;
+ char buf[1024], *response = NULL;
+ char *tmpbuf = buf;
+ size_t len_in = strlen (input), len_out = sizeof (buf);
+
+ while (len_in > 0) {
+ int written = write (conn->fd_in, input, len_in);
+ if (written == -1) {
+ /* Interrupted by a signal, retry it */
+ if (errno == EINTR)
+ continue;
+ /* write failed if other errors happen */
+ goto done;
+ }
+ input += written;
+ len_in -= written;
+ }
+ /* Read one line */
+ while (len_out > 0) {
+ size = read (conn->fd_out, tmpbuf, len_out);
+ if (size == -1) {
+ if (errno == EINTR)
+ continue;
+ goto done;
+ } else if (size == 0)
+ goto done;
+ else if (tmpbuf[size - 1] == '\n') {
+ tmpbuf[size - 1] = '\0';
+ goto wrfinish;
+ }
+ tmpbuf += size;
+ len_out -= size;
+ }
+ goto done;
+wrfinish:
+ if (conn_state == SOUP_NTLM_NEW &&
+ g_ascii_strcasecmp (buf, "PW") == 0) {
+ /* Samba/winbind installed but not configured */
+ response = g_strdup ("PW");
+ goto done;
+ }
+ if (conn_state == SOUP_NTLM_NEW &&
+ g_ascii_strncasecmp (buf, "YR ", 3) != 0)
+ /* invalid response for type 1 message */
+ goto done;
+ if (conn_state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE &&
+ g_ascii_strncasecmp (buf, "KK ", 3) != 0 &&
+ g_ascii_strncasecmp (buf, "AF ", 3) != 0)
+ /* invalid response for type 3 message */
+ goto done;
+
+ response = g_strdup_printf ("NTLM %.*s", (int)(size - 4), buf + 3);
+ goto done;
+done:
+ return response;
+}
+#endif /* USE_NTLM_AUTH */
+
+static void
+ntlm_authorize_pre (SoupMessage *msg, gpointer ntlm)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
+ SoupNTLMConnection *conn;
+ const char *val;
+
+ conn = get_connection_for_msg (priv, msg);
+ if (!conn)
+ return;
+
+ val = soup_message_headers_get_list (msg->response_headers,
+ "WWW-Authenticate");
+ if (val)
+ val = strstr (val, "NTLM ");
+ if (!val)
+ return;
+
+ if (conn->state > SOUP_NTLM_SENT_REQUEST) {
+ /* We already authenticated, but then got another 401.
+ * That means "permission denied", so don't try to
+ * authenticate again.
+ */
+ conn->state = SOUP_NTLM_FAILED;
+ goto done;
+ }
+
+ if (!soup_ntlm_parse_challenge (val, &conn->nonce, &conn->domain)) {
+ conn->state = SOUP_NTLM_FAILED;
+ goto done;
+ }
+
+ conn->auth = soup_auth_ntlm_new (conn->domain,
+ soup_message_get_uri (msg)->host);
+#ifdef USE_NTLM_AUTH
+ conn->challenge_header = g_strdup (val + 5);
+ if (conn->state == SOUP_NTLM_SENT_SSO_REQUEST) {
+ conn->state = SOUP_NTLM_RECEIVED_SSO_CHALLENGE;
+ goto done;
+ }
+#endif
+ conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
+ soup_auth_manager_emit_authenticate (SOUP_AUTH_MANAGER (ntlm), msg,
+ conn->auth, FALSE);
+
+ done:
+ /* Remove the WWW-Authenticate headers so the session won't try
+ * to do Basic auth too.
+ */
+ soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
+}
+
+static void
+ntlm_authorize_post (SoupMessage *msg, gpointer ntlm)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
+ SoupNTLMConnection *conn;
+ const char *username = NULL, *password = NULL;
+ char *slash, *domain = NULL;
+
+ conn = get_connection_for_msg (priv, msg);
+ if (!conn || !conn->auth)
+ return;
+
+#ifdef USE_NTLM_AUTH
+ if (conn->state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE) {
+ char *input;
+ input = g_strdup_printf ("TT %s\n", conn->challenge_header);
+ /* Re-Initiate ntlm_auth process in case it was closed/killed abnormally */
+ if (sso_ntlm_initiate (conn, priv)) {
+ conn->response_header = sso_ntlm_response (conn,
+ input,
+ conn->state);
+ /* Close ntlm_auth as it is no longer needed for current connection */
+ sso_ntlm_close (conn);
+ if (!conn->response_header) {
+ g_free (input);
+ goto ssofailure;
+ }
+ soup_session_requeue_message (priv->session, msg);
+ g_free (input);
+ goto done;
+ }
+ssofailure:
+ conn->state = SOUP_NTLM_SSO_FAILED;
+ soup_session_requeue_message (priv->session, msg);
+ goto done;
+ }
+#endif
+ username = soup_auth_ntlm_get_username (conn->auth);
+ password = soup_auth_ntlm_get_password (conn->auth);
+ if (!username || !password)
+ goto done;
+
+ slash = strpbrk (username, "\\/");
+ if (slash) {
+ domain = g_strdup (username);
+ slash = domain + (slash - username);
+ *slash = '\0';
+ username = slash + 1;
+ } else
+ domain = conn->domain;
+
+ conn->response_header = soup_ntlm_response (conn->nonce,
+ username, password,
+ NULL, domain);
+ soup_session_requeue_message (priv->session, msg);
+
+done:
+ if (domain != conn->domain)
+ g_free (domain);
+ g_free (conn->domain);
+ conn->domain = NULL;
+ g_free (conn->nonce);
+ conn->nonce = NULL;
+ g_object_unref (conn->auth);
+ conn->auth = NULL;
+}
+
+static void
+request_queued (SoupSessionFeature *ntlm, SoupSession *session, SoupMessage *msg)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
+
+ if (priv->use_ntlm) {
+ soup_message_add_status_code_handler (
+ msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
+ G_CALLBACK (ntlm_authorize_pre), ntlm);
+ soup_message_add_status_code_handler (
+ msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
+ G_CALLBACK (ntlm_authorize_post), ntlm);
+ }
+
+ soup_auth_manager_parent_feature_interface->request_queued (ntlm, session, msg);
+}
+
+static void
+request_started (SoupSessionFeature *ntlm, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
+ SoupNTLMConnection *conn;
+ char *header = NULL;
+
+ if (!priv->use_ntlm)
+ goto super;
+
+ conn = get_connection (priv, socket);
+ set_connection_for_msg (priv, msg, conn);
+
+ switch (conn->state) {
+ case SOUP_NTLM_NEW:
+#ifdef USE_NTLM_AUTH
+ /* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
+ * by delegating the NTLM challenge/response protocal to a helper
+ * in ntlm_auth.
+ * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
+ * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
+ * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
+ * The preprocessor variable 'USE_NTLM_AUTH' indicates whether
+ * this feature is enabled. Another one 'NTLM_AUTH' contains absolute
+ * path of it.
+ * If NTLM single-sign-on fails, go back to original request handling process.
+ */
+ if (sso_ntlm_initiate (conn, priv)) {
+ header = sso_ntlm_response (conn, "YR\n", conn->state);
+ if (header) {
+ if (g_ascii_strcasecmp (header, "PW") != 0) {
+ conn->state = SOUP_NTLM_SENT_SSO_REQUEST;
+ break;
+ } else {
+ g_free (header);
+ header = NULL;
+ goto ssofailure;
+ }
+ } else {
+ g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
+ goto ssofailure;
+ }
+ }
+ssofailure:
+#endif
+ header = soup_ntlm_request ();
+ conn->state = SOUP_NTLM_SENT_REQUEST;
+ break;
+#ifdef USE_NTLM_AUTH
+ case SOUP_NTLM_RECEIVED_SSO_CHALLENGE:
+ header = conn->response_header;
+ conn->response_header = NULL;
+ conn->state = SOUP_NTLM_SENT_SSO_RESPONSE;
+ break;
+ case SOUP_NTLM_SSO_FAILED:
+ /* Restart request without SSO */
+ g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
+ header = soup_ntlm_request ();
+ conn->state = SOUP_NTLM_SENT_REQUEST;
+ break;
+#endif
+ case SOUP_NTLM_RECEIVED_CHALLENGE:
+ header = conn->response_header;
+ conn->response_header = NULL;
+ conn->state = SOUP_NTLM_SENT_RESPONSE;
+ break;
+ default:
+ break;
+ }
+
+ if (header && !soup_message_get_auth (msg)) {
+ soup_message_headers_replace (msg->request_headers,
+ "Authorization", header);
+ g_free (header);
+ }
+
+super:
+ soup_auth_manager_parent_feature_interface->request_started (ntlm, session, msg, socket);
+}
+
+static void
+request_unqueued (SoupSessionFeature *ntlm, SoupSession *session,
+ SoupMessage *msg)
+{
+ g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, ntlm);
+ g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, ntlm);
+
+ soup_auth_manager_parent_feature_interface->request_unqueued (ntlm, session, msg);
+}
+
+static gboolean
+add_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
+
+ if (type == SOUP_TYPE_AUTH_NTLM) {
+ priv->use_ntlm = TRUE;
+ return TRUE;
+ }
+
+ return soup_auth_manager_parent_feature_interface->add_feature (feature, type);
+}
+
+static gboolean
+remove_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
+
+ if (type == SOUP_TYPE_AUTH_NTLM) {
+ priv->use_ntlm = FALSE;
+ return TRUE;
+ }
+
+ return soup_auth_manager_parent_feature_interface->remove_feature (feature, type);
+}
+
+static gboolean
+has_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupAuthManagerNTLMPrivate *priv =
+ SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
+
+ if (type == SOUP_TYPE_AUTH_NTLM)
+ return priv->use_ntlm;
+
+ return soup_auth_manager_parent_feature_interface->has_feature (feature, type);
+}
+
+/* NTLM code */
+
+static void md4sum (const unsigned char *in,
+ int nbytes,
+ unsigned char digest[16]);
+
+typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */
+
+static void deskey (DES_KS, unsigned char *, int);
+
+static void des (DES_KS, unsigned char *);
+
+static void setup_schedule (const guchar *key_56, DES_KS ks);
+
+static void calc_response (const guchar *key,
+ const guchar *plaintext,
+ guchar *results);
+
+#define LM_PASSWORD_MAGIC "\x4B\x47\x53\x21\x40\x23\x24\x25" \
+ "\x4B\x47\x53\x21\x40\x23\x24\x25" \
+ "\x00\x00\x00\x00\x00"
+
+static void
+lanmanager_hash (const char *password, guchar hash[21])
+{
+ guchar lm_password [15];
+ DES_KS ks;
+ int i;
+
+ for (i = 0; i < 14 && password [i]; i++)
+ lm_password [i] = toupper ((unsigned char) password [i]);
+
+ for (; i < 15; i++)
+ lm_password [i] = '\0';
+
+ memcpy (hash, LM_PASSWORD_MAGIC, 21);
+
+ setup_schedule (lm_password, ks);
+ des (ks, hash);
+
+ setup_schedule (lm_password + 7, ks);
+ des (ks, hash + 8);
+}
+
+static void
+nt_hash (const char *password, guchar hash[21])
+{
+ unsigned char *buf, *p;
+
+ p = buf = g_malloc (strlen (password) * 2);
+
+ while (*password) {
+ *p++ = *password++;
+ *p++ = '\0';
+ }
+
+ md4sum (buf, p - buf, hash);
+ memset (hash + 16, 0, 5);
+
+ g_free (buf);
+}
+
+typedef struct {
+ guint16 length;
+ guint16 length2;
+ guint16 offset;
+ guchar zero_pad[2];
+} NTLMString;
+
+#define NTLM_CHALLENGE_NONCE_OFFSET 24
+#define NTLM_CHALLENGE_NONCE_LENGTH 8
+#define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
+
+#define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
+#define NTLM_RESPONSE_FLAGS 0x8202
+
+typedef struct {
+ guchar header[12];
+
+ NTLMString lm_resp;
+ NTLMString nt_resp;
+ NTLMString domain;
+ NTLMString user;
+ NTLMString host;
+ NTLMString session_key;
+
+ guint32 flags;
+} NTLMResponse;
+
+static void
+ntlm_set_string (NTLMString *string, int *offset, int len)
+{
+ string->offset = GUINT16_TO_LE (*offset);
+ string->length = string->length2 = GUINT16_TO_LE (len);
+ *offset += len;
+}
+
+static char *
+soup_ntlm_request (void)
+{
+ return g_strdup ("NTLM TlRMTVNTUAABAAAABoIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
+}
+
+static gboolean
+soup_ntlm_parse_challenge (const char *challenge,
+ char **nonce,
+ char **default_domain)
+{
+ gsize clen;
+ NTLMString domain;
+ guchar *chall;
+
+ if (strncmp (challenge, "NTLM ", 5) != 0)
+ return FALSE;
+
+ chall = g_base64_decode (challenge + 5, &clen);
+ if (clen < NTLM_CHALLENGE_DOMAIN_STRING_OFFSET ||
+ clen < NTLM_CHALLENGE_NONCE_OFFSET + NTLM_CHALLENGE_NONCE_LENGTH) {
+ g_free (chall);
+ return FALSE;
+ }
+
+ if (default_domain) {
+ memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
+ domain.length = GUINT16_FROM_LE (domain.length);
+ domain.offset = GUINT16_FROM_LE (domain.offset);
+
+ if (clen < domain.length + domain.offset) {
+ g_free (chall);
+ return FALSE;
+ }
+
+ *default_domain = g_strndup ((char *)chall + domain.offset, domain.length);
+ }
+
+ if (nonce) {
+ *nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
+ NTLM_CHALLENGE_NONCE_LENGTH);
+ }
+
+ g_free (chall);
+ return TRUE;
+}
+
+static char *
+soup_ntlm_response (const char *nonce,
+ const char *user,
+ const char *password,
+ const char *host,
+ const char *domain)
+{
+ int hlen, dlen, ulen, offset;
+ guchar hash[21], lm_resp[24], nt_resp[24];
+ NTLMResponse resp;
+ char *out, *p;
+ int state, save;
+
+ nt_hash (password, hash);
+ calc_response (hash, (guchar *)nonce, nt_resp);
+ lanmanager_hash (password, hash);
+ calc_response (hash, (guchar *)nonce, lm_resp);
+
+ memset (&resp, 0, sizeof (resp));
+ memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
+ resp.flags = GUINT32_TO_LE (NTLM_RESPONSE_FLAGS);
+
+ offset = sizeof (resp);
+
+ dlen = strlen (domain);
+ ntlm_set_string (&resp.domain, &offset, dlen);
+ ulen = strlen (user);
+ ntlm_set_string (&resp.user, &offset, ulen);
+ if (!host)
+ host = "UNKNOWN";
+ hlen = strlen (host);
+ ntlm_set_string (&resp.host, &offset, hlen);
+ ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp));
+ ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp));
+
+ out = g_malloc (((offset + 3) * 4) / 3 + 6);
+ strncpy (out, "NTLM ", 5);
+ p = out + 5;
+
+ state = save = 0;
+
+ p += g_base64_encode_step ((const guchar *) &resp, sizeof (resp),
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step ((const guchar *) domain, dlen,
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step ((const guchar *) user, ulen,
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step ((const guchar *) host, hlen,
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step (lm_resp, sizeof (lm_resp),
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step (nt_resp, sizeof (nt_resp),
+ FALSE, p, &state, &save);
+ p += g_base64_encode_close (FALSE, p, &state, &save);
+ *p = '\0';
+
+ return out;
+}
+
+/* DES utils */
+/* Set up a key schedule based on a 56bit key */
+static void
+setup_schedule (const guchar *key_56, DES_KS ks)
+{
+ guchar key[8];
+ int i, c, bit;
+
+ key[0] = (key_56[0]) ;
+ key[1] = (key_56[1] >> 1) | ((key_56[0] << 7) & 0xFF);
+ key[2] = (key_56[2] >> 2) | ((key_56[1] << 6) & 0xFF);
+ key[3] = (key_56[3] >> 3) | ((key_56[2] << 5) & 0xFF);
+ key[4] = (key_56[4] >> 4) | ((key_56[3] << 4) & 0xFF);
+ key[5] = (key_56[5] >> 5) | ((key_56[4] << 3) & 0xFF);
+ key[6] = (key_56[6] >> 6) | ((key_56[5] << 2) & 0xFF);
+ key[7] = ((key_56[6] << 1) & 0xFF);
+
+ /* Fix parity */
+ for (i = 0; i < 8; i++) {
+ for (c = bit = 0; bit < 8; bit++)
+ if (key[i] & (1 << bit))
+ c++;
+ if (!(c & 1))
+ key[i] ^= 0x01;
+ }
+
+ deskey (ks, key, 0);
+}
+
+static void
+calc_response (const guchar *key, const guchar *plaintext, guchar *results)
+{
+ DES_KS ks;
+
+ memcpy (results, plaintext, 8);
+ memcpy (results + 8, plaintext, 8);
+ memcpy (results + 16, plaintext, 8);
+
+ setup_schedule (key, ks);
+ des (ks, results);
+
+ setup_schedule (key + 7, ks);
+ des (ks, results + 8);
+
+ setup_schedule (key + 14, ks);
+ des (ks, results + 16);
+}
+
+
+/*
+ * MD4 encoder. (The one everyone else uses is not GPL-compatible;
+ * this is a reimplementation from spec.) This doesn't need to be
+ * efficient for our purposes, although it would be nice to fix
+ * it to not malloc()...
+ */
+
+#define F(X,Y,Z) ( ((X)&(Y)) | ((~(X))&(Z)) )
+#define G(X,Y,Z) ( ((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)) )
+#define H(X,Y,Z) ( (X)^(Y)^(Z) )
+#define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) )
+
+static void
+md4sum (const unsigned char *in, int nbytes, unsigned char digest[16])
+{
+ unsigned char *M;
+ guint32 A, B, C, D, AA, BB, CC, DD, X[16];
+ int pbytes, nbits = nbytes * 8, i, j;
+
+ pbytes = (120 - (nbytes % 64)) % 64;
+ M = alloca (nbytes + pbytes + 8);
+ memcpy (M, in, nbytes);
+ memset (M + nbytes, 0, pbytes + 8);
+ M[nbytes] = 0x80;
+ M[nbytes + pbytes] = nbits & 0xFF;
+ M[nbytes + pbytes + 1] = (nbits >> 8) & 0xFF;
+ M[nbytes + pbytes + 2] = (nbits >> 16) & 0xFF;
+ M[nbytes + pbytes + 3] = (nbits >> 24) & 0xFF;
+
+ A = 0x67452301;
+ B = 0xEFCDAB89;
+ C = 0x98BADCFE;
+ D = 0x10325476;
+
+ for (i = 0; i < nbytes + pbytes + 8; i += 64) {
+ for (j = 0; j < 16; j++) {
+ X[j] = (M[i + j*4]) |
+ (M[i + j*4 + 1] << 8) |
+ (M[i + j*4 + 2] << 16) |
+ (M[i + j*4 + 3] << 24);
+ }
+
+ AA = A;
+ BB = B;
+ CC = C;
+ DD = D;
+
+ A = ROT (A + F(B, C, D) + X[0], 3);
+ D = ROT (D + F(A, B, C) + X[1], 7);
+ C = ROT (C + F(D, A, B) + X[2], 11);
+ B = ROT (B + F(C, D, A) + X[3], 19);
+ A = ROT (A + F(B, C, D) + X[4], 3);
+ D = ROT (D + F(A, B, C) + X[5], 7);
+ C = ROT (C + F(D, A, B) + X[6], 11);
+ B = ROT (B + F(C, D, A) + X[7], 19);
+ A = ROT (A + F(B, C, D) + X[8], 3);
+ D = ROT (D + F(A, B, C) + X[9], 7);
+ C = ROT (C + F(D, A, B) + X[10], 11);
+ B = ROT (B + F(C, D, A) + X[11], 19);
+ A = ROT (A + F(B, C, D) + X[12], 3);
+ D = ROT (D + F(A, B, C) + X[13], 7);
+ C = ROT (C + F(D, A, B) + X[14], 11);
+ B = ROT (B + F(C, D, A) + X[15], 19);
+
+ A = ROT (A + G(B, C, D) + X[0] + 0x5A827999, 3);
+ D = ROT (D + G(A, B, C) + X[4] + 0x5A827999, 5);
+ C = ROT (C + G(D, A, B) + X[8] + 0x5A827999, 9);
+ B = ROT (B + G(C, D, A) + X[12] + 0x5A827999, 13);
+ A = ROT (A + G(B, C, D) + X[1] + 0x5A827999, 3);
+ D = ROT (D + G(A, B, C) + X[5] + 0x5A827999, 5);
+ C = ROT (C + G(D, A, B) + X[9] + 0x5A827999, 9);
+ B = ROT (B + G(C, D, A) + X[13] + 0x5A827999, 13);
+ A = ROT (A + G(B, C, D) + X[2] + 0x5A827999, 3);
+ D = ROT (D + G(A, B, C) + X[6] + 0x5A827999, 5);
+ C = ROT (C + G(D, A, B) + X[10] + 0x5A827999, 9);
+ B = ROT (B + G(C, D, A) + X[14] + 0x5A827999, 13);
+ A = ROT (A + G(B, C, D) + X[3] + 0x5A827999, 3);
+ D = ROT (D + G(A, B, C) + X[7] + 0x5A827999, 5);
+ C = ROT (C + G(D, A, B) + X[11] + 0x5A827999, 9);
+ B = ROT (B + G(C, D, A) + X[15] + 0x5A827999, 13);
+
+ A = ROT (A + H(B, C, D) + X[0] + 0x6ED9EBA1, 3);
+ D = ROT (D + H(A, B, C) + X[8] + 0x6ED9EBA1, 9);
+ C = ROT (C + H(D, A, B) + X[4] + 0x6ED9EBA1, 11);
+ B = ROT (B + H(C, D, A) + X[12] + 0x6ED9EBA1, 15);
+ A = ROT (A + H(B, C, D) + X[2] + 0x6ED9EBA1, 3);
+ D = ROT (D + H(A, B, C) + X[10] + 0x6ED9EBA1, 9);
+ C = ROT (C + H(D, A, B) + X[6] + 0x6ED9EBA1, 11);
+ B = ROT (B + H(C, D, A) + X[14] + 0x6ED9EBA1, 15);
+ A = ROT (A + H(B, C, D) + X[1] + 0x6ED9EBA1, 3);
+ D = ROT (D + H(A, B, C) + X[9] + 0x6ED9EBA1, 9);
+ C = ROT (C + H(D, A, B) + X[5] + 0x6ED9EBA1, 11);
+ B = ROT (B + H(C, D, A) + X[13] + 0x6ED9EBA1, 15);
+ A = ROT (A + H(B, C, D) + X[3] + 0x6ED9EBA1, 3);
+ D = ROT (D + H(A, B, C) + X[11] + 0x6ED9EBA1, 9);
+ C = ROT (C + H(D, A, B) + X[7] + 0x6ED9EBA1, 11);
+ B = ROT (B + H(C, D, A) + X[15] + 0x6ED9EBA1, 15);
+
+ A += AA;
+ B += BB;
+ C += CC;
+ D += DD;
+ }
+
+ digest[0] = A & 0xFF;
+ digest[1] = (A >> 8) & 0xFF;
+ digest[2] = (A >> 16) & 0xFF;
+ digest[3] = (A >> 24) & 0xFF;
+ digest[4] = B & 0xFF;
+ digest[5] = (B >> 8) & 0xFF;
+ digest[6] = (B >> 16) & 0xFF;
+ digest[7] = (B >> 24) & 0xFF;
+ digest[8] = C & 0xFF;
+ digest[9] = (C >> 8) & 0xFF;
+ digest[10] = (C >> 16) & 0xFF;
+ digest[11] = (C >> 24) & 0xFF;
+ digest[12] = D & 0xFF;
+ digest[13] = (D >> 8) & 0xFF;
+ digest[14] = (D >> 16) & 0xFF;
+ digest[15] = (D >> 24) & 0xFF;
+}
+
+
+/* Public domain DES implementation from Phil Karn */
+static const guint32 Spbox[8][64] = {
+ { 0x01010400,0x00000000,0x00010000,0x01010404,
+ 0x01010004,0x00010404,0x00000004,0x00010000,
+ 0x00000400,0x01010400,0x01010404,0x00000400,
+ 0x01000404,0x01010004,0x01000000,0x00000004,
+ 0x00000404,0x01000400,0x01000400,0x00010400,
+ 0x00010400,0x01010000,0x01010000,0x01000404,
+ 0x00010004,0x01000004,0x01000004,0x00010004,
+ 0x00000000,0x00000404,0x00010404,0x01000000,
+ 0x00010000,0x01010404,0x00000004,0x01010000,
+ 0x01010400,0x01000000,0x01000000,0x00000400,
+ 0x01010004,0x00010000,0x00010400,0x01000004,
+ 0x00000400,0x00000004,0x01000404,0x00010404,
+ 0x01010404,0x00010004,0x01010000,0x01000404,
+ 0x01000004,0x00000404,0x00010404,0x01010400,
+ 0x00000404,0x01000400,0x01000400,0x00000000,
+ 0x00010004,0x00010400,0x00000000,0x01010004 },
+ { 0x80108020,0x80008000,0x00008000,0x00108020,
+ 0x00100000,0x00000020,0x80100020,0x80008020,
+ 0x80000020,0x80108020,0x80108000,0x80000000,
+ 0x80008000,0x00100000,0x00000020,0x80100020,
+ 0x00108000,0x00100020,0x80008020,0x00000000,
+ 0x80000000,0x00008000,0x00108020,0x80100000,
+ 0x00100020,0x80000020,0x00000000,0x00108000,
+ 0x00008020,0x80108000,0x80100000,0x00008020,
+ 0x00000000,0x00108020,0x80100020,0x00100000,
+ 0x80008020,0x80100000,0x80108000,0x00008000,
+ 0x80100000,0x80008000,0x00000020,0x80108020,
+ 0x00108020,0x00000020,0x00008000,0x80000000,
+ 0x00008020,0x80108000,0x00100000,0x80000020,
+ 0x00100020,0x80008020,0x80000020,0x00100020,
+ 0x00108000,0x00000000,0x80008000,0x00008020,
+ 0x80000000,0x80100020,0x80108020,0x00108000 },
+ { 0x00000208,0x08020200,0x00000000,0x08020008,
+ 0x08000200,0x00000000,0x00020208,0x08000200,
+ 0x00020008,0x08000008,0x08000008,0x00020000,
+ 0x08020208,0x00020008,0x08020000,0x00000208,
+ 0x08000000,0x00000008,0x08020200,0x00000200,
+ 0x00020200,0x08020000,0x08020008,0x00020208,
+ 0x08000208,0x00020200,0x00020000,0x08000208,
+ 0x00000008,0x08020208,0x00000200,0x08000000,
+ 0x08020200,0x08000000,0x00020008,0x00000208,
+ 0x00020000,0x08020200,0x08000200,0x00000000,
+ 0x00000200,0x00020008,0x08020208,0x08000200,
+ 0x08000008,0x00000200,0x00000000,0x08020008,
+ 0x08000208,0x00020000,0x08000000,0x08020208,
+ 0x00000008,0x00020208,0x00020200,0x08000008,
+ 0x08020000,0x08000208,0x00000208,0x08020000,
+ 0x00020208,0x00000008,0x08020008,0x00020200 },
+ { 0x00802001,0x00002081,0x00002081,0x00000080,
+ 0x00802080,0x00800081,0x00800001,0x00002001,
+ 0x00000000,0x00802000,0x00802000,0x00802081,
+ 0x00000081,0x00000000,0x00800080,0x00800001,
+ 0x00000001,0x00002000,0x00800000,0x00802001,
+ 0x00000080,0x00800000,0x00002001,0x00002080,
+ 0x00800081,0x00000001,0x00002080,0x00800080,
+ 0x00002000,0x00802080,0x00802081,0x00000081,
+ 0x00800080,0x00800001,0x00802000,0x00802081,
+ 0x00000081,0x00000000,0x00000000,0x00802000,
+ 0x00002080,0x00800080,0x00800081,0x00000001,
+ 0x00802001,0x00002081,0x00002081,0x00000080,
+ 0x00802081,0x00000081,0x00000001,0x00002000,
+ 0x00800001,0x00002001,0x00802080,0x00800081,
+ 0x00002001,0x00002080,0x00800000,0x00802001,
+ 0x00000080,0x00800000,0x00002000,0x00802080 },
+ { 0x00000100,0x02080100,0x02080000,0x42000100,
+ 0x00080000,0x00000100,0x40000000,0x02080000,
+ 0x40080100,0x00080000,0x02000100,0x40080100,
+ 0x42000100,0x42080000,0x00080100,0x40000000,
+ 0x02000000,0x40080000,0x40080000,0x00000000,
+ 0x40000100,0x42080100,0x42080100,0x02000100,
+ 0x42080000,0x40000100,0x00000000,0x42000000,
+ 0x02080100,0x02000000,0x42000000,0x00080100,
+ 0x00080000,0x42000100,0x00000100,0x02000000,
+ 0x40000000,0x02080000,0x42000100,0x40080100,
+ 0x02000100,0x40000000,0x42080000,0x02080100,
+ 0x40080100,0x00000100,0x02000000,0x42080000,
+ 0x42080100,0x00080100,0x42000000,0x42080100,
+ 0x02080000,0x00000000,0x40080000,0x42000000,
+ 0x00080100,0x02000100,0x40000100,0x00080000,
+ 0x00000000,0x40080000,0x02080100,0x40000100 },
+ { 0x20000010,0x20400000,0x00004000,0x20404010,
+ 0x20400000,0x00000010,0x20404010,0x00400000,
+ 0x20004000,0x00404010,0x00400000,0x20000010,
+ 0x00400010,0x20004000,0x20000000,0x00004010,
+ 0x00000000,0x00400010,0x20004010,0x00004000,
+ 0x00404000,0x20004010,0x00000010,0x20400010,
+ 0x20400010,0x00000000,0x00404010,0x20404000,
+ 0x00004010,0x00404000,0x20404000,0x20000000,
+ 0x20004000,0x00000010,0x20400010,0x00404000,
+ 0x20404010,0x00400000,0x00004010,0x20000010,
+ 0x00400000,0x20004000,0x20000000,0x00004010,
+ 0x20000010,0x20404010,0x00404000,0x20400000,
+ 0x00404010,0x20404000,0x00000000,0x20400010,
+ 0x00000010,0x00004000,0x20400000,0x00404010,
+ 0x00004000,0x00400010,0x20004010,0x00000000,
+ 0x20404000,0x20000000,0x00400010,0x20004010 },
+ { 0x00200000,0x04200002,0x04000802,0x00000000,
+ 0x00000800,0x04000802,0x00200802,0x04200800,
+ 0x04200802,0x00200000,0x00000000,0x04000002,
+ 0x00000002,0x04000000,0x04200002,0x00000802,
+ 0x04000800,0x00200802,0x00200002,0x04000800,
+ 0x04000002,0x04200000,0x04200800,0x00200002,
+ 0x04200000,0x00000800,0x00000802,0x04200802,
+ 0x00200800,0x00000002,0x04000000,0x00200800,
+ 0x04000000,0x00200800,0x00200000,0x04000802,
+ 0x04000802,0x04200002,0x04200002,0x00000002,
+ 0x00200002,0x04000000,0x04000800,0x00200000,
+ 0x04200800,0x00000802,0x00200802,0x04200800,
+ 0x00000802,0x04000002,0x04200802,0x04200000,
+ 0x00200800,0x00000000,0x00000002,0x04200802,
+ 0x00000000,0x00200802,0x04200000,0x00000800,
+ 0x04000002,0x04000800,0x00000800,0x00200002 },
+ { 0x10001040,0x00001000,0x00040000,0x10041040,
+ 0x10000000,0x10001040,0x00000040,0x10000000,
+ 0x00040040,0x10040000,0x10041040,0x00041000,
+ 0x10041000,0x00041040,0x00001000,0x00000040,
+ 0x10040000,0x10000040,0x10001000,0x00001040,
+ 0x00041000,0x00040040,0x10040040,0x10041000,
+ 0x00001040,0x00000000,0x00000000,0x10040040,
+ 0x10000040,0x10001000,0x00041040,0x00040000,
+ 0x00041040,0x00040000,0x10041000,0x00001000,
+ 0x00000040,0x10040040,0x00001000,0x00041040,
+ 0x10001000,0x00000040,0x10000040,0x10040000,
+ 0x10040040,0x10000000,0x00040000,0x10001040,
+ 0x00000000,0x10041040,0x00040040,0x10000040,
+ 0x10040000,0x10001000,0x10001040,0x00000000,
+ 0x10041040,0x00041000,0x00041000,0x00001040,
+ 0x00001040,0x00040040,0x10000000,0x10041000 }
+};
+
+#undef F
+#define F(l,r,key){\
+ work = ((r >> 4) | (r << 28)) ^ key[0];\
+ l ^= Spbox[6][work & 0x3f];\
+ l ^= Spbox[4][(work >> 8) & 0x3f];\
+ l ^= Spbox[2][(work >> 16) & 0x3f];\
+ l ^= Spbox[0][(work >> 24) & 0x3f];\
+ work = r ^ key[1];\
+ l ^= Spbox[7][work & 0x3f];\
+ l ^= Spbox[5][(work >> 8) & 0x3f];\
+ l ^= Spbox[3][(work >> 16) & 0x3f];\
+ l ^= Spbox[1][(work >> 24) & 0x3f];\
+}
+/* Encrypt or decrypt a block of data in ECB mode */
+static void
+des (guint32 ks[16][2], unsigned char block[8])
+{
+ guint32 left,right,work;
+
+ /* Read input block and place in left/right in big-endian order */
+ left = ((guint32)block[0] << 24)
+ | ((guint32)block[1] << 16)
+ | ((guint32)block[2] << 8)
+ | (guint32)block[3];
+ right = ((guint32)block[4] << 24)
+ | ((guint32)block[5] << 16)
+ | ((guint32)block[6] << 8)
+ | (guint32)block[7];
+
+ /* Hoey's clever initial permutation algorithm, from Outerbridge
+ * (see Schneier p 478)
+ *
+ * The convention here is the same as Outerbridge: rotate each
+ * register left by 1 bit, i.e., so that "left" contains permuted
+ * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32
+ * (using origin-1 numbering as in the FIPS). This allows us to avoid
+ * one of the two rotates that would otherwise be required in each of
+ * the 16 rounds.
+ */
+ work = ((left >> 4) ^ right) & 0x0f0f0f0f;
+ right ^= work;
+ left ^= work << 4;
+ work = ((left >> 16) ^ right) & 0xffff;
+ right ^= work;
+ left ^= work << 16;
+ work = ((right >> 2) ^ left) & 0x33333333;
+ left ^= work;
+ right ^= (work << 2);
+ work = ((right >> 8) ^ left) & 0xff00ff;
+ left ^= work;
+ right ^= (work << 8);
+ right = (right << 1) | (right >> 31);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left << 1) | (left >> 31);
+
+ /* Now do the 16 rounds */
+ F(left,right,ks[0]);
+ F(right,left,ks[1]);
+ F(left,right,ks[2]);
+ F(right,left,ks[3]);
+ F(left,right,ks[4]);
+ F(right,left,ks[5]);
+ F(left,right,ks[6]);
+ F(right,left,ks[7]);
+ F(left,right,ks[8]);
+ F(right,left,ks[9]);
+ F(left,right,ks[10]);
+ F(right,left,ks[11]);
+ F(left,right,ks[12]);
+ F(right,left,ks[13]);
+ F(left,right,ks[14]);
+ F(right,left,ks[15]);
+
+ /* Inverse permutation, also from Hoey via Outerbridge and Schneier */
+ right = (right << 31) | (right >> 1);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left >> 1) | (left << 31);
+ work = ((left >> 8) ^ right) & 0xff00ff;
+ right ^= work;
+ left ^= work << 8;
+ work = ((left >> 2) ^ right) & 0x33333333;
+ right ^= work;
+ left ^= work << 2;
+ work = ((right >> 16) ^ left) & 0xffff;
+ left ^= work;
+ right ^= work << 16;
+ work = ((right >> 4) ^ left) & 0x0f0f0f0f;
+ left ^= work;
+ right ^= work << 4;
+
+ /* Put the block back into the user's buffer with final swap */
+ block[0] = right >> 24;
+ block[1] = right >> 16;
+ block[2] = right >> 8;
+ block[3] = right;
+ block[4] = left >> 24;
+ block[5] = left >> 16;
+ block[6] = left >> 8;
+ block[7] = left;
+}
+
+/* Key schedule-related tables from FIPS-46 */
+
+/* permuted choice table (key) */
+static const unsigned char pc1[] = {
+ 57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4
+};
+
+/* number left rotations of pc1 */
+static const unsigned char totrot[] = {
+ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
+};
+
+/* permuted choice key (table) */
+static const unsigned char pc2[] = {
+ 14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32
+};
+
+/* End of DES-defined tables */
+
+
+/* bit 0 is left-most in byte */
+static const int bytebit[] = {
+ 0200,0100,040,020,010,04,02,01
+};
+
+
+/* Generate key schedule for encryption or decryption
+ * depending on the value of "decrypt"
+ */
+static void
+deskey (DES_KS k, unsigned char *key, int decrypt)
+{
+ unsigned char pc1m[56]; /* place to modify pc1 into */
+ unsigned char pcr[56]; /* place to rotate pc1 into */
+ register int i,j,l;
+ int m;
+ unsigned char ks[8];
+
+ for (j=0; j<56; j++) { /* convert pc1 to bits of key */
+ l=pc1[j]-1; /* integer bit location */
+ m = l & 07; /* find bit */
+ pc1m[j]=(key[l>>3] & /* find which key byte l is in */
+ bytebit[m]) /* and which bit of that byte */
+ ? 1 : 0; /* and store 1-bit result */
+ }
+ for (i=0; i<16; i++) { /* key chunk for each iteration */
+ memset(ks,0,sizeof(ks)); /* Clear key schedule */
+ for (j=0; j<56; j++) /* rotate pc1 the right amount */
+ pcr[j] = pc1m[(l=j+totrot[decrypt? 15-i : i])<(j<28? 28 : 56) ? l: l-28];
+ /* rotate left and right halves independently */
+ for (j=0; j<48; j++){ /* select bits individually */
+ /* check bit that goes to ks[j] */
+ if (pcr[pc2[j]-1]){
+ /* mask it in if it's there */
+ l= j % 6;
+ ks[j/6] |= bytebit[l] >> 2;
+ }
+ }
+ /* Now convert to packed odd/even interleaved form */
+ k[i][0] = ((guint32)ks[0] << 24)
+ | ((guint32)ks[2] << 16)
+ | ((guint32)ks[4] << 8)
+ | ((guint32)ks[6]);
+ k[i][1] = ((guint32)ks[1] << 24)
+ | ((guint32)ks[3] << 16)
+ | ((guint32)ks[5] << 8)
+ | ((guint32)ks[7]);
+ }
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_AUTH_MANAGER_NTLM_H
+#define SOUP_AUTH_MANAGER_NTLM_H 1
+
+#include "soup-auth-manager.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_AUTH_MANAGER_NTLM (soup_auth_manager_ntlm_get_type ())
+#define SOUP_AUTH_MANAGER_NTLM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLM))
+#define SOUP_AUTH_MANAGER_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMClass))
+#define SOUP_IS_AUTH_MANAGER_NTLM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_MANAGER_NTLM))
+#define SOUP_IS_AUTH_MANAGER_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_MANAGER_NTLM))
+#define SOUP_AUTH_MANAGER_NTLM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMClass))
+
+typedef struct {
+ SoupAuthManager parent;
+
+} SoupAuthManagerNTLM;
+
+typedef struct {
+ SoupAuthManagerClass parent_class;
+
+} SoupAuthManagerNTLMClass;
+
+#define SOUP_AUTH_MANAGER_NTLM_USE_NTLM "use-ntlm"
+
+GType soup_auth_manager_ntlm_get_type (void);
+
+G_END_DECLS
+
+#endif /* SOUP_AUTH_MANAGER_NTLM_NTLM_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-manager.c: SoupAuth manager for SoupSession
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-manager.h"
+#include "soup-address.h"
+#include "soup-headers.h"
+#include "soup-marshal.h"
+#include "soup-message-private.h"
+#include "soup-message-queue.h"
+#include "soup-path-map.h"
+#include "soup-session.h"
+#include "soup-session-feature.h"
+#include "soup-session-private.h"
+#include "soup-uri.h"
+
+static void soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static SoupSessionFeatureInterface *soup_session_feature_default_interface;
+
+static void attach (SoupSessionFeature *feature, SoupSession *session);
+static void request_queued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg);
+static void request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket);
+static void request_unqueued (SoupSessionFeature *feature,
+ SoupSession *session, SoupMessage *msg);
+static gboolean add_feature (SoupSessionFeature *feature, GType type);
+static gboolean remove_feature (SoupSessionFeature *feature, GType type);
+static gboolean has_feature (SoupSessionFeature *feature, GType type);
+
+enum {
+ AUTHENTICATE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_CODE (SoupAuthManager, soup_auth_manager, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_auth_manager_session_feature_init))
+
+typedef struct {
+ SoupSession *session;
+ GPtrArray *auth_types;
+
+ SoupAuth *proxy_auth;
+ GHashTable *auth_hosts;
+} SoupAuthManagerPrivate;
+#define SOUP_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerPrivate))
+
+typedef struct {
+ SoupURI *uri;
+ SoupPathMap *auth_realms; /* path -> scheme:realm */
+ GHashTable *auths; /* scheme:realm -> SoupAuth */
+} SoupAuthHost;
+
+static void
+soup_auth_manager_init (SoupAuthManager *manager)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+
+ priv->auth_types = g_ptr_array_new ();
+ priv->auth_hosts = g_hash_table_new (soup_uri_host_hash,
+ soup_uri_host_equal);
+}
+
+static gboolean
+foreach_free_host (gpointer key, gpointer value, gpointer data)
+{
+ SoupAuthHost *host = value;
+
+ if (host->auth_realms)
+ soup_path_map_free (host->auth_realms);
+ if (host->auths)
+ g_hash_table_destroy (host->auths);
+
+ soup_uri_free (host->uri);
+ g_slice_free (SoupAuthHost, host);
+
+ return TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (object);
+ int i;
+
+ for (i = 0; i < priv->auth_types->len; i++)
+ g_type_class_unref (priv->auth_types->pdata[i]);
+ g_ptr_array_free (priv->auth_types, TRUE);
+
+ g_hash_table_foreach_remove (priv->auth_hosts, foreach_free_host, NULL);
+ g_hash_table_destroy (priv->auth_hosts);
+
+ if (priv->proxy_auth)
+ g_object_unref (priv->proxy_auth);
+
+ G_OBJECT_CLASS (soup_auth_manager_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_manager_class_init (SoupAuthManagerClass *auth_manager_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_class);
+
+ g_type_class_add_private (auth_manager_class, sizeof (SoupAuthManagerPrivate));
+
+ object_class->finalize = finalize;
+
+ signals[AUTHENTICATE] =
+ g_signal_new ("authenticate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupAuthManagerClass, authenticate),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
+ G_TYPE_NONE, 3,
+ SOUP_TYPE_MESSAGE,
+ SOUP_TYPE_AUTH,
+ G_TYPE_BOOLEAN);
+
+}
+
+static void
+soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ soup_session_feature_default_interface =
+ g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
+
+ feature_interface->attach = attach;
+ feature_interface->request_queued = request_queued;
+ feature_interface->request_started = request_started;
+ feature_interface->request_unqueued = request_unqueued;
+ feature_interface->add_feature = add_feature;
+ feature_interface->remove_feature = remove_feature;
+ feature_interface->has_feature = has_feature;
+}
+
+static int
+auth_type_compare_func (gconstpointer a, gconstpointer b)
+{
+ SoupAuthClass **auth1 = (SoupAuthClass **)a;
+ SoupAuthClass **auth2 = (SoupAuthClass **)b;
+
+ return (*auth1)->strength - (*auth2)->strength;
+}
+
+static gboolean
+add_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+ SoupAuthClass *auth_class;
+
+ if (!g_type_is_a (type, SOUP_TYPE_AUTH))
+ return FALSE;
+
+ auth_class = g_type_class_ref (type);
+ g_ptr_array_add (priv->auth_types, auth_class);
+ g_ptr_array_sort (priv->auth_types, auth_type_compare_func);
+ return TRUE;
+}
+
+static gboolean
+remove_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+ SoupAuthClass *auth_class;
+ int i;
+
+ if (!g_type_is_a (type, SOUP_TYPE_AUTH))
+ return FALSE;
+
+ auth_class = g_type_class_peek (type);
+ for (i = 0; i < priv->auth_types->len; i++) {
+ if (priv->auth_types->pdata[i] == (gpointer)auth_class) {
+ g_ptr_array_remove_index (priv->auth_types, i);
+ g_type_class_unref (auth_class);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+has_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+ SoupAuthClass *auth_class;
+ int i;
+
+ if (!g_type_is_a (type, SOUP_TYPE_AUTH))
+ return FALSE;
+
+ auth_class = g_type_class_peek (type);
+ for (i = 0; i < priv->auth_types->len; i++) {
+ if (priv->auth_types->pdata[i] == (gpointer)auth_class)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void
+soup_auth_manager_emit_authenticate (SoupAuthManager *manager, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying)
+{
+ g_signal_emit (manager, signals[AUTHENTICATE], 0, msg, auth, retrying);
+}
+
+static void
+attach (SoupSessionFeature *manager, SoupSession *session)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+
+ /* FIXME: should support multiple sessions */
+ priv->session = session;
+
+ soup_session_feature_default_interface->attach (manager, session);
+}
+
+static inline const char *
+auth_header_for_message (SoupMessage *msg)
+{
+ if (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
+ return soup_message_headers_get_list (msg->response_headers,
+ "Proxy-Authenticate");
+ } else {
+ return soup_message_headers_get_list (msg->response_headers,
+ "WWW-Authenticate");
+ }
+}
+
+static char *
+extract_challenge (const char *challenges, const char *scheme)
+{
+ GSList *items, *i;
+ int schemelen = strlen (scheme);
+ char *item, *space, *equals;
+ GString *challenge;
+
+ /* The relevant grammar:
+ *
+ * WWW-Authenticate = 1#challenge
+ * Proxy-Authenticate = 1#challenge
+ * challenge = auth-scheme 1#auth-param
+ * auth-scheme = token
+ * auth-param = token "=" ( token | quoted-string )
+ *
+ * The fact that quoted-strings can contain commas, equals
+ * signs, and auth scheme names makes it tricky to "cheat" on
+ * the parsing. We just use soup_header_parse_list(), and then
+ * reassemble the pieces after we find the one we want.
+ */
+
+ items = soup_header_parse_list (challenges);
+
+ /* First item will start with the scheme name, followed by a
+ * space and then the first auth-param.
+ */
+ for (i = items; i; i = i->next) {
+ item = i->data;
+ if (!g_ascii_strncasecmp (item, scheme, schemelen) &&
+ g_ascii_isspace (item[schemelen]))
+ break;
+ }
+ if (!i) {
+ soup_header_free_list (items);
+ return NULL;
+ }
+
+ /* The challenge extends from this item until the end, or until
+ * the next item that has a space before an equals sign.
+ */
+ challenge = g_string_new (item);
+ for (i = i->next; i; i = i->next) {
+ item = i->data;
+ space = strpbrk (item, " \t");
+ equals = strchr (item, '=');
+ if (!equals || (space && equals > space))
+ break;
+
+ g_string_append (challenge, ", ");
+ g_string_append (challenge, item);
+ }
+
+ soup_header_free_list (items);
+ return g_string_free (challenge, FALSE);
+}
+
+static SoupAuth *
+create_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
+{
+ const char *header;
+ SoupAuthClass *auth_class;
+ char *challenge = NULL;
+ SoupAuth *auth;
+ int i;
+
+ header = auth_header_for_message (msg);
+ if (!header)
+ return NULL;
+
+ for (i = priv->auth_types->len - 1; i >= 0; i--) {
+ auth_class = priv->auth_types->pdata[i];
+ challenge = extract_challenge (header, auth_class->scheme_name);
+ if (challenge)
+ break;
+ }
+ if (!challenge)
+ return NULL;
+
+ auth = soup_auth_new (G_TYPE_FROM_CLASS (auth_class), msg, challenge);
+ g_free (challenge);
+ return auth;
+}
+
+static gboolean
+check_auth (SoupMessage *msg, SoupAuth *auth)
+{
+ const char *header;
+ char *challenge;
+ gboolean ok;
+
+ header = auth_header_for_message (msg);
+ if (!header)
+ return FALSE;
+
+ challenge = extract_challenge (header, soup_auth_get_scheme_name (auth));
+ if (!challenge)
+ return FALSE;
+
+ ok = soup_auth_update (auth, msg, challenge);
+ g_free (challenge);
+ return ok;
+}
+
+static SoupAuthHost *
+get_auth_host_for_message (SoupAuthManagerPrivate *priv, SoupMessage *msg)
+{
+ SoupAuthHost *host;
+ SoupURI *uri = soup_message_get_uri (msg);
+
+ host = g_hash_table_lookup (priv->auth_hosts, uri);
+ if (host)
+ return host;
+
+ host = g_slice_new0 (SoupAuthHost);
+ host->uri = soup_uri_copy_host (uri);
+ g_hash_table_insert (priv->auth_hosts, host->uri, host);
+
+ return host;
+}
+
+static SoupAuth *
+lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
+{
+ SoupAuthHost *host;
+ const char *path, *realm;
+
+ host = get_auth_host_for_message (priv, msg);
+ if (!host->auth_realms)
+ return NULL;
+
+ path = soup_message_get_uri (msg)->path;
+ if (!path)
+ path = "/";
+ realm = soup_path_map_lookup (host->auth_realms, path);
+ if (realm)
+ return g_hash_table_lookup (host->auths, realm);
+ else
+ return NULL;
+}
+
+static gboolean
+authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
+ SoupMessage *msg, gboolean prior_auth_failed,
+ gboolean proxy, gboolean can_interact)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupURI *uri;
+
+ if (proxy) {
+ SoupMessageQueue *queue;
+ SoupMessageQueueItem *item;
+
+ queue = soup_session_get_queue (priv->session);
+ item = soup_message_queue_lookup (queue, msg);
+ if (item) {
+ uri = soup_connection_get_proxy_uri (item->conn);
+ soup_message_queue_item_unref (item);
+ } else
+ uri = NULL;
+
+ if (!uri)
+ return FALSE;
+ } else
+ uri = soup_message_get_uri (msg);
+
+ /* If a password is specified explicitly in the URI, use it
+ * even if the auth had previously already been authenticated.
+ */
+ if (uri->password) {
+ if (!prior_auth_failed)
+ soup_auth_authenticate (auth, uri->user, uri->password);
+ } else if (!soup_auth_is_authenticated (auth) && can_interact) {
+ soup_auth_manager_emit_authenticate (manager, msg, auth,
+ prior_auth_failed);
+ }
+
+ return soup_auth_is_authenticated (auth);
+}
+
+static void
+update_auth (SoupMessage *msg, gpointer manager)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuthHost *host;
+ SoupAuth *auth, *prior_auth, *old_auth;
+ const char *path;
+ char *auth_info, *old_auth_info;
+ GSList *pspace, *p;
+ gboolean prior_auth_failed = FALSE;
+
+ host = get_auth_host_for_message (priv, msg);
+
+ /* See if we used auth last time */
+ prior_auth = soup_message_get_auth (msg);
+ if (prior_auth && check_auth (msg, prior_auth)) {
+ auth = prior_auth;
+ if (!soup_auth_is_authenticated (auth))
+ prior_auth_failed = TRUE;
+ } else {
+ auth = create_auth (priv, msg);
+ if (!auth)
+ return;
+ }
+ auth_info = soup_auth_get_info (auth);
+
+ if (!host->auth_realms) {
+ host->auth_realms = soup_path_map_new (g_free);
+ host->auths = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+ }
+
+ /* Record where this auth realm is used. */
+ pspace = soup_auth_get_protection_space (auth, soup_message_get_uri (msg));
+ for (p = pspace; p; p = p->next) {
+ path = p->data;
+ old_auth_info = soup_path_map_lookup (host->auth_realms, path);
+ if (old_auth_info) {
+ if (!strcmp (old_auth_info, auth_info))
+ continue;
+ soup_path_map_remove (host->auth_realms, path);
+ }
+
+ soup_path_map_add (host->auth_realms, path,
+ g_strdup (auth_info));
+ }
+ soup_auth_free_protection_space (auth, pspace);
+
+ /* Now, make sure the auth is recorded. (If there's a
+ * pre-existing auth, we keep that rather than the new one,
+ * since the old one might already be authenticated.)
+ */
+ old_auth = g_hash_table_lookup (host->auths, auth_info);
+ if (old_auth) {
+ g_free (auth_info);
+ if (auth != old_auth && auth != prior_auth) {
+ g_object_unref (auth);
+ auth = old_auth;
+ }
+ } else {
+ g_hash_table_insert (host->auths, auth_info, auth);
+ }
+
+ /* If we need to authenticate, try to do it. */
+ authenticate_auth (manager, auth, msg,
+ prior_auth_failed, FALSE, TRUE);
+}
+
+static void
+requeue_if_authenticated (SoupMessage *msg, gpointer manager)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuth *auth = lookup_auth (priv, msg);
+
+ if (auth && soup_auth_is_authenticated (auth))
+ soup_session_requeue_message (priv->session, msg);
+}
+
+static void
+update_proxy_auth (SoupMessage *msg, gpointer manager)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuth *prior_auth;
+ gboolean prior_auth_failed = FALSE;
+
+ /* See if we used auth last time */
+ prior_auth = soup_message_get_proxy_auth (msg);
+ if (prior_auth && check_auth (msg, prior_auth)) {
+ if (!soup_auth_is_authenticated (prior_auth))
+ prior_auth_failed = TRUE;
+ }
+
+ if (!priv->proxy_auth) {
+ priv->proxy_auth = create_auth (priv, msg);
+ if (!priv->proxy_auth)
+ return;
+ }
+
+ /* If we need to authenticate, try to do it. */
+ authenticate_auth (manager, priv->proxy_auth, msg,
+ prior_auth_failed, TRUE, TRUE);
+}
+
+static void
+requeue_if_proxy_authenticated (SoupMessage *msg, gpointer manager)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuth *auth = priv->proxy_auth;
+
+ if (auth && soup_auth_is_authenticated (auth))
+ soup_session_requeue_message (priv->session, msg);
+}
+
+static void
+request_queued (SoupSessionFeature *manager, SoupSession *session,
+ SoupMessage *msg)
+{
+ soup_message_add_status_code_handler (
+ msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
+ G_CALLBACK (update_auth), manager);
+ soup_message_add_status_code_handler (
+ msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
+ G_CALLBACK (requeue_if_authenticated), manager);
+
+ soup_message_add_status_code_handler (
+ msg, "got_headers", SOUP_STATUS_PROXY_UNAUTHORIZED,
+ G_CALLBACK (update_proxy_auth), manager);
+ soup_message_add_status_code_handler (
+ msg, "got_body", SOUP_STATUS_PROXY_UNAUTHORIZED,
+ G_CALLBACK (requeue_if_proxy_authenticated), manager);
+}
+
+static void
+request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket)
+{
+ SoupAuthManager *manager = SOUP_AUTH_MANAGER (feature);
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuth *auth;
+
+ auth = lookup_auth (priv, msg);
+ if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE, FALSE))
+ auth = NULL;
+ soup_message_set_auth (msg, auth);
+
+ auth = priv->proxy_auth;
+ if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE))
+ auth = NULL;
+ soup_message_set_proxy_auth (msg, auth);
+}
+
+static void
+request_unqueued (SoupSessionFeature *manager, SoupSession *session,
+ SoupMessage *msg)
+{
+ g_signal_handlers_disconnect_matched (msg, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, manager);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_AUTH_MANAGER_H
+#define SOUP_AUTH_MANAGER_H 1
+
+#include "soup-types.h"
+#include "soup-auth.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_AUTH_MANAGER (soup_auth_manager_get_type ())
+#define SOUP_AUTH_MANAGER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_MANAGER, SoupAuthManager))
+#define SOUP_AUTH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerClass))
+#define SOUP_IS_AUTH_MANAGER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_MANAGER))
+#define SOUP_IS_AUTH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_MANAGER))
+#define SOUP_AUTH_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerClass))
+
+typedef struct {
+ GObject parent;
+
+} SoupAuthManager;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ void (*authenticate) (SoupAuthManager *manager, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
+} SoupAuthManagerClass;
+
+GType soup_auth_manager_get_type (void);
+
+void soup_auth_manager_emit_authenticate (SoupAuthManager *manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying);
+
+G_END_DECLS
+
+#endif /* SOUP_AUTH_MANAGER_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-ntlm.c: HTTP NTLM Authentication helper
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-ntlm.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-uri.h"
+
+static gboolean update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params);
+static GSList *get_protection_space (SoupAuth *auth, SoupURI *source_uri);
+static void authenticate (SoupAuth *auth, const char *username, const char *password);
+static gboolean is_authenticated (SoupAuth *auth);
+static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
+
+typedef struct {
+ char *username, *password;
+} SoupAuthNTLMPrivate;
+#define SOUP_AUTH_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMPrivate))
+
+G_DEFINE_TYPE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_AUTH)
+
+static void
+soup_auth_ntlm_init (SoupAuthNTLM *ntlm)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (object);
+
+ g_free (priv->username);
+ if (priv->password) {
+ memset (priv->password, 0, strlen (priv->password));
+ g_free (priv->password);
+ }
+
+ G_OBJECT_CLASS (soup_auth_ntlm_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_ntlm_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (auth_ntlm_class);
+
+ g_type_class_add_private (auth_ntlm_class, sizeof (SoupAuthNTLMPrivate));
+
+ auth_class->scheme_name = "NTLM";
+ auth_class->strength = 3;
+
+ auth_class->update = update;
+ auth_class->get_protection_space = get_protection_space;
+ auth_class->authenticate = authenticate;
+ auth_class->is_authenticated = is_authenticated;
+ auth_class->get_authorization = get_authorization;
+
+ object_class->finalize = finalize;
+}
+
+SoupAuth *
+soup_auth_ntlm_new (const char *realm, const char *host)
+{
+ SoupAuth *auth;
+
+ auth = g_object_new (SOUP_TYPE_AUTH_NTLM,
+ SOUP_AUTH_REALM, realm,
+ SOUP_AUTH_HOST, host,
+ NULL);
+ return auth;
+}
+
+static gboolean
+update (SoupAuth *auth, SoupMessage *msg, GHashTable *auth_params)
+{
+ g_return_val_if_reached (FALSE);
+}
+
+static GSList *
+get_protection_space (SoupAuth *auth, SoupURI *source_uri)
+{
+ g_return_val_if_reached (NULL);
+}
+
+static void
+authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+ g_return_if_fail (username != NULL);
+ g_return_if_fail (password != NULL);
+
+ priv->username = g_strdup (username);
+ priv->password = g_strdup (password);
+}
+
+static gboolean
+is_authenticated (SoupAuth *auth)
+{
+ return SOUP_AUTH_NTLM_GET_PRIVATE (auth)->password != NULL;
+}
+
+static char *
+get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+ g_return_val_if_reached (NULL);
+}
+
+const char *
+soup_auth_ntlm_get_username (SoupAuth *auth)
+{
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+ return priv->username;
+}
+
+const char *
+soup_auth_ntlm_get_password (SoupAuth *auth)
+{
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+ return priv->password;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_AUTH_NTLM_H
+#define SOUP_AUTH_NTLM_H 1
+
+#include "soup-auth.h"
+
+#define SOUP_AUTH_NTLM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLM))
+#define SOUP_AUTH_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
+#define SOUP_IS_AUTH_NTLM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_NTLM))
+#define SOUP_IS_AUTH_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_NTLM))
+#define SOUP_AUTH_NTLM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
+
+typedef struct {
+ SoupAuth parent;
+
+} SoupAuthNTLM;
+
+typedef struct {
+ SoupAuthClass parent_class;
+
+} SoupAuthNTLMClass;
+
+SoupAuth *soup_auth_ntlm_new (const char *realm,
+ const char *host);
+const char *soup_auth_ntlm_get_username (SoupAuth *auth);
+const char *soup_auth_ntlm_get_password (SoupAuth *auth);
+
+#endif /* SOUP_AUTH_NTLM_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth.c: HTTP Authentication framework
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+
+#include <string.h>
+
+#include "soup-auth.h"
+#include "soup-auth-basic.h"
+#include "soup-auth-digest.h"
+#include "soup-headers.h"
+#include "soup-marshal.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-auth
+ * @short_description: HTTP client-side authentication support
+ * @see_also: #SoupSession
+ *
+ * #SoupAuth objects store the authentication data associated with a
+ * given bit of web space. They are created automatically by
+ * #SoupSession.
+ **/
+
+/**
+ * SoupAuth:
+ *
+ * The abstract base class for handling authentication. Specific HTTP
+ * Authentication mechanisms are implemented by its subclasses, but
+ * applications never need to be aware of the specific subclasses
+ * being used.
+ **/
+
+typedef struct {
+ gboolean proxy;
+ char *host;
+
+ GHashTable *saved_passwords;
+} SoupAuthPrivate;
+#define SOUP_AUTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH, SoupAuthPrivate))
+
+G_DEFINE_ABSTRACT_TYPE (SoupAuth, soup_auth, G_TYPE_OBJECT)
+
+enum {
+ SAVE_PASSWORD,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+
+ PROP_SCHEME_NAME,
+ PROP_REALM,
+ PROP_HOST,
+ PROP_IS_FOR_PROXY,
+ PROP_IS_AUTHENTICATED,
+
+ LAST_PROP
+};
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+finalize (GObject *object)
+{
+ SoupAuth *auth = SOUP_AUTH (object);
+ SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (auth);
+
+ g_free (auth->realm);
+ g_free (priv->host);
+ if (priv->saved_passwords)
+ g_hash_table_destroy (priv->saved_passwords);
+
+ G_OBJECT_CLASS (soup_auth_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_class_init (SoupAuthClass *auth_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (auth_class);
+
+ g_type_class_add_private (auth_class, sizeof (SoupAuthPrivate));
+
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /**
+ * SoupAuth::save-password:
+ * @auth: the auth
+ * @username: the username to save
+ * @password: the password to save
+ *
+ * Emitted to request that the @username/@password pair be
+ * saved. If the session supports password-saving, it will
+ * connect to this signal before emitting
+ * #SoupSession::authenticate, so that it record the password
+ * if requested by the caller.
+ *
+ * Since: 2.28
+ **/
+ signals[SAVE_PASSWORD] =
+ g_signal_new ("save-password",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ soup_marshal_NONE__STRING_STRING,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ /* properties */
+ /**
+ * SOUP_AUTH_SCHEME_NAME:
+ *
+ * An alias for the #SoupAuth:scheme property. (The
+ * authentication scheme name.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SCHEME_NAME,
+ g_param_spec_string (SOUP_AUTH_SCHEME_NAME,
+ "Scheme name",
+ "Authentication scheme name",
+ NULL,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_AUTH_REALM:
+ *
+ * An alias for the #SoupAuth:realm property. (The
+ * authentication realm.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REALM,
+ g_param_spec_string (SOUP_AUTH_REALM,
+ "Realm",
+ "Authentication realm",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_AUTH_HOST:
+ *
+ * An alias for the #SoupAuth:host property. (The
+ * host being authenticated to.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_HOST,
+ g_param_spec_string (SOUP_AUTH_HOST,
+ "Host",
+ "Authentication host",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_AUTH_IS_FOR_PROXY:
+ *
+ * An alias for the #SoupAuth:is-for-proxy property. (Whether
+ * or not the auth is for a proxy server.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_IS_FOR_PROXY,
+ g_param_spec_boolean (SOUP_AUTH_IS_FOR_PROXY,
+ "For Proxy",
+ "Whether or not the auth is for a proxy server",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_AUTH_IS_AUTHENTICATED:
+ *
+ * An alias for the #SoupAuth:is-authenticated property.
+ * (Whether or not the auth has been authenticated.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_IS_AUTHENTICATED,
+ g_param_spec_boolean (SOUP_AUTH_IS_AUTHENTICATED,
+ "Authenticated",
+ "Whether or not the auth is authenticated",
+ FALSE,
+ G_PARAM_READABLE));
+}
+
+static void
+soup_auth_init (SoupAuth *auth)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupAuth *auth = SOUP_AUTH (object);
+ SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_REALM:
+ auth->realm = g_value_dup_string (value);
+ break;
+ case PROP_HOST:
+ priv->host = g_value_dup_string (value);
+ break;
+ case PROP_IS_FOR_PROXY:
+ priv->proxy = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupAuth *auth = SOUP_AUTH (object);
+ SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_SCHEME_NAME:
+ g_value_set_string (value, soup_auth_get_scheme_name (auth));
+ break;
+ case PROP_REALM:
+ g_value_set_string (value, soup_auth_get_realm (auth));
+ break;
+ case PROP_HOST:
+ g_value_set_string (value, soup_auth_get_host (auth));
+ break;
+ case PROP_IS_FOR_PROXY:
+ g_value_set_boolean (value, priv->proxy);
+ break;
+ case PROP_IS_AUTHENTICATED:
+ g_value_set_boolean (value, soup_auth_is_authenticated (auth));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_auth_new:
+ * @type: the type of auth to create (a subtype of #SoupAuth)
+ * @msg: the #SoupMessage the auth is being created for
+ * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
+ *
+ * Creates a new #SoupAuth of type @type with the information from
+ * @msg and @auth_header.
+ *
+ * This is called by #SoupSession; you will normally not create auths
+ * yourself.
+ *
+ * Return value: the new #SoupAuth, or %NULL if it could not be
+ * created
+ **/
+SoupAuth *
+soup_auth_new (GType type, SoupMessage *msg, const char *auth_header)
+{
+ SoupAuth *auth;
+ GHashTable *params;
+ const char *scheme, *realm;
+
+ g_return_val_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+ g_return_val_if_fail (auth_header != NULL, NULL);
+
+ auth = g_object_new (type,
+ SOUP_AUTH_IS_FOR_PROXY, (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED),
+ SOUP_AUTH_HOST, soup_message_get_uri (msg)->host,
+ NULL);
+
+ scheme = soup_auth_get_scheme_name (auth);
+ if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0) {
+ g_object_unref (auth);
+ return NULL;
+ }
+
+ params = soup_header_parse_param_list (auth_header + strlen (scheme));
+ if (!params) {
+ g_object_unref (auth);
+ return NULL;
+ }
+
+ realm = g_hash_table_lookup (params, "realm");
+ if (!realm) {
+ soup_header_free_param_list (params);
+ g_object_unref (auth);
+ return NULL;
+ }
+
+ auth->realm = g_strdup (realm);
+
+ if (!SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params)) {
+ g_object_unref (auth);
+ auth = NULL;
+ }
+ soup_header_free_param_list (params);
+ return auth;
+}
+
+/**
+ * soup_auth_update:
+ * @auth: a #SoupAuth
+ * @msg: the #SoupMessage @auth is being updated for
+ * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
+ *
+ * Updates @auth with the information from @msg and @auth_header,
+ * possibly un-authenticating it. As with soup_auth_new(), this is
+ * normally only used by #SoupSession.
+ *
+ * Return value: %TRUE if @auth is still a valid (but potentially
+ * unauthenticated) #SoupAuth. %FALSE if something about @auth_params
+ * could not be parsed or incorporated into @auth at all.
+ **/
+gboolean
+soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
+{
+ GHashTable *params;
+ const char *scheme, *realm;
+ gboolean was_authenticated, success;
+
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+ g_return_val_if_fail (auth_header != NULL, FALSE);
+
+ scheme = soup_auth_get_scheme_name (auth);
+ if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0)
+ return FALSE;
+
+ params = soup_header_parse_param_list (auth_header + strlen (scheme));
+ if (!params)
+ return FALSE;
+
+ realm = g_hash_table_lookup (params, "realm");
+ if (!realm || strcmp (realm, auth->realm) != 0) {
+ soup_header_free_param_list (params);
+ return FALSE;
+ }
+
+ was_authenticated = soup_auth_is_authenticated (auth);
+ success = SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params);
+ if (was_authenticated != soup_auth_is_authenticated (auth))
+ g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
+ soup_header_free_param_list (params);
+ return success;
+}
+
+/**
+ * soup_auth_authenticate:
+ * @auth: a #SoupAuth
+ * @username: the username provided by the user or client
+ * @password: the password provided by the user or client
+ *
+ * Call this on an auth to authenticate it; normally this will cause
+ * the auth's message to be requeued with the new authentication info.
+ *
+ * This does not cause the password to be saved to persistent storage;
+ * see soup_auth_save_password() for that.
+ **/
+void
+soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+ gboolean was_authenticated;
+
+ g_return_if_fail (SOUP_IS_AUTH (auth));
+ g_return_if_fail (username != NULL);
+ g_return_if_fail (password != NULL);
+
+ was_authenticated = soup_auth_is_authenticated (auth);
+ SOUP_AUTH_GET_CLASS (auth)->authenticate (auth, username, password);
+ if (was_authenticated != soup_auth_is_authenticated (auth))
+ g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
+}
+
+/**
+ * soup_auth_is_for_proxy:
+ * @auth: a #SoupAuth
+ *
+ * Tests whether or not @auth is associated with a proxy server rather
+ * than an "origin" server.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+soup_auth_is_for_proxy (SoupAuth *auth)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
+
+ return SOUP_AUTH_GET_PRIVATE (auth)->proxy;
+}
+
+/**
+ * soup_auth_get_scheme_name:
+ * @auth: a #SoupAuth
+ *
+ * Returns @auth's scheme name. (Eg, "Basic", "Digest", or "NTLM")
+ *
+ * Return value: the scheme name
+ **/
+const char *
+soup_auth_get_scheme_name (SoupAuth *auth)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+
+ return SOUP_AUTH_GET_CLASS (auth)->scheme_name;
+}
+
+/**
+ * soup_auth_get_host:
+ * @auth: a #SoupAuth
+ *
+ * Returns the host that @auth is associated with.
+ *
+ * Return value: the hostname
+ **/
+const char *
+soup_auth_get_host (SoupAuth *auth)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+
+ return SOUP_AUTH_GET_PRIVATE (auth)->host;
+}
+
+
+/**
+ * soup_auth_get_realm:
+ * @auth: a #SoupAuth
+ *
+ * Returns @auth's realm. This is an identifier that distinguishes
+ * separate authentication spaces on a given server, and may be some
+ * string that is meaningful to the user. (Although it is probably not
+ * localized.)
+ *
+ * Return value: the realm name
+ **/
+const char *
+soup_auth_get_realm (SoupAuth *auth)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+
+ return auth->realm;
+}
+
+/**
+ * soup_auth_get_info:
+ * @auth: a #SoupAuth
+ *
+ * Gets an opaque identifier for @auth, for use as a hash key or the
+ * like. #SoupAuth objects from the same server with the same
+ * identifier refer to the same authentication domain (eg, the URLs
+ * associated with them take the same usernames and passwords).
+ *
+ * Return value: the identifier
+ **/
+char *
+soup_auth_get_info (SoupAuth *auth)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+
+ return g_strdup_printf ("%s:%s",
+ SOUP_AUTH_GET_CLASS (auth)->scheme_name,
+ auth->realm);
+}
+
+/**
+ * soup_auth_is_authenticated:
+ * @auth: a #SoupAuth
+ *
+ * Tests if @auth has been given a username and password
+ *
+ * Return value: %TRUE if @auth has been given a username and password
+ **/
+gboolean
+soup_auth_is_authenticated (SoupAuth *auth)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
+
+ return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
+}
+
+/**
+ * soup_auth_get_authorization:
+ * @auth: a #SoupAuth
+ * @msg: the #SoupMessage to be authorized
+ *
+ * Generates an appropriate "Authorization" header for @msg. (The
+ * session will only call this if soup_auth_is_authenticated()
+ * returned %TRUE.)
+ *
+ * Return value: the "Authorization" header, which must be freed.
+ **/
+char *
+soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+ g_return_val_if_fail (msg != NULL, NULL);
+
+ return SOUP_AUTH_GET_CLASS (auth)->get_authorization (auth, msg);
+}
+
+/**
+ * soup_auth_get_protection_space:
+ * @auth: a #SoupAuth
+ * @source_uri: the URI of the request that @auth was generated in
+ * response to.
+ *
+ * Returns a list of paths on the server which @auth extends over.
+ * (All subdirectories of these paths are also assumed to be part
+ * of @auth's protection space, unless otherwise discovered not to
+ * be.)
+ *
+ * Return value: (element-type utf8) (transfer full): the list of
+ * paths, which can be freed with soup_auth_free_protection_space().
+ **/
+GSList *
+soup_auth_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+ g_return_val_if_fail (source_uri != NULL, NULL);
+
+ return SOUP_AUTH_GET_CLASS (auth)->get_protection_space (auth, source_uri);
+}
+
+/**
+ * soup_auth_free_protection_space: (skip)
+ * @auth: a #SoupAuth
+ * @space: the return value from soup_auth_get_protection_space()
+ *
+ * Frees @space.
+ **/
+void
+soup_auth_free_protection_space (SoupAuth *auth, GSList *space)
+{
+ GSList *s;
+
+ for (s = space; s; s = s->next)
+ g_free (s->data);
+ g_slist_free (space);
+}
+
+/**
+ * soup_auth_get_saved_users:
+ * @auth: a #SoupAuth
+ *
+ * Gets a list of usernames for which a saved password is available.
+ * (If the session is not configured to save passwords, this will
+ * always be %NULL.)
+ *
+ * Return value: (transfer container): the list of usernames. You must
+ * free the list with g_slist_free(), but do not free or modify the
+ * contents.
+ *
+ * Since: 2.28
+ **/
+GSList *
+soup_auth_get_saved_users (SoupAuth *auth)
+{
+ SoupAuthPrivate *priv;
+ GSList *users;
+
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+
+ priv = SOUP_AUTH_GET_PRIVATE (auth);
+ users = NULL;
+
+ if (priv->saved_passwords) {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, priv->saved_passwords);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ users = g_slist_prepend (users, key);
+ }
+ return users;
+}
+
+/**
+ * soup_auth_get_saved_password:
+ * @auth: a #SoupAuth
+ * @user: a username from the list returned from
+ * soup_auth_get_saved_users().
+ *
+ * Given a username for which @auth has a saved password, this returns
+ * that password. If @auth doesn't have a passwords saved for @user, it
+ * returns %NULL.
+ *
+ * Return value: the saved password, or %NULL.
+ *
+ * Since: 2.28
+ **/
+const char *
+soup_auth_get_saved_password (SoupAuth *auth, const char *user)
+{
+ SoupAuthPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+ g_return_val_if_fail (user != NULL, NULL);
+
+ priv = SOUP_AUTH_GET_PRIVATE (auth);
+ if (!priv->saved_passwords)
+ return NULL;
+ return g_hash_table_lookup (priv->saved_passwords, user);
+}
+
+static void
+free_password (gpointer password)
+{
+ memset (password, 0, strlen (password));
+ g_free (password);
+}
+
+static inline void
+init_saved_passwords (SoupAuthPrivate *priv)
+{
+ priv->saved_passwords = g_hash_table_new_full (
+ g_str_hash, g_str_equal, g_free, free_password);
+}
+
+/**
+ * soup_auth_has_saved_password:
+ * @auth: a #SoupAuth
+ * @username: a username
+ * @password: a password
+ *
+ * Updates @auth to be aware of an already-saved username/password
+ * combination. This method <emphasis>does not</emphasis> cause the
+ * given @username and @password to be saved; use
+ * soup_auth_save_password() for that. (soup_auth_has_saved_password()
+ * is an internal method, which is used by the code that actually
+ * saves and restores the passwords.)
+ *
+ * Since: 2.28
+ **/
+void
+soup_auth_has_saved_password (SoupAuth *auth, const char *username,
+ const char *password)
+{
+ SoupAuthPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_AUTH (auth));
+ g_return_if_fail (username != NULL);
+ g_return_if_fail (password != NULL);
+
+ priv = SOUP_AUTH_GET_PRIVATE (auth);
+
+ if (!priv->saved_passwords)
+ init_saved_passwords (priv);
+ g_hash_table_insert (priv->saved_passwords,
+ g_strdup (username), g_strdup (password));
+}
+
+/**
+ * soup_auth_save_password:
+ * @auth: a #SoupAuth
+ * @username: the username provided by the user or client
+ * @password: the password provided by the user or client
+ *
+ * Requests that the username/password pair be saved to whatever form
+ * of persistent password storage the session supports.
+ *
+ * Since: 2.28
+ **/
+void
+soup_auth_save_password (SoupAuth *auth, const char *username,
+ const char *password)
+{
+ g_return_if_fail (SOUP_IS_AUTH (auth));
+ g_return_if_fail (username != NULL);
+ g_return_if_fail (password != NULL);
+
+ g_signal_emit (auth, signals[SAVE_PASSWORD], 0,
+ username, password);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_AUTH_H
+#define SOUP_AUTH_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-headers.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_AUTH (soup_auth_get_type ())
+#define SOUP_AUTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH, SoupAuth))
+#define SOUP_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH, SoupAuthClass))
+#define SOUP_IS_AUTH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_AUTH))
+#define SOUP_IS_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH))
+#define SOUP_AUTH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH, SoupAuthClass))
+
+struct _SoupAuth {
+ GObject parent;
+
+ char *realm;
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ const char *scheme_name;
+ guint strength;
+
+ gboolean (*update) (SoupAuth *auth,
+ SoupMessage *msg,
+ GHashTable *auth_params);
+
+ GSList * (*get_protection_space) (SoupAuth *auth,
+ SoupURI *source_uri);
+
+ void (*authenticate) (SoupAuth *auth,
+ const char *username,
+ const char *password);
+ gboolean (*is_authenticated) (SoupAuth *auth);
+
+ char * (*get_authorization) (SoupAuth *auth,
+ SoupMessage *msg);
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupAuthClass;
+
+#define SOUP_AUTH_SCHEME_NAME "scheme-name"
+#define SOUP_AUTH_REALM "realm"
+#define SOUP_AUTH_HOST "host"
+#define SOUP_AUTH_IS_FOR_PROXY "is-for-proxy"
+#define SOUP_AUTH_IS_AUTHENTICATED "is-authenticated"
+
+GType soup_auth_get_type (void);
+
+SoupAuth *soup_auth_new (GType type,
+ SoupMessage *msg,
+ const char *auth_header);
+gboolean soup_auth_update (SoupAuth *auth,
+ SoupMessage *msg,
+ const char *auth_header);
+
+gboolean soup_auth_is_for_proxy (SoupAuth *auth);
+const char *soup_auth_get_scheme_name (SoupAuth *auth);
+const char *soup_auth_get_host (SoupAuth *auth);
+const char *soup_auth_get_realm (SoupAuth *auth);
+char *soup_auth_get_info (SoupAuth *auth);
+
+#ifdef LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+GSList *soup_auth_get_saved_users (SoupAuth *auth);
+const char *soup_auth_get_saved_password (SoupAuth *auth,
+ const char *user);
+void soup_auth_save_password (SoupAuth *auth,
+ const char *username,
+ const char *password);
+#endif
+
+void soup_auth_authenticate (SoupAuth *auth,
+ const char *username,
+ const char *password);
+gboolean soup_auth_is_authenticated (SoupAuth *auth);
+
+char *soup_auth_get_authorization (SoupAuth *auth,
+ SoupMessage *msg);
+
+GSList *soup_auth_get_protection_space (SoupAuth *auth,
+ SoupURI *source_uri);
+void soup_auth_free_protection_space (SoupAuth *auth,
+ GSList *space);
+
+#ifdef LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+void soup_auth_has_saved_password (SoupAuth *auth,
+ const char *username,
+ const char *password);
+#endif
+
+/* The actual auth types, which can be added/removed as features */
+
+#define SOUP_TYPE_AUTH_BASIC (soup_auth_basic_get_type ())
+GType soup_auth_basic_get_type (void);
+#define SOUP_TYPE_AUTH_DIGEST (soup_auth_digest_get_type ())
+GType soup_auth_digest_get_type (void);
+#define SOUP_TYPE_AUTH_NTLM (soup_auth_ntlm_get_type ())
+GType soup_auth_ntlm_get_type (void);
+
+G_END_DECLS
+
+#endif /* SOUP_AUTH_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache-private.h:
+ *
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SOUP_CACHE_PRIVATE_H
+#define SOUP_CACHE_PRIVATE_H 1
+
+#include "soup-cache.h"
+#include <libsoup/soup-message.h>
+
+G_BEGIN_DECLS
+
+SoupCacheResponse soup_cache_has_response (SoupCache *cache,
+ SoupMessage *msg);
+GInputStream *soup_cache_send_response (SoupCache *cache,
+ SoupMessage *msg);
+SoupCacheability soup_cache_get_cacheability (SoupCache *cache,
+ SoupMessage *msg);
+SoupMessage *soup_cache_generate_conditional_request (SoupCache *cache,
+ SoupMessage *original);
+
+G_END_DECLS
+
+#endif /* SOUP_CACHE_PRIVATE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache.c
+ *
+ * Copyright (C) 2009, 2010 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* TODO:
+ * - Need to hook the feature in the sync SoupSession.
+ * - Need more tests.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <gio/gio.h>
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "soup-cache.h"
+#include "soup-cache-private.h"
+#include "soup-date.h"
+#include "soup-enum-types.h"
+#include "soup-headers.h"
+#include "soup-session.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+/*TIZEN patch*/
+#include "TIZEN.h"
+
+static SoupSessionFeatureInterface *soup_cache_default_feature_interface;
+static void soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+#define DEFAULT_MAX_SIZE 50 * 1024 * 1024
+#define MAX_ENTRY_DATA_PERCENTAGE 10 /* Percentage of the total size
+ of the cache that can be
+ filled by a single entry */
+
+/*
+ * Version 2: cache is now saved in soup.cache2. Added the version
+ * number to the beginning of the file.
+ *
+ * Version 3: added HTTP status code to the cache entries.
+ *
+ * Version 4: replaced several types.
+ * - freshness_lifetime,corrected_initial_age,response_time: time_t -> guint32
+ * - status_code: guint -> guint16
+ * - hits: guint -> guint32
+ *
+ * Version 5: key is no longer stored on disk as it can be easily
+ * built from the URI. Apart from that some fields in the
+ * SoupCacheEntry have changed:
+ * - entry key is now a uint32 instead of a (char *).
+ * - added uri, used to check for collisions
+ * - removed filename, it's built from the entry key.
+ */
+#define SOUP_CACHE_CURRENT_VERSION 5
+
+typedef struct _SoupCacheEntry {
+ guint32 key;
+ char *uri;
+ guint32 freshness_lifetime;
+ gboolean must_revalidate;
+ gsize length;
+ guint32 corrected_initial_age;
+ guint32 response_time;
+ SoupBuffer *current_writing_buffer;
+ gboolean dirty;
+ gboolean got_body;
+ gboolean being_validated;
+ SoupMessageHeaders *headers;
+ GOutputStream *stream;
+ GError *error;
+ guint32 hits;
+ GCancellable *cancellable;
+ guint16 status_code;
+} SoupCacheEntry;
+
+struct _SoupCachePrivate {
+ char *cache_dir;
+ GHashTable *cache;
+ guint n_pending;
+ SoupSession *session;
+ SoupCacheType cache_type;
+ guint size;
+ guint max_size;
+ guint max_entry_data_size; /* Computed value. Here for performance reasons */
+ GList *lru_start;
+};
+
+typedef struct {
+ SoupCache *cache;
+ SoupCacheEntry *entry;
+ SoupMessage *msg;
+ gulong got_chunk_handler;
+ gulong got_body_handler;
+ gulong restarted_handler;
+ GQueue *buffer_queue;
+} SoupCacheWritingFixture;
+
+enum {
+ PROP_0,
+ PROP_CACHE_DIR,
+ PROP_CACHE_TYPE
+};
+
+#define SOUP_CACHE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CACHE, SoupCachePrivate))
+
+G_DEFINE_TYPE_WITH_CODE (SoupCache, soup_cache, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_cache_session_feature_init))
+
+static gboolean soup_cache_entry_remove (SoupCache *cache, SoupCacheEntry *entry);
+static void make_room_for_new_entry (SoupCache *cache, guint length_to_add);
+static gboolean cache_accepts_entries_of_size (SoupCache *cache, guint length_to_add);
+static gboolean write_next_buffer (SoupCacheEntry *entry, SoupCacheWritingFixture *fixture);
+
+static GFile *
+get_file_from_entry (SoupCache *cache, SoupCacheEntry *entry)
+{
+ char *filename = g_strdup_printf ("%s%s%u", cache->priv->cache_dir,
+ G_DIR_SEPARATOR_S, (guint) entry->key);
+ GFile *file = g_file_new_for_path (filename);
+ g_free (filename);
+
+ return file;
+}
+
+static SoupCacheability
+get_cacheability (SoupCache *cache, SoupMessage *msg)
+{
+ SoupCacheability cacheability;
+ const char *cache_control, *content_type;
+
+ /* 1. The request method must be cacheable */
+ if (msg->method == SOUP_METHOD_GET)
+ cacheability = SOUP_CACHE_CACHEABLE;
+ else if (msg->method == SOUP_METHOD_HEAD ||
+ msg->method == SOUP_METHOD_TRACE ||
+ msg->method == SOUP_METHOD_CONNECT)
+ return SOUP_CACHE_UNCACHEABLE;
+ else
+ return (SOUP_CACHE_UNCACHEABLE | SOUP_CACHE_INVALIDATES);
+
+ content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
+ if (content_type && !g_ascii_strcasecmp (content_type, "multipart/x-mixed-replace"))
+ return SOUP_CACHE_UNCACHEABLE;
+
+ cache_control = soup_message_headers_get (msg->response_headers, "Cache-Control");
+ if (cache_control) {
+ GHashTable *hash;
+ SoupCachePrivate *priv = SOUP_CACHE_GET_PRIVATE (cache);
+
+ hash = soup_header_parse_param_list (cache_control);
+
+ /* Shared caches MUST NOT store private resources */
+ if (priv->cache_type == SOUP_CACHE_SHARED) {
+ if (g_hash_table_lookup_extended (hash, "private", NULL, NULL)) {
+ soup_header_free_param_list (hash);
+ return SOUP_CACHE_UNCACHEABLE;
+ }
+ }
+
+ /* 2. The 'no-store' cache directive does not appear in the
+ * headers
+ */
+ if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
+ soup_header_free_param_list (hash);
+ return SOUP_CACHE_UNCACHEABLE;
+ }
+
+ /* This does not appear in section 2.1, but I think it makes
+ * sense to check it too?
+ */
+ if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
+ soup_header_free_param_list (hash);
+ return SOUP_CACHE_UNCACHEABLE;
+ }
+
+ soup_header_free_param_list (hash);
+ }
+
+ switch (msg->status_code) {
+ case SOUP_STATUS_PARTIAL_CONTENT:
+ /* We don't cache partial responses, but they only
+ * invalidate cached full responses if the headers
+ * don't match.
+ */
+ cacheability = SOUP_CACHE_UNCACHEABLE;
+ break;
+
+ case SOUP_STATUS_NOT_MODIFIED:
+ /* A 304 response validates an existing cache entry */
+ cacheability = SOUP_CACHE_VALIDATES;
+ break;
+
+ case SOUP_STATUS_MULTIPLE_CHOICES:
+ case SOUP_STATUS_MOVED_PERMANENTLY:
+ case SOUP_STATUS_GONE:
+ /* FIXME: cacheable unless indicated otherwise */
+ cacheability = SOUP_CACHE_UNCACHEABLE;
+ break;
+
+ case SOUP_STATUS_FOUND:
+ case SOUP_STATUS_TEMPORARY_REDIRECT:
+ /* FIXME: cacheable if explicitly indicated */
+ cacheability = SOUP_CACHE_UNCACHEABLE;
+ break;
+
+ case SOUP_STATUS_SEE_OTHER:
+ case SOUP_STATUS_FORBIDDEN:
+ case SOUP_STATUS_NOT_FOUND:
+ case SOUP_STATUS_METHOD_NOT_ALLOWED:
+ return (SOUP_CACHE_UNCACHEABLE | SOUP_CACHE_INVALIDATES);
+
+ default:
+ /* Any 5xx status or any 4xx status not handled above
+ * is uncacheable but doesn't break the cache.
+ */
+ if ((msg->status_code >= SOUP_STATUS_BAD_REQUEST &&
+ msg->status_code <= SOUP_STATUS_FAILED_DEPENDENCY) ||
+ msg->status_code >= SOUP_STATUS_INTERNAL_SERVER_ERROR)
+ return SOUP_CACHE_UNCACHEABLE;
+
+ /* An unrecognized 2xx, 3xx, or 4xx response breaks
+ * the cache.
+ */
+ if ((msg->status_code > SOUP_STATUS_PARTIAL_CONTENT &&
+ msg->status_code < SOUP_STATUS_MULTIPLE_CHOICES) ||
+ (msg->status_code > SOUP_STATUS_TEMPORARY_REDIRECT &&
+ msg->status_code < SOUP_STATUS_INTERNAL_SERVER_ERROR))
+ return (SOUP_CACHE_UNCACHEABLE | SOUP_CACHE_INVALIDATES);
+ break;
+ }
+
+ return cacheability;
+}
+
+/* NOTE: this function deletes the file pointed by the file argument
+ * and also unref's the GFile object representing it.
+ */
+static void
+soup_cache_entry_free (SoupCacheEntry *entry, GFile *file)
+{
+ if (file) {
+ g_file_delete (file, NULL, NULL);
+ g_object_unref (file);
+ }
+
+ g_free (entry->uri);
+ entry->uri = NULL;
+
+ if (entry->current_writing_buffer) {
+ soup_buffer_free (entry->current_writing_buffer);
+ entry->current_writing_buffer = NULL;
+ }
+
+ if (entry->headers) {
+ soup_message_headers_free (entry->headers);
+ entry->headers = NULL;
+ }
+ if (entry->error) {
+ g_error_free (entry->error);
+ entry->error = NULL;
+ }
+ if (entry->cancellable) {
+ g_object_unref (entry->cancellable);
+ entry->cancellable = NULL;
+ }
+
+ g_slice_free (SoupCacheEntry, entry);
+}
+
+static void
+copy_headers (const char *name, const char *value, SoupMessageHeaders *headers)
+{
+ soup_message_headers_append (headers, name, value);
+}
+
+static char *hop_by_hop_headers[] = {"Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailer", "Transfer-Encoding", "Upgrade"};
+
+static void
+copy_end_to_end_headers (SoupMessageHeaders *source, SoupMessageHeaders *destination)
+{
+ int i;
+
+ soup_message_headers_foreach (source, (SoupMessageHeadersForeachFunc) copy_headers, destination);
+ for (i = 0; i < G_N_ELEMENTS (hop_by_hop_headers); i++)
+ soup_message_headers_remove (destination, hop_by_hop_headers[i]);
+ soup_message_headers_clean_connection_headers (destination);
+}
+
+static guint
+soup_cache_entry_get_current_age (SoupCacheEntry *entry)
+{
+ time_t now = time (NULL);
+ time_t resident_time;
+
+ resident_time = now - entry->response_time;
+ return entry->corrected_initial_age + resident_time;
+}
+
+static gboolean
+soup_cache_entry_is_fresh_enough (SoupCacheEntry *entry, gint min_fresh)
+{
+ guint limit = (min_fresh == -1) ? soup_cache_entry_get_current_age (entry) : (guint) min_fresh;
+ return entry->freshness_lifetime > limit;
+}
+
+static inline guint32
+get_cache_key_from_uri (const char *uri)
+{
+ return (guint32) g_str_hash (uri);
+}
+
+static void
+soup_cache_entry_set_freshness (SoupCacheEntry *entry, SoupMessage *msg, SoupCache *cache)
+{
+ const char *cache_control;
+ const char *expires, *date, *last_modified;
+
+ cache_control = soup_message_headers_get (entry->headers, "Cache-Control");
+ if (cache_control) {
+ const char *max_age, *s_maxage;
+ gint64 freshness_lifetime = 0;
+ GHashTable *hash;
+ SoupCachePrivate *priv = SOUP_CACHE_GET_PRIVATE (cache);
+
+ hash = soup_header_parse_param_list (cache_control);
+
+ /* Should we re-validate the entry when it goes stale */
+ entry->must_revalidate = g_hash_table_lookup_extended (hash, "must-revalidate", NULL, NULL);
+
+ /* Section 2.3.1 */
+ if (priv->cache_type == SOUP_CACHE_SHARED) {
+ s_maxage = g_hash_table_lookup (hash, "s-maxage");
+ if (s_maxage) {
+ freshness_lifetime = g_ascii_strtoll (s_maxage, NULL, 10);
+ if (freshness_lifetime) {
+ /* Implies proxy-revalidate. TODO: is it true? */
+ entry->must_revalidate = TRUE;
+ soup_header_free_param_list (hash);
+ return;
+ }
+ }
+ }
+
+ /* If 'max-age' cache directive is present, use that */
+ max_age = g_hash_table_lookup (hash, "max-age");
+ if (max_age)
+ freshness_lifetime = g_ascii_strtoll (max_age, NULL, 10);
+
+ if (freshness_lifetime) {
+ entry->freshness_lifetime = (guint32) MIN (freshness_lifetime, G_MAXUINT32);
+ soup_header_free_param_list (hash);
+ return;
+ }
+
+ soup_header_free_param_list (hash);
+ }
+
+ /* If the 'Expires' response header is present, use its value
+ * minus the value of the 'Date' response header
+ */
+ expires = soup_message_headers_get (entry->headers, "Expires");
+ date = soup_message_headers_get (entry->headers, "Date");
+ if (expires && date) {
+ SoupDate *expires_d, *date_d;
+ time_t expires_t, date_t;
+
+ expires_d = soup_date_new_from_string (expires);
+ if (expires_d) {
+ date_d = soup_date_new_from_string (date);
+
+ expires_t = soup_date_to_time_t (expires_d);
+ date_t = soup_date_to_time_t (date_d);
+
+ soup_date_free (expires_d);
+ soup_date_free (date_d);
+
+ if (expires_t && date_t) {
+ entry->freshness_lifetime = (guint32) MAX (expires_t - date_t, 0);
+ return;
+ }
+ } else {
+ /* If Expires is not a valid date we should
+ treat it as already expired, see section
+ 3.3 */
+ entry->freshness_lifetime = 0;
+ return;
+ }
+ }
+
+ /* Otherwise an heuristic may be used */
+
+ /* Heuristics MUST NOT be used with these status codes
+ (section 2.3.1.1) */
+ if (msg->status_code != SOUP_STATUS_OK &&
+ msg->status_code != SOUP_STATUS_NON_AUTHORITATIVE &&
+ msg->status_code != SOUP_STATUS_PARTIAL_CONTENT &&
+ msg->status_code != SOUP_STATUS_MULTIPLE_CHOICES &&
+ msg->status_code != SOUP_STATUS_MOVED_PERMANENTLY &&
+ msg->status_code != SOUP_STATUS_GONE)
+ goto expire;
+
+ /* TODO: attach warning 113 if response's current_age is more
+ than 24h (section 2.3.1.1) when using heuristics */
+
+ /* Last-Modified based heuristic */
+ last_modified = soup_message_headers_get (entry->headers, "Last-Modified");
+ if (last_modified) {
+ SoupDate *soup_date;
+ time_t now, last_modified_t;
+
+ soup_date = soup_date_new_from_string (last_modified);
+ last_modified_t = soup_date_to_time_t (soup_date);
+ now = time (NULL);
+
+#define HEURISTIC_FACTOR 0.1 /* From Section 2.3.1.1 */
+
+ entry->freshness_lifetime = MAX (0, (now - last_modified_t) * HEURISTIC_FACTOR);
+ soup_date_free (soup_date);
+ }
+
+ return;
+
+ expire:
+ /* If all else fails, make the entry expire immediately */
+ entry->freshness_lifetime = 0;
+}
+
+static SoupCacheEntry *
+soup_cache_entry_new (SoupCache *cache, SoupMessage *msg, time_t request_time, time_t response_time)
+{
+ SoupCacheEntry *entry;
+ const char *date;
+
+ entry = g_slice_new0 (SoupCacheEntry);
+ entry->dirty = FALSE;
+ entry->current_writing_buffer = NULL;
+ entry->got_body = FALSE;
+ entry->being_validated = FALSE;
+ entry->error = NULL;
+ entry->status_code = msg->status_code;
+ entry->response_time = response_time;
+ entry->uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+
+ /* Headers */
+ entry->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+ copy_end_to_end_headers (msg->response_headers, entry->headers);
+
+ /* LRU list */
+ entry->hits = 0;
+
+ /* Section 2.3.1, Freshness Lifetime */
+ soup_cache_entry_set_freshness (entry, msg, cache);
+
+ /* Section 2.3.2, Calculating Age */
+ date = soup_message_headers_get (entry->headers, "Date");
+
+ if (date) {
+ SoupDate *soup_date;
+ const char *age;
+ time_t date_value, apparent_age, corrected_received_age, response_delay, age_value = 0;
+
+ soup_date = soup_date_new_from_string (date);
+ date_value = soup_date_to_time_t (soup_date);
+ soup_date_free (soup_date);
+
+ age = soup_message_headers_get (entry->headers, "Age");
+ if (age)
+ age_value = g_ascii_strtoll (age, NULL, 10);
+
+ apparent_age = MAX (0, entry->response_time - date_value);
+ corrected_received_age = MAX (apparent_age, age_value);
+ response_delay = entry->response_time - request_time;
+ entry->corrected_initial_age = corrected_received_age + response_delay;
+ } else {
+ /* Is this correct ? */
+ entry->corrected_initial_age = time (NULL);
+ }
+
+ return entry;
+}
+
+static void
+soup_cache_writing_fixture_free (SoupCacheWritingFixture *fixture)
+{
+ /* Free fixture. And disconnect signals, we don't want to
+ listen to more SoupMessage events as we're finished with
+ this resource */
+ if (g_signal_handler_is_connected (fixture->msg, fixture->got_chunk_handler))
+ g_signal_handler_disconnect (fixture->msg, fixture->got_chunk_handler);
+ if (g_signal_handler_is_connected (fixture->msg, fixture->got_body_handler))
+ g_signal_handler_disconnect (fixture->msg, fixture->got_body_handler);
+ if (g_signal_handler_is_connected (fixture->msg, fixture->restarted_handler))
+ g_signal_handler_disconnect (fixture->msg, fixture->restarted_handler);
+ g_queue_foreach (fixture->buffer_queue, (GFunc) soup_buffer_free, NULL);
+ g_queue_free (fixture->buffer_queue);
+ g_object_unref (fixture->msg);
+ g_object_unref (fixture->cache);
+ g_slice_free (SoupCacheWritingFixture, fixture);
+}
+
+static void
+close_ready_cb (GObject *source, GAsyncResult *result, SoupCacheWritingFixture *fixture)
+{
+ SoupCacheEntry *entry = fixture->entry;
+ SoupCache *cache = fixture->cache;
+ GOutputStream *stream = G_OUTPUT_STREAM (source);
+ goffset content_length;
+
+ g_warn_if_fail (entry->error == NULL);
+
+ /* FIXME: what do we do on error ? */
+
+ if (stream) {
+ g_output_stream_close_finish (stream, result, NULL);
+ g_object_unref (stream);
+ }
+ entry->stream = NULL;
+
+ content_length = soup_message_headers_get_content_length (entry->headers);
+
+ /* If the process was cancelled, then delete the entry from
+ the cache. Do it also if the size of a chunked resource is
+ too much for the cache */
+ if (g_cancellable_is_cancelled (entry->cancellable)) {
+ entry->dirty = FALSE;
+ soup_cache_entry_remove (cache, entry);
+ soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+ entry = NULL;
+ } else if ((soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CHUNKED) ||
+ entry->length != (gsize) content_length) {
+ /* Two options here:
+ *
+ * 1. "chunked" data, entry was temporarily added to
+ * cache (as content-length is 0) and now that we have
+ * the actual size we have to evaluate if we want it
+ * in the cache or not
+ *
+ * 2. Content-Length has a different value than actual
+ * length, means that the content was encoded for
+ * transmission (typically compressed) and thus we
+ * have to substract the content-length value that was
+ * added to the cache and add the unencoded length
+ */
+ gint length_to_add = entry->length - content_length;
+
+ /* Make room in cache if needed */
+ if (cache_accepts_entries_of_size (cache, length_to_add)) {
+ make_room_for_new_entry (cache, length_to_add);
+
+ cache->priv->size += length_to_add;
+ } else {
+ entry->dirty = FALSE;
+ soup_cache_entry_remove (cache, entry);
+ soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+ entry = NULL;
+ }
+ }
+
+ if (entry) {
+ entry->dirty = FALSE;
+ entry->got_body = FALSE;
+
+ if (entry->current_writing_buffer) {
+ soup_buffer_free (entry->current_writing_buffer);
+ entry->current_writing_buffer = NULL;
+ }
+
+ g_object_unref (entry->cancellable);
+ entry->cancellable = NULL;
+ }
+
+ cache->priv->n_pending--;
+
+ /* Frees */
+ soup_cache_writing_fixture_free (fixture);
+}
+
+static void
+write_ready_cb (GObject *source, GAsyncResult *result, SoupCacheWritingFixture *fixture)
+{
+ GOutputStream *stream = G_OUTPUT_STREAM (source);
+ GError *error = NULL;
+ gssize write_size;
+ SoupCacheEntry *entry = fixture->entry;
+
+ if (g_cancellable_is_cancelled (entry->cancellable)) {
+ g_output_stream_close_async (stream,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)close_ready_cb,
+ fixture);
+ return;
+ }
+
+ write_size = g_output_stream_write_finish (stream, result, &error);
+ if (write_size <= 0 || error) {
+ if (error)
+ entry->error = error;
+ g_output_stream_close_async (stream,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)close_ready_cb,
+ fixture);
+ /* FIXME: We should completely stop caching the
+ resource at this point */
+ } else {
+ /* Are we still writing and is there new data to write
+ already ? */
+ if (fixture->buffer_queue->length > 0)
+ write_next_buffer (entry, fixture);
+ else {
+ soup_buffer_free (entry->current_writing_buffer);
+ entry->current_writing_buffer = NULL;
+
+ if (entry->got_body) {
+ /* If we already received 'got-body'
+ and we have written all the data,
+ we can close the stream */
+ g_output_stream_close_async (entry->stream,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)close_ready_cb,
+ fixture);
+ }
+ }
+ }
+}
+
+static gboolean
+write_next_buffer (SoupCacheEntry *entry, SoupCacheWritingFixture *fixture)
+{
+ SoupBuffer *buffer = g_queue_pop_head (fixture->buffer_queue);
+
+ if (buffer == NULL)
+ return FALSE;
+
+ /* Free the old buffer */
+ if (entry->current_writing_buffer) {
+ soup_buffer_free (entry->current_writing_buffer);
+ entry->current_writing_buffer = NULL;
+ }
+ entry->current_writing_buffer = buffer;
+
+ g_output_stream_write_async (entry->stream, buffer->data, buffer->length,
+ G_PRIORITY_LOW, entry->cancellable,
+ (GAsyncReadyCallback) write_ready_cb,
+ fixture);
+ return TRUE;
+}
+
+static void
+msg_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, SoupCacheWritingFixture *fixture)
+{
+ SoupCacheEntry *entry = fixture->entry;
+
+ /* Ignore this if the writing or appending was cancelled */
+ if (!g_cancellable_is_cancelled (entry->cancellable)) {
+ g_queue_push_tail (fixture->buffer_queue, soup_buffer_copy (chunk));
+ entry->length += chunk->length;
+
+ if (!cache_accepts_entries_of_size (fixture->cache, entry->length)) {
+ /* Quickly cancel the caching of the resource */
+ g_cancellable_cancel (entry->cancellable);
+ }
+ }
+
+ /* FIXME: remove the error check when we cancel the caching at
+ the first write error */
+ /* Only write if the entry stream is ready */
+ if (entry->current_writing_buffer == NULL && entry->error == NULL && entry->stream)
+ write_next_buffer (entry, fixture);
+}
+
+static void
+msg_got_body_cb (SoupMessage *msg, SoupCacheWritingFixture *fixture)
+{
+ SoupCacheEntry *entry = fixture->entry;
+ g_return_if_fail (entry);
+
+ entry->got_body = TRUE;
+
+ if (!entry->stream && fixture->buffer_queue->length > 0)
+ /* The stream is not ready to be written but we still
+ have data to write, we'll write it when the stream
+ is opened for writing */
+ return;
+
+
+ if (fixture->buffer_queue->length > 0) {
+ /* If we still have data to write, write it,
+ write_ready_cb will close the stream */
+ if (entry->current_writing_buffer == NULL && entry->error == NULL && entry->stream)
+ write_next_buffer (entry, fixture);
+ return;
+ }
+
+ if (entry->stream && entry->current_writing_buffer == NULL)
+ g_output_stream_close_async (entry->stream,
+ G_PRIORITY_LOW,
+ entry->cancellable,
+ (GAsyncReadyCallback)close_ready_cb,
+ fixture);
+}
+
+static gboolean
+soup_cache_entry_remove (SoupCache *cache, SoupCacheEntry *entry)
+{
+ GList *lru_item;
+
+ /* if (entry->dirty && !g_cancellable_is_cancelled (entry->cancellable)) { */
+ if (entry->dirty) {
+ g_cancellable_cancel (entry->cancellable);
+ return FALSE;
+ }
+
+ g_assert (!entry->dirty);
+ g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+ if (!g_hash_table_remove (cache->priv->cache, GUINT_TO_POINTER (entry->key)))
+ return FALSE;
+
+ /* Remove from LRU */
+ lru_item = g_list_find (cache->priv->lru_start, entry);
+ cache->priv->lru_start = g_list_delete_link (cache->priv->lru_start, lru_item);
+
+ /* Adjust cache size */
+ cache->priv->size -= entry->length;
+
+ g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+ return TRUE;
+}
+
+static gint
+lru_compare_func (gconstpointer a, gconstpointer b)
+{
+ SoupCacheEntry *entry_a = (SoupCacheEntry *)a;
+ SoupCacheEntry *entry_b = (SoupCacheEntry *)b;
+
+ /* The rationale of this sorting func is
+ *
+ * 1. sort by hits -> LRU algorithm, then
+ *
+ * 2. sort by freshness lifetime, we better discard first
+ * entries that are close to expire
+ *
+ * 3. sort by size, replace first small size resources as they
+ * are cheaper to download
+ */
+
+ /* Sort by hits */
+ if (entry_a->hits != entry_b->hits)
+ return entry_a->hits - entry_b->hits;
+
+ /* Sort by freshness_lifetime */
+ if (entry_a->freshness_lifetime != entry_b->freshness_lifetime)
+ return entry_a->freshness_lifetime - entry_b->freshness_lifetime;
+
+ /* Sort by size */
+ return entry_a->length - entry_b->length;
+}
+
+static gboolean
+cache_accepts_entries_of_size (SoupCache *cache, guint length_to_add)
+{
+ /* We could add here some more heuristics. TODO: review how
+ this is done by other HTTP caches */
+
+ return length_to_add <= cache->priv->max_entry_data_size;
+}
+
+static void
+make_room_for_new_entry (SoupCache *cache, guint length_to_add)
+{
+ GList *lru_entry = cache->priv->lru_start;
+
+ /* Check that there is enough room for the new entry. This is
+ an approximation as we're not working out the size of the
+ cache file or the size of the headers for performance
+ reasons. TODO: check if that would be really that expensive */
+
+ while (lru_entry &&
+ (length_to_add + cache->priv->size > cache->priv->max_size)) {
+ SoupCacheEntry *old_entry = (SoupCacheEntry *)lru_entry->data;
+
+ /* Discard entries. Once cancelled resources will be
+ * freed in close_ready_cb
+ */
+ if (soup_cache_entry_remove (cache, old_entry)) {
+ soup_cache_entry_free (old_entry, get_file_from_entry (cache, old_entry));
+ lru_entry = cache->priv->lru_start;
+ } else
+ lru_entry = g_list_next (lru_entry);
+ }
+}
+
+static gboolean
+soup_cache_entry_insert (SoupCache *cache,
+ SoupCacheEntry *entry,
+ gboolean sort)
+{
+ guint length_to_add = 0;
+ SoupCacheEntry *old_entry;
+
+ /* Fill the key */
+ entry->key = get_cache_key_from_uri ((const char *) entry->uri);
+
+ if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CHUNKED)
+ length_to_add = soup_message_headers_get_content_length (entry->headers);
+
+ /* Check if we are going to store the resource depending on its size */
+ if (length_to_add) {
+ if (!cache_accepts_entries_of_size (cache, length_to_add))
+ return FALSE;
+
+ /* Make room for new entry if needed */
+ make_room_for_new_entry (cache, length_to_add);
+ }
+
+ /* Remove any previous entry */
+ if ((old_entry = g_hash_table_lookup (cache->priv->cache, GUINT_TO_POINTER (entry->key))) != NULL) {
+ if (soup_cache_entry_remove (cache, old_entry))
+ soup_cache_entry_free (old_entry, get_file_from_entry (cache, old_entry));
+ else
+ return FALSE;
+ }
+
+ /* Add to hash table */
+ g_hash_table_insert (cache->priv->cache, GUINT_TO_POINTER (entry->key), entry);
+
+ /* Compute new cache size */
+ cache->priv->size += length_to_add;
+
+ /* Update LRU */
+ if (sort)
+ cache->priv->lru_start = g_list_insert_sorted (cache->priv->lru_start, entry, lru_compare_func);
+ else
+ cache->priv->lru_start = g_list_prepend (cache->priv->lru_start, entry);
+
+ g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+
+ return TRUE;
+}
+
+static SoupCacheEntry*
+soup_cache_entry_lookup (SoupCache *cache,
+ SoupMessage *msg)
+{
+ SoupCacheEntry *entry;
+ guint32 key;
+ char *uri = NULL;
+
+ uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+ key = get_cache_key_from_uri ((const char *) uri);
+
+ entry = g_hash_table_lookup (cache->priv->cache, GUINT_TO_POINTER (key));
+
+ if (entry != NULL && (strcmp (entry->uri, uri) != 0))
+ entry = NULL;
+
+ g_free (uri);
+ return entry;
+}
+
+static void
+msg_restarted_cb (SoupMessage *msg, SoupCacheEntry *entry)
+{
+ /* FIXME: What should we do here exactly? */
+}
+
+static void
+replace_cb (GObject *source, GAsyncResult *result, SoupCacheWritingFixture *fixture)
+{
+ SoupCacheEntry *entry = fixture->entry;
+ GOutputStream *stream = (GOutputStream *) g_file_replace_finish (G_FILE (source),
+ result, &entry->error);
+
+ if (g_cancellable_is_cancelled (entry->cancellable) || entry->error) {
+ if (stream)
+ g_object_unref (stream);
+ fixture->cache->priv->n_pending--;
+ entry->dirty = FALSE;
+ soup_cache_entry_remove (fixture->cache, entry);
+ soup_cache_entry_free (entry, get_file_from_entry (fixture->cache, entry));
+ soup_cache_writing_fixture_free (fixture);
+ return;
+ }
+
+ entry->stream = stream;
+
+ /* If we already got all the data we have to initiate the
+ * writing here, since we won't get more 'got-chunk'
+ * signals
+ */
+ if (!entry->got_body)
+ return;
+
+ /* It could happen that reading the data from server
+ * was completed before this happens. In that case
+ * there is no data
+ */
+ if (!write_next_buffer (entry, fixture))
+ /* Could happen if the resource is empty */
+ g_output_stream_close_async (stream, G_PRIORITY_LOW, entry->cancellable,
+ (GAsyncReadyCallback) close_ready_cb,
+ fixture);
+}
+
+typedef struct {
+ time_t request_time;
+ SoupSessionFeature *feature;
+ gulong got_headers_handler;
+} RequestHelper;
+
+static void
+msg_got_headers_cb (SoupMessage *msg, gpointer user_data)
+{
+ SoupCache *cache;
+ SoupCacheability cacheable;
+ RequestHelper *helper;
+ time_t request_time, response_time;
+ SoupCacheEntry *entry;
+
+ response_time = time (NULL);
+
+ helper = (RequestHelper *)user_data;
+ cache = SOUP_CACHE (helper->feature);
+ request_time = helper->request_time;
+ g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data);
+ g_slice_free (RequestHelper, helper);
+
+ cacheable = soup_cache_get_cacheability (cache, msg);
+
+ if (cacheable & SOUP_CACHE_CACHEABLE) {
+ GFile *file;
+ SoupCacheWritingFixture *fixture;
+
+ /* Check if we are already caching this resource */
+ entry = soup_cache_entry_lookup (cache, msg);
+
+ if (entry && entry->dirty)
+ return;
+
+ /* Create a new entry, deleting any old one if present */
+ if (entry) {
+ soup_cache_entry_remove (cache, entry);
+ soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+ }
+
+ entry = soup_cache_entry_new (cache, msg, request_time, response_time);
+ entry->hits = 1;
+
+ /* Do not continue if it can not be stored */
+ if (!soup_cache_entry_insert (cache, entry, TRUE)) {
+ soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+ return;
+ }
+
+ fixture = g_slice_new0 (SoupCacheWritingFixture);
+ fixture->cache = g_object_ref (cache);
+ fixture->entry = entry;
+ fixture->msg = g_object_ref (msg);
+ fixture->buffer_queue = g_queue_new ();
+
+ /* We connect now to these signals and buffer the data
+ if it comes before the file is ready for writing */
+ fixture->got_chunk_handler =
+ g_signal_connect (msg, "got-chunk", G_CALLBACK (msg_got_chunk_cb), fixture);
+ fixture->got_body_handler =
+ g_signal_connect (msg, "got-body", G_CALLBACK (msg_got_body_cb), fixture);
+ fixture->restarted_handler =
+ g_signal_connect (msg, "restarted", G_CALLBACK (msg_restarted_cb), entry);
+
+ /* Prepare entry */
+ cache->priv->n_pending++;
+
+ entry->dirty = TRUE;
+ entry->cancellable = g_cancellable_new ();
+ file = get_file_from_entry (cache, entry);
+ g_file_replace_async (file, NULL, FALSE,
+ G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION,
+ G_PRIORITY_LOW, entry->cancellable,
+ (GAsyncReadyCallback) replace_cb, fixture);
+ g_object_unref (file);
+ } else if (cacheable & SOUP_CACHE_INVALIDATES) {
+ entry = soup_cache_entry_lookup (cache, msg);
+
+ if (entry) {
+ if (soup_cache_entry_remove (cache, entry))
+ soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+ }
+ } else if (cacheable & SOUP_CACHE_VALIDATES) {
+ entry = soup_cache_entry_lookup (cache, msg);
+
+ /* It's possible to get a CACHE_VALIDATES with no
+ * entry in the hash table. This could happen if for
+ * example the soup client is the one creating the
+ * conditional request.
+ */
+ if (entry) {
+ entry->being_validated = FALSE;
+ copy_end_to_end_headers (msg->response_headers, entry->headers);
+ soup_cache_entry_set_freshness (entry, msg, cache);
+ }
+ }
+}
+
+GInputStream *
+soup_cache_send_response (SoupCache *cache, SoupMessage *msg)
+{
+ SoupCacheEntry *entry;
+ char *current_age;
+ GInputStream *stream = NULL;
+ GFile *file;
+
+ g_return_val_if_fail (SOUP_IS_CACHE (cache), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ entry = soup_cache_entry_lookup (cache, msg);
+ g_return_val_if_fail (entry, NULL);
+
+ /* TODO: the original idea was to save reads, but current code
+ assumes that a stream is always returned. Need to reach
+ some agreement here. Also we have to handle the situation
+ were the file was no longer there (for example files
+ removed without notifying the cache */
+ file = get_file_from_entry (cache, entry);
+ stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+ g_object_unref (file);
+
+ /* Do not change the original message if there is no resource */
+ if (stream == NULL)
+ return stream;
+
+ /* If we are told to send a response from cache any validation
+ in course is over by now */
+ entry->being_validated = FALSE;
+
+ /* Status */
+ soup_message_set_status (msg, entry->status_code);
+
+ /* Headers */
+ copy_end_to_end_headers (entry->headers, msg->response_headers);
+
+ /* Add 'Age' header with the current age */
+ current_age = g_strdup_printf ("%d", soup_cache_entry_get_current_age (entry));
+ soup_message_headers_replace (msg->response_headers,
+ "Age",
+ current_age);
+ g_free (current_age);
+
+ return stream;
+}
+
+static void
+request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket)
+{
+ RequestHelper *helper = g_slice_new0 (RequestHelper);
+ helper->request_time = time (NULL);
+ helper->feature = feature;
+ helper->got_headers_handler = g_signal_connect (msg, "got-headers",
+ G_CALLBACK (msg_got_headers_cb),
+ helper);
+}
+
+static void
+attach (SoupSessionFeature *feature, SoupSession *session)
+{
+ SoupCache *cache = SOUP_CACHE (feature);
+ cache->priv->session = session;
+
+ soup_cache_default_feature_interface->attach (feature, session);
+}
+
+static void
+soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ soup_cache_default_feature_interface =
+ g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
+
+ feature_interface->attach = attach;
+ feature_interface->request_started = request_started;
+}
+
+static void
+soup_cache_init (SoupCache *cache)
+{
+ SoupCachePrivate *priv;
+
+ priv = cache->priv = SOUP_CACHE_GET_PRIVATE (cache);
+
+ priv->cache = g_hash_table_new (g_direct_hash, g_direct_equal);
+ /* LRU */
+ priv->lru_start = NULL;
+
+ /* */
+ priv->n_pending = 0;
+
+ /* Cache size */
+ priv->max_size = DEFAULT_MAX_SIZE;
+ priv->max_entry_data_size = priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
+ priv->size = 0;
+}
+
+static void
+remove_cache_item (gpointer data,
+ gpointer user_data)
+{
+ SoupCache *cache = (SoupCache *) user_data;
+ SoupCacheEntry *entry = (SoupCacheEntry *) data;
+
+ if (soup_cache_entry_remove (cache, entry))
+ soup_cache_entry_free (entry, NULL);
+}
+
+static void
+soup_cache_finalize (GObject *object)
+{
+ SoupCachePrivate *priv;
+ GList *entries;
+
+ priv = SOUP_CACHE (object)->priv;
+
+ // Cannot use g_hash_table_foreach as callbacks must not modify the hash table
+ entries = g_hash_table_get_values (priv->cache);
+ g_list_foreach (entries, remove_cache_item, object);
+ g_list_free (entries);
+
+ g_hash_table_destroy (priv->cache);
+ g_free (priv->cache_dir);
+
+ g_list_free (priv->lru_start);
+ priv->lru_start = NULL;
+
+ G_OBJECT_CLASS (soup_cache_parent_class)->finalize (object);
+}
+
+static void
+soup_cache_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupCachePrivate *priv = SOUP_CACHE (object)->priv;
+
+ switch (prop_id) {
+ case PROP_CACHE_DIR:
+ priv->cache_dir = g_value_dup_string (value);
+ /* Create directory if it does not exist (FIXME: should we?) */
+ if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
+ g_mkdir_with_parents (priv->cache_dir, 0700);
+ break;
+ case PROP_CACHE_TYPE:
+ priv->cache_type = g_value_get_enum (value);
+ /* TODO: clear private entries and issue a warning if moving to shared? */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_cache_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupCachePrivate *priv = SOUP_CACHE (object)->priv;
+
+ switch (prop_id) {
+ case PROP_CACHE_DIR:
+ g_value_set_string (value, priv->cache_dir);
+ break;
+ case PROP_CACHE_TYPE:
+ g_value_set_enum (value, priv->cache_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_cache_constructed (GObject *object)
+{
+ SoupCachePrivate *priv;
+
+ priv = SOUP_CACHE (object)->priv;
+
+ if (!priv->cache_dir) {
+ /* Set a default cache dir, different for each user */
+ priv->cache_dir = g_build_filename (g_get_user_cache_dir (),
+ "httpcache",
+ NULL);
+ if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
+ g_mkdir_with_parents (priv->cache_dir, 0700);
+ }
+
+ if (G_OBJECT_CLASS (soup_cache_parent_class)->constructed)
+ G_OBJECT_CLASS (soup_cache_parent_class)->constructed (object);
+}
+
+static void
+soup_cache_class_init (SoupCacheClass *cache_class)
+{
+ GObjectClass *gobject_class = (GObjectClass *)cache_class;
+
+ gobject_class->finalize = soup_cache_finalize;
+ gobject_class->constructed = soup_cache_constructed;
+ gobject_class->set_property = soup_cache_set_property;
+ gobject_class->get_property = soup_cache_get_property;
+
+ cache_class->get_cacheability = get_cacheability;
+
+ g_object_class_install_property (gobject_class, PROP_CACHE_DIR,
+ g_param_spec_string ("cache-dir",
+ "Cache directory",
+ "The directory to store the cache files",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (gobject_class, PROP_CACHE_TYPE,
+ g_param_spec_enum ("cache-type",
+ "Cache type",
+ "Whether the cache is private or shared",
+ SOUP_TYPE_CACHE_TYPE,
+ SOUP_CACHE_SINGLE_USER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (cache_class, sizeof (SoupCachePrivate));
+}
+
+/**
+ * soup_cache_new:
+ * @cache_dir: the directory to store the cached data, or %NULL to use the default one
+ * @cache_type: the #SoupCacheType of the cache
+ *
+ * Creates a new #SoupCache.
+ *
+ * Returns: a new #SoupCache
+ *
+ * Since: 2.34
+ */
+SoupCache *
+soup_cache_new (const char *cache_dir, SoupCacheType cache_type)
+{
+ return g_object_new (SOUP_TYPE_CACHE,
+ "cache-dir", cache_dir,
+ "cache-type", cache_type,
+ NULL);
+}
+
+/**
+ * soup_cache_has_response:
+ * @cache: a #SoupCache
+ * @msg: a #SoupMessage
+ *
+ * This function calculates whether the @cache object has a proper
+ * response for the request @msg given the flags both in the request
+ * and the cached reply and the time ellapsed since it was cached.
+ *
+ * Returns: whether or not the @cache has a valid response for @msg
+ *
+ * Since: 2.34
+ */
+SoupCacheResponse
+soup_cache_has_response (SoupCache *cache, SoupMessage *msg)
+{
+ SoupCacheEntry *entry;
+ const char *cache_control, *pragma;
+ gpointer value;
+ int max_age, max_stale, min_fresh;
+ GList *lru_item, *item;
+
+ entry = soup_cache_entry_lookup (cache, msg);
+
+ /* 1. The presented Request-URI and that of stored response
+ * match
+ */
+ if (!entry)
+ return SOUP_CACHE_RESPONSE_STALE;
+
+ /* Increase hit count. Take sorting into account */
+ entry->hits++;
+ lru_item = g_list_find (cache->priv->lru_start, entry);
+ item = lru_item;
+ while (item->next && lru_compare_func (item->data, item->next->data) > 0)
+ item = g_list_next (item);
+
+ if (item != lru_item) {
+ cache->priv->lru_start = g_list_remove_link (cache->priv->lru_start, lru_item);
+ item = g_list_insert_sorted (item, lru_item->data, lru_compare_func);
+ g_list_free (lru_item);
+ }
+
+ if (entry->dirty || entry->being_validated)
+ return SOUP_CACHE_RESPONSE_STALE;
+
+ /* 2. The request method associated with the stored response
+ * allows it to be used for the presented request
+ */
+
+ /* In practice this means we only return our resource for GET,
+ * cacheability for other methods is a TODO in the RFC
+ * (TODO: although we could return the headers for HEAD
+ * probably).
+ */
+ if (msg->method != SOUP_METHOD_GET)
+ return SOUP_CACHE_RESPONSE_STALE;
+
+ /* 3. Selecting request-headers nominated by the stored
+ * response (if any) match those presented.
+ */
+
+ /* TODO */
+
+ /* 4. The request is a conditional request issued by the client.
+ */
+ if (soup_message_headers_get (msg->request_headers, "If-Modified-Since") ||
+ soup_message_headers_get (msg->request_headers, "If-None-Match"))
+ return SOUP_CACHE_RESPONSE_STALE;
+
+ /* 5. The presented request and stored response are free from
+ * directives that would prevent its use.
+ */
+
+ max_age = max_stale = min_fresh = -1;
+
+ /* For HTTP 1.0 compatibility. RFC2616 section 14.9.4
+ */
+ pragma = soup_message_headers_get (msg->request_headers, "Pragma");
+ if (pragma && soup_header_contains (pragma, "no-cache"))
+ return SOUP_CACHE_RESPONSE_STALE;
+
+ cache_control = soup_message_headers_get (msg->request_headers, "Cache-Control");
+ if (cache_control) {
+ GHashTable *hash = soup_header_parse_param_list (cache_control);
+
+ if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
+ soup_header_free_param_list (hash);
+ return SOUP_CACHE_RESPONSE_STALE;
+ }
+
+ if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
+ soup_header_free_param_list (hash);
+ return SOUP_CACHE_RESPONSE_STALE;
+ }
+
+ if (g_hash_table_lookup_extended (hash, "max-age", NULL, &value)) {
+ max_age = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+ /* Forcing cache revalidaton
+ */
+ if (!max_age) {
+ soup_header_free_param_list (hash);
+ return SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+ }
+ }
+
+ /* max-stale can have no value set, we need to use _extended */
+ if (g_hash_table_lookup_extended (hash, "max-stale", NULL, &value)) {
+ if (value)
+ max_stale = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+ else
+ max_stale = G_MAXINT32;
+ }
+
+ value = g_hash_table_lookup (hash, "min-fresh");
+ if (value)
+ min_fresh = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32);
+
+ soup_header_free_param_list (hash);
+
+ if (max_age > 0) {
+ guint current_age = soup_cache_entry_get_current_age (entry);
+
+ /* If we are over max-age and max-stale is not
+ set, do not use the value from the cache
+ without validation */
+ if ((guint) max_age <= current_age && max_stale == -1)
+ return SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+ }
+ }
+
+ /* 6. The stored response is either: fresh, allowed to be
+ * served stale or succesfully validated
+ */
+ /* TODO consider also proxy-revalidate & s-maxage */
+ if (entry->must_revalidate)
+ return SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+
+ if (!soup_cache_entry_is_fresh_enough (entry, min_fresh)) {
+ /* Not fresh, can it be served stale? */
+ if (max_stale != -1) {
+ /* G_MAXINT32 means we accept any staleness */
+ if (max_stale == G_MAXINT32)
+ return SOUP_CACHE_RESPONSE_FRESH;
+
+ if ((soup_cache_entry_get_current_age (entry) - entry->freshness_lifetime) <= (guint) max_stale)
+ return SOUP_CACHE_RESPONSE_FRESH;
+ }
+
+ return SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
+ }
+
+ return SOUP_CACHE_RESPONSE_FRESH;
+}
+
+/**
+ * soup_cache_get_cacheability:
+ * @cache: a #SoupCache
+ * @msg: a #SoupMessage
+ *
+ * Calculates whether the @msg can be cached or not.
+ *
+ * Returns: a #SoupCacheability value indicating whether the @msg can be cached or not.
+ *
+ * Since: 2.34
+ */
+SoupCacheability
+soup_cache_get_cacheability (SoupCache *cache, SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_CACHE (cache), SOUP_CACHE_UNCACHEABLE);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_CACHE_UNCACHEABLE);
+
+ return SOUP_CACHE_GET_CLASS (cache)->get_cacheability (cache, msg);
+}
+
+static gboolean
+force_flush_timeout (gpointer data)
+{
+ gboolean *forced = (gboolean *)data;
+ *forced = TRUE;
+
+ return FALSE;
+}
+
+/**
+ * soup_cache_flush:
+ * @cache: a #SoupCache
+ *
+ * This function will force all pending writes in the @cache to be
+ * committed to disk. For doing so it will iterate the #GMainContext
+ * associated with @cache's session as long as needed.
+ *
+ * Since: 2.34
+ */
+void
+soup_cache_flush (SoupCache *cache)
+{
+ GMainContext *async_context;
+ SoupSession *session;
+ GSource *timeout;
+ gboolean forced = FALSE;
+
+ g_return_if_fail (SOUP_IS_CACHE (cache));
+
+ session = cache->priv->session;
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ async_context = soup_session_get_async_context (session);
+
+ /* We give cache 10 secs to finish */
+ timeout = soup_add_timeout (async_context, 10000, force_flush_timeout, &forced);
+
+ while (!forced && cache->priv->n_pending > 0)
+ g_main_context_iteration (async_context, FALSE);
+
+ if (!forced)
+ g_source_destroy (timeout);
+ else
+ g_warning ("Cache flush finished despite %d pending requests", cache->priv->n_pending);
+}
+
+static void
+clear_cache_item (gpointer data,
+ gpointer user_data)
+{
+ SoupCache *cache = (SoupCache *) user_data;
+ SoupCacheEntry *entry = (SoupCacheEntry *) data;
+
+ if (soup_cache_entry_remove (cache, entry))
+ soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+}
+
+/**
+ * soup_cache_clear:
+ * @cache: a #SoupCache
+ *
+ * Will remove all entries in the @cache plus all the cache files
+ * associated with them.
+ *
+ * Since: 2.34
+ */
+void
+soup_cache_clear (SoupCache *cache)
+{
+ GList *entries;
+
+ g_return_if_fail (SOUP_IS_CACHE (cache));
+ g_return_if_fail (cache->priv->cache);
+
+ // Cannot use g_hash_table_foreach as callbacks must not modify the hash table
+ entries = g_hash_table_get_values (cache->priv->cache);
+ g_list_foreach (entries, clear_cache_item, cache);
+ g_list_free (entries);
+}
+
+SoupMessage *
+soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original)
+{
+ SoupMessage *msg;
+ SoupURI *uri;
+ SoupCacheEntry *entry;
+ const char *value;
+
+ g_return_val_if_fail (SOUP_IS_CACHE (cache), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL);
+
+ /* First copy the data we need from the original message */
+ uri = soup_message_get_uri (original);
+ msg = soup_message_new_from_uri (original->method, uri);
+
+ soup_message_headers_foreach (original->request_headers,
+ (SoupMessageHeadersForeachFunc)copy_headers,
+ msg->request_headers);
+
+ /* Now add the validator entries in the header from the cached
+ data */
+ entry = soup_cache_entry_lookup (cache, original);
+ g_return_val_if_fail (entry, NULL);
+
+ entry->being_validated = TRUE;
+
+ value = soup_message_headers_get (entry->headers, "Last-Modified");
+ if (value)
+ soup_message_headers_append (msg->request_headers,
+ "If-Modified-Since",
+ value);
+ value = soup_message_headers_get (entry->headers, "ETag");
+ if (value)
+ soup_message_headers_append (msg->request_headers,
+ "If-None-Match",
+ value);
+ return msg;
+}
+
+#define OLD_SOUP_CACHE_FILE "soup.cache"
+#define SOUP_CACHE_FILE "soup.cache2"
+
+#define SOUP_CACHE_HEADERS_FORMAT "{ss}"
+#define SOUP_CACHE_PHEADERS_FORMAT "(sbuuuuuqa" SOUP_CACHE_HEADERS_FORMAT ")"
+#define SOUP_CACHE_ENTRIES_FORMAT "(qa" SOUP_CACHE_PHEADERS_FORMAT ")"
+
+/* Basically the same format than above except that some strings are
+ prepended with &. This way the GVariant returns a pointer to the
+ data instead of duplicating the string */
+#define SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}"
+
+static void
+pack_entry (gpointer data,
+ gpointer user_data)
+{
+ SoupCacheEntry *entry = (SoupCacheEntry *) data;
+ SoupMessageHeadersIter iter;
+ const char *header_key, *header_value;
+ GVariantBuilder *entries_builder = (GVariantBuilder *)user_data;
+
+ /* Do not store non-consolidated entries */
+ if (entry->dirty || entry->current_writing_buffer != NULL || !entry->key)
+ return;
+
+ g_variant_builder_open (entries_builder, G_VARIANT_TYPE (SOUP_CACHE_PHEADERS_FORMAT));
+#if ENABLE(TIZEN_FIX_PACK_ENTRY)
+ if (!g_utf8_validate (entry->uri, -1, NULL)) {
+ g_variant_builder_close (entries_builder);
+ return;
+ }
+#endif
+ g_variant_builder_add (entries_builder, "s", entry->uri);
+ g_variant_builder_add (entries_builder, "b", entry->must_revalidate);
+ g_variant_builder_add (entries_builder, "u", entry->freshness_lifetime);
+ g_variant_builder_add (entries_builder, "u", entry->corrected_initial_age);
+ g_variant_builder_add (entries_builder, "u", entry->response_time);
+ g_variant_builder_add (entries_builder, "u", entry->hits);
+ g_variant_builder_add (entries_builder, "u", entry->length);
+ g_variant_builder_add (entries_builder, "q", entry->status_code);
+
+ /* Pack headers */
+ g_variant_builder_open (entries_builder, G_VARIANT_TYPE ("a" SOUP_CACHE_HEADERS_FORMAT));
+ soup_message_headers_iter_init (&iter, entry->headers);
+ while (soup_message_headers_iter_next (&iter, &header_key, &header_value)) {
+ if (g_utf8_validate (header_value, -1, NULL))
+ g_variant_builder_add (entries_builder, SOUP_CACHE_HEADERS_FORMAT,
+ header_key, header_value);
+ }
+ g_variant_builder_close (entries_builder); /* "a" SOUP_CACHE_HEADERS_FORMAT */
+ g_variant_builder_close (entries_builder); /* SOUP_CACHE_PHEADERS_FORMAT */
+}
+
+void
+soup_cache_dump (SoupCache *cache)
+{
+ SoupCachePrivate *priv = SOUP_CACHE_GET_PRIVATE (cache);
+ char *filename;
+ GVariantBuilder entries_builder;
+ GVariant *cache_variant;
+
+ if (!g_list_length (cache->priv->lru_start))
+ return;
+
+ /* Create the builder and iterate over all entries */
+ g_variant_builder_init (&entries_builder, G_VARIANT_TYPE (SOUP_CACHE_ENTRIES_FORMAT));
+ g_variant_builder_add (&entries_builder, "q", SOUP_CACHE_CURRENT_VERSION);
+ g_variant_builder_open (&entries_builder, G_VARIANT_TYPE ("a" SOUP_CACHE_PHEADERS_FORMAT));
+ g_list_foreach (cache->priv->lru_start, pack_entry, &entries_builder);
+ g_variant_builder_close (&entries_builder);
+
+ /* Serialize and dump */
+ cache_variant = g_variant_builder_end (&entries_builder);
+ g_variant_ref_sink (cache_variant);
+ filename = g_build_filename (priv->cache_dir, SOUP_CACHE_FILE, NULL);
+ g_file_set_contents (filename, (const char *) g_variant_get_data (cache_variant),
+ g_variant_get_size (cache_variant), NULL);
+ g_free (filename);
+ g_variant_unref (cache_variant);
+}
+
+static void
+clear_cache_files (SoupCache *cache)
+{
+ GFileInfo *file_info;
+ GFileEnumerator *file_enumerator;
+ GFile *cache_dir_file = g_file_new_for_path (cache->priv->cache_dir);
+
+ file_enumerator = g_file_enumerate_children (cache_dir_file, G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (file_enumerator) {
+ while ((file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL)) != NULL) {
+ const char *filename = g_file_info_get_name (file_info);
+
+ if (strcmp (filename, SOUP_CACHE_FILE) != 0) {
+ GFile *cache_file = g_file_get_child (cache_dir_file, filename);
+ g_file_delete (cache_file, NULL, NULL);
+ g_object_unref (cache_file);
+ }
+ }
+ g_object_unref (file_enumerator);
+ }
+ g_object_unref (cache_dir_file);
+}
+
+void
+soup_cache_load (SoupCache *cache)
+{
+ gboolean must_revalidate;
+ guint32 freshness_lifetime, hits;
+ guint32 corrected_initial_age, response_time;
+ char *url, *filename = NULL, *contents = NULL;
+ GVariant *cache_variant;
+ GVariantIter *entries_iter = NULL, *headers_iter = NULL;
+ gsize length;
+ SoupCacheEntry *entry;
+ SoupCachePrivate *priv = cache->priv;
+ guint16 version, status_code;
+
+ filename = g_build_filename (priv->cache_dir, SOUP_CACHE_FILE, NULL);
+ if (!g_file_get_contents (filename, &contents, &length, NULL)) {
+ g_free (filename);
+ g_free (contents);
+ clear_cache_files (cache);
+ return;
+ }
+ g_free (filename);
+
+ cache_variant = g_variant_new_from_data (G_VARIANT_TYPE (SOUP_CACHE_ENTRIES_FORMAT),
+ (const char *) contents, length, FALSE, g_free, contents);
+ g_variant_get (cache_variant, SOUP_CACHE_ENTRIES_FORMAT, &version, &entries_iter);
+ if (version != SOUP_CACHE_CURRENT_VERSION) {
+ g_variant_iter_free (entries_iter);
+ g_variant_unref (cache_variant);
+ clear_cache_files (cache);
+ return;
+ }
+
+ while (g_variant_iter_loop (entries_iter, SOUP_CACHE_PHEADERS_FORMAT,
+ &url, &must_revalidate, &freshness_lifetime, &corrected_initial_age,
+ &response_time, &hits, &length, &status_code,
+ &headers_iter)) {
+ const char *header_key, *header_value;
+ SoupMessageHeaders *headers;
+ SoupMessageHeadersIter soup_headers_iter;
+
+ /* SoupMessage Headers */
+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+ while (g_variant_iter_loop (headers_iter, SOUP_CACHE_HEADERS_FORMAT, &header_key, &header_value))
+ if (*header_key && *header_value)
+ soup_message_headers_append (headers, header_key, header_value);
+
+ /* Check that we have headers */
+ soup_message_headers_iter_init (&soup_headers_iter, headers);
+ if (!soup_message_headers_iter_next (&soup_headers_iter, &header_key, &header_value)) {
+ soup_message_headers_free (headers);
+ continue;
+ }
+
+ /* Insert in cache */
+ entry = g_slice_new0 (SoupCacheEntry);
+ entry->uri = g_strdup (url);
+ entry->must_revalidate = must_revalidate;
+ entry->freshness_lifetime = freshness_lifetime;
+ entry->corrected_initial_age = corrected_initial_age;
+ entry->response_time = response_time;
+ entry->hits = hits;
+ entry->length = length;
+ entry->headers = headers;
+ entry->status_code = status_code;
+
+ if (!soup_cache_entry_insert (cache, entry, FALSE))
+ soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+ }
+
+ cache->priv->lru_start = g_list_reverse (cache->priv->lru_start);
+
+ /* frees */
+ g_variant_iter_free (entries_iter);
+ g_variant_unref (cache_variant);
+}
+
+void
+soup_cache_set_max_size (SoupCache *cache,
+ guint max_size)
+{
+ cache->priv->max_size = max_size;
+ cache->priv->max_entry_data_size = cache->priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
+}
+
+guint
+soup_cache_get_max_size (SoupCache *cache)
+{
+ return cache->priv->max_size;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache.h:
+ *
+ * Copyright (C) 2009, 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SOUP_CACHE_H
+#define SOUP_CACHE_H 1
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include <libsoup/soup-types.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CACHE (soup_cache_get_type ())
+#define SOUP_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CACHE, SoupCache))
+#define SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CACHE, SoupCacheClass))
+#define SOUP_IS_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CACHE))
+#define SOUP_IS_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CACHE))
+#define SOUP_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CACHE, SoupCacheClass))
+
+typedef struct _SoupCache SoupCache;
+typedef struct _SoupCachePrivate SoupCachePrivate;
+
+typedef enum {
+ SOUP_CACHE_CACHEABLE = (1 << 0),
+ SOUP_CACHE_UNCACHEABLE = (1 << 1),
+ SOUP_CACHE_INVALIDATES = (1 << 2),
+ SOUP_CACHE_VALIDATES = (1 << 3)
+} SoupCacheability;
+
+typedef enum {
+ SOUP_CACHE_RESPONSE_FRESH,
+ SOUP_CACHE_RESPONSE_NEEDS_VALIDATION,
+ SOUP_CACHE_RESPONSE_STALE
+} SoupCacheResponse;
+
+typedef enum {
+ SOUP_CACHE_SINGLE_USER,
+ SOUP_CACHE_SHARED
+} SoupCacheType;
+
+struct _SoupCache {
+ GObject parent_instance;
+
+ SoupCachePrivate *priv;
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* methods */
+ SoupCacheability (*get_cacheability) (SoupCache *cache,
+ SoupMessage *msg);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1)(void);
+ void (*_libsoup_reserved2)(void);
+ void (*_libsoup_reserved3)(void);
+} SoupCacheClass;
+
+GType soup_cache_get_type (void);
+SoupCache *soup_cache_new (const char *cache_dir,
+ SoupCacheType cache_type);
+void soup_cache_flush (SoupCache *cache);
+void soup_cache_clear (SoupCache *cache);
+
+void soup_cache_dump (SoupCache *cache);
+void soup_cache_load (SoupCache *cache);
+
+void soup_cache_set_max_size (SoupCache *cache,
+ guint max_size);
+guint soup_cache_get_max_size (SoupCache *cache);
+
+G_END_DECLS
+
+#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
+
+#endif /* SOUP_CACHE_H */
+
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-connection.c: A single HTTP/HTTPS connection
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "soup-address.h"
+#include "soup-connection.h"
+#include "soup-marshal.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-message-queue.h"
+#include "soup-misc.h"
+#include "soup-misc-private.h"
+#include "soup-socket.h"
+#include "soup-ssl.h"
+#include "soup-uri.h"
+#include "soup-enum-types.h"
+
+typedef struct {
+ SoupSocket *socket;
+
+ SoupAddress *remote_addr, *tunnel_addr;
+ SoupURI *proxy_uri;
+ gpointer ssl_creds;
+ gboolean ssl_strict;
+ gboolean ssl_fallback;
+
+ GMainContext *async_context;
+
+ SoupMessageQueueItem *cur_item;
+ SoupConnectionState state;
+ time_t unused_timeout;
+ guint io_timeout, idle_timeout;
+ GSource *idle_timeout_src;
+} SoupConnectionPrivate;
+#define SOUP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CONNECTION, SoupConnectionPrivate))
+
+G_DEFINE_TYPE (SoupConnection, soup_connection, G_TYPE_OBJECT)
+
+enum {
+ DISCONNECTED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+
+ PROP_REMOTE_ADDRESS,
+ PROP_TUNNEL_ADDRESS,
+ PROP_PROXY_URI,
+ PROP_SSL_CREDS,
+ PROP_SSL_STRICT,
+ PROP_SSL_FALLBACK,
+ PROP_ASYNC_CONTEXT,
+ PROP_TIMEOUT,
+ PROP_IDLE_TIMEOUT,
+ PROP_STATE,
+ PROP_MESSAGE,
+
+ LAST_PROP
+};
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void stop_idle_timer (SoupConnectionPrivate *priv);
+static void clear_current_item (SoupConnection *conn);
+
+/* Number of seconds after which we close a connection that hasn't yet
+ * been used.
+ */
+#define SOUP_CONNECTION_UNUSED_TIMEOUT 3
+
+static void
+soup_connection_init (SoupConnection *conn)
+{
+ ;
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
+
+ if (priv->remote_addr)
+ g_object_unref (priv->remote_addr);
+ if (priv->tunnel_addr)
+ g_object_unref (priv->tunnel_addr);
+ if (priv->proxy_uri)
+ soup_uri_free (priv->proxy_uri);
+
+ if (priv->async_context)
+ g_main_context_unref (priv->async_context);
+
+ G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object);
+}
+
+static void
+dispose (GObject *object)
+{
+ SoupConnection *conn = SOUP_CONNECTION (object);
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ stop_idle_timer (priv);
+ /* Make sure clear_current_item doesn't re-establish the timeout */
+ priv->idle_timeout = 0;
+
+ if (priv->cur_item) {
+ g_warning ("Disposing connection with cur_item set");
+ clear_current_item (conn);
+ }
+ if (priv->socket) {
+ g_warning ("Disposing connection while connected");
+ soup_connection_disconnect (conn);
+ }
+
+ G_OBJECT_CLASS (soup_connection_parent_class)->dispose (object);
+}
+
+static void
+soup_connection_class_init (SoupConnectionClass *connection_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
+
+ g_type_class_add_private (connection_class, sizeof (SoupConnectionPrivate));
+
+ /* virtual method override */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /* signals */
+ signals[DISCONNECTED] =
+ g_signal_new ("disconnected",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /* properties */
+ g_object_class_install_property (
+ object_class, PROP_REMOTE_ADDRESS,
+ g_param_spec_object (SOUP_CONNECTION_REMOTE_ADDRESS,
+ "Remote address",
+ "The address of the HTTP or proxy server",
+ SOUP_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_TUNNEL_ADDRESS,
+ g_param_spec_object (SOUP_CONNECTION_TUNNEL_ADDRESS,
+ "Tunnel address",
+ "The address of the HTTPS server this tunnel connects to",
+ SOUP_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_PROXY_URI,
+ g_param_spec_boxed (SOUP_CONNECTION_PROXY_URI,
+ "Proxy URI",
+ "URI of the HTTP proxy this connection connects to",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ object_class, PROP_SSL_CREDS,
+ g_param_spec_pointer (SOUP_CONNECTION_SSL_CREDENTIALS,
+ "SSL credentials",
+ "Opaque SSL credentials for this connection",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_SSL_STRICT,
+ g_param_spec_boolean (SOUP_CONNECTION_SSL_STRICT,
+ "Strictly validate SSL certificates",
+ "Whether certificate errors should be considered a connection error",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_SSL_FALLBACK,
+ g_param_spec_boolean (SOUP_CONNECTION_SSL_FALLBACK,
+ "SSLv3 fallback",
+ "Use SSLv3 instead of TLS",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_ASYNC_CONTEXT,
+ g_param_spec_pointer (SOUP_CONNECTION_ASYNC_CONTEXT,
+ "Async GMainContext",
+ "GMainContext to dispatch this connection's async I/O in",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_TIMEOUT,
+ g_param_spec_uint (SOUP_CONNECTION_TIMEOUT,
+ "Timeout value",
+ "Value in seconds to timeout a blocking I/O",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ object_class, PROP_IDLE_TIMEOUT,
+ g_param_spec_uint (SOUP_CONNECTION_IDLE_TIMEOUT,
+ "Idle Timeout",
+ "Connection lifetime when idle",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_STATE,
+ g_param_spec_enum (SOUP_CONNECTION_STATE,
+ "Connection state",
+ "Current state of connection",
+ SOUP_TYPE_CONNECTION_STATE, SOUP_CONNECTION_NEW,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ object_class, PROP_MESSAGE,
+ g_param_spec_object (SOUP_CONNECTION_MESSAGE,
+ "Message",
+ "Message being processed",
+ SOUP_TYPE_MESSAGE,
+ G_PARAM_READABLE));
+}
+
+
+SoupConnection *
+soup_connection_new (const char *propname1, ...)
+{
+ SoupConnection *conn;
+ va_list ap;
+
+ va_start (ap, propname1);
+ conn = (SoupConnection *)g_object_new_valist (SOUP_TYPE_CONNECTION,
+ propname1, ap);
+ va_end (ap);
+
+ return conn;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_REMOTE_ADDRESS:
+ priv->remote_addr = g_value_dup_object (value);
+ break;
+ case PROP_TUNNEL_ADDRESS:
+ priv->tunnel_addr = g_value_dup_object (value);
+ break;
+ case PROP_PROXY_URI:
+ if (priv->proxy_uri)
+ soup_uri_free (priv->proxy_uri);
+ priv->proxy_uri = g_value_dup_boxed (value);
+ break;
+ case PROP_SSL_CREDS:
+ priv->ssl_creds = g_value_get_pointer (value);
+ break;
+ case PROP_SSL_STRICT:
+ priv->ssl_strict = g_value_get_boolean (value);
+ break;
+ case PROP_SSL_FALLBACK:
+ priv->ssl_fallback = g_value_get_boolean (value);
+ break;
+ case PROP_ASYNC_CONTEXT:
+ priv->async_context = g_value_get_pointer (value);
+ if (priv->async_context)
+ g_main_context_ref (priv->async_context);
+ break;
+ case PROP_TIMEOUT:
+ priv->io_timeout = g_value_get_uint (value);
+ break;
+ case PROP_IDLE_TIMEOUT:
+ priv->idle_timeout = g_value_get_uint (value);
+ break;
+ case PROP_STATE:
+ soup_connection_set_state (SOUP_CONNECTION (object), g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_REMOTE_ADDRESS:
+ g_value_set_object (value, priv->remote_addr);
+ break;
+ case PROP_TUNNEL_ADDRESS:
+ g_value_set_object (value, priv->tunnel_addr);
+ break;
+ case PROP_PROXY_URI:
+ g_value_set_boxed (value, priv->proxy_uri);
+ break;
+ case PROP_SSL_CREDS:
+ g_value_set_pointer (value, priv->ssl_creds);
+ break;
+ case PROP_SSL_STRICT:
+ g_value_set_boolean (value, priv->ssl_strict);
+ break;
+ case PROP_SSL_FALLBACK:
+ g_value_set_boolean (value, priv->ssl_fallback);
+ break;
+ case PROP_ASYNC_CONTEXT:
+ g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
+ break;
+ case PROP_TIMEOUT:
+ g_value_set_uint (value, priv->io_timeout);
+ break;
+ case PROP_IDLE_TIMEOUT:
+ g_value_set_uint (value, priv->idle_timeout);
+ break;
+ case PROP_STATE:
+ g_value_set_enum (value, priv->state);
+ break;
+ case PROP_MESSAGE:
+ if (priv->cur_item)
+ g_value_set_object (value, priv->cur_item->msg);
+ else
+ g_value_set_object (value, NULL);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+idle_timeout (gpointer conn)
+{
+ soup_connection_disconnect (conn);
+ return FALSE;
+}
+
+static void
+start_idle_timer (SoupConnection *conn)
+{
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ if (priv->idle_timeout > 0 && !priv->idle_timeout_src) {
+ priv->idle_timeout_src =
+ soup_add_timeout (priv->async_context,
+ priv->idle_timeout * 1000,
+ idle_timeout, conn);
+ }
+}
+
+static void
+stop_idle_timer (SoupConnectionPrivate *priv)
+{
+ if (priv->idle_timeout_src) {
+ g_source_destroy (priv->idle_timeout_src);
+ priv->idle_timeout_src = NULL;
+ }
+}
+
+static void
+set_current_item (SoupConnection *conn, SoupMessageQueueItem *item)
+{
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ g_return_if_fail (priv->cur_item == NULL);
+
+ g_object_freeze_notify (G_OBJECT (conn));
+
+ stop_idle_timer (priv);
+
+ item->state = SOUP_MESSAGE_RUNNING;
+ priv->cur_item = item;
+ g_object_notify (G_OBJECT (conn), "message");
+
+ if (priv->state == SOUP_CONNECTION_IDLE ||
+ item->msg->method != SOUP_METHOD_CONNECT)
+ soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+
+ g_object_thaw_notify (G_OBJECT (conn));
+}
+
+static void
+clear_current_item (SoupConnection *conn)
+{
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ g_object_freeze_notify (G_OBJECT (conn));
+
+ priv->unused_timeout = 0;
+ start_idle_timer (conn);
+
+ if (priv->cur_item) {
+ SoupMessageQueueItem *item;
+
+ item = priv->cur_item;
+ priv->cur_item = NULL;
+ g_object_notify (G_OBJECT (conn), "message");
+
+ if (item->msg->method == SOUP_METHOD_CONNECT &&
+ SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code)) {
+ /* We're now effectively no longer proxying */
+ soup_uri_free (priv->proxy_uri);
+ priv->proxy_uri = NULL;
+ }
+
+ if (!soup_message_is_keepalive (item->msg))
+ soup_connection_disconnect (conn);
+ }
+
+ g_object_thaw_notify (G_OBJECT (conn));
+}
+
+static void
+socket_disconnected (SoupSocket *sock, gpointer conn)
+{
+ soup_connection_disconnect (conn);
+}
+
+typedef struct {
+ SoupConnection *conn;
+ SoupConnectionCallback callback;
+ gpointer callback_data;
+ GCancellable *cancellable;
+} SoupConnectionAsyncConnectData;
+
+static void
+socket_connect_finished (SoupSocket *socket, guint status, gpointer user_data)
+{
+ SoupConnectionAsyncConnectData *data = user_data;
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ g_signal_connect (priv->socket, "disconnected",
+ G_CALLBACK (socket_disconnected), data->conn);
+
+ soup_connection_set_state (data->conn, SOUP_CONNECTION_IN_USE);
+ priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
+ start_idle_timer (data->conn);
+ } else if (status == SOUP_STATUS_TLS_FAILED) {
+ priv->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
+ }
+
+ if (data->callback) {
+ if (priv->proxy_uri != NULL)
+ status = soup_status_proxify (status);
+ data->callback (data->conn, status, data->callback_data);
+ }
+ g_object_unref (data->conn);
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_slice_free (SoupConnectionAsyncConnectData, data);
+}
+
+static void
+socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
+{
+ SoupConnectionAsyncConnectData *data = user_data;
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status) &&
+ priv->ssl_creds && !priv->tunnel_addr) {
+ if (soup_socket_start_ssl (sock, data->cancellable)) {
+ soup_socket_handshake_async (sock, data->cancellable,
+ socket_connect_finished, data);
+ return;
+ }
+
+ status = SOUP_STATUS_SSL_FAILED;
+ }
+
+ socket_connect_finished (sock, status, data);
+}
+
+void
+soup_connection_connect_async (SoupConnection *conn,
+ GCancellable *cancellable,
+ SoupConnectionCallback callback,
+ gpointer user_data)
+{
+ SoupConnectionAsyncConnectData *data;
+ SoupConnectionPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ g_return_if_fail (priv->socket == NULL);
+
+ soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
+
+ data = g_slice_new (SoupConnectionAsyncConnectData);
+ data->conn = g_object_ref (conn);
+ data->callback = callback;
+ data->callback_data = user_data;
+ data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+ priv->socket =
+ soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
+ SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+ SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
+ SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
+ SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
+ SOUP_SOCKET_TIMEOUT, priv->io_timeout,
+ "clean-dispose", TRUE,
+ NULL);
+ soup_socket_connect_async (priv->socket, cancellable,
+ socket_connect_result, data);
+}
+
+guint
+soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable)
+{
+ SoupConnectionPrivate *priv;
+ guint status;
+
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
+
+ soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
+
+ priv->socket =
+ soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
+ SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+ SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
+ SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
+ SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
+ SOUP_SOCKET_TIMEOUT, priv->io_timeout,
+ "clean-dispose", TRUE,
+ NULL);
+
+ status = soup_socket_connect_sync (priv->socket, cancellable);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ goto fail;
+
+ if (priv->ssl_creds && !priv->tunnel_addr) {
+ if (!soup_socket_start_ssl (priv->socket, cancellable))
+ status = SOUP_STATUS_SSL_FAILED;
+ else {
+ status = soup_socket_handshake_sync (priv->socket, cancellable);
+ if (status == SOUP_STATUS_TLS_FAILED) {
+ priv->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
+ }
+ }
+ }
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ g_signal_connect (priv->socket, "disconnected",
+ G_CALLBACK (socket_disconnected), conn);
+
+ soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+ priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
+ start_idle_timer (conn);
+ } else {
+ fail:
+ if (priv->socket) {
+ soup_socket_disconnect (priv->socket);
+ g_object_unref (priv->socket);
+ priv->socket = NULL;
+ }
+ }
+
+ if (priv->proxy_uri != NULL)
+ status = soup_status_proxify (status);
+ return status;
+}
+
+SoupAddress *
+soup_connection_get_tunnel_addr (SoupConnection *conn)
+{
+ SoupConnectionPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ return priv->tunnel_addr;
+}
+
+guint
+soup_connection_start_ssl_sync (SoupConnection *conn,
+ GCancellable *cancellable)
+{
+ SoupConnectionPrivate *priv;
+ const char *server_name;
+ guint status;
+
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ server_name = soup_address_get_name (priv->tunnel_addr ?
+ priv->tunnel_addr :
+ priv->remote_addr);
+ if (!soup_socket_start_proxy_ssl (priv->socket, server_name,
+ cancellable))
+ return SOUP_STATUS_SSL_FAILED;
+
+ status = soup_socket_handshake_sync (priv->socket, cancellable);
+ if (status == SOUP_STATUS_TLS_FAILED) {
+ priv->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
+ }
+
+ return status;
+}
+
+static void
+start_ssl_completed (SoupSocket *socket, guint status, gpointer user_data)
+{
+ SoupConnectionAsyncConnectData *data = user_data;
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+
+ if (status == SOUP_STATUS_TLS_FAILED) {
+ priv->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
+ }
+
+ data->callback (data->conn, status, data->callback_data);
+ g_object_unref (data->conn);
+ g_slice_free (SoupConnectionAsyncConnectData, data);
+}
+
+static gboolean
+idle_start_ssl_completed (gpointer user_data)
+{
+ SoupConnectionAsyncConnectData *data = user_data;
+
+ start_ssl_completed (NULL, SOUP_STATUS_SSL_FAILED, data);
+ return FALSE;
+}
+
+void
+soup_connection_start_ssl_async (SoupConnection *conn,
+ GCancellable *cancellable,
+ SoupConnectionCallback callback,
+ gpointer user_data)
+{
+ SoupConnectionPrivate *priv;
+ const char *server_name;
+ SoupConnectionAsyncConnectData *data;
+
+ g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ data = g_slice_new (SoupConnectionAsyncConnectData);
+ data->conn = g_object_ref (conn);
+ data->callback = callback;
+ data->callback_data = user_data;
+
+ server_name = soup_address_get_name (priv->tunnel_addr ?
+ priv->tunnel_addr :
+ priv->remote_addr);
+ if (!soup_socket_start_proxy_ssl (priv->socket, server_name,
+ cancellable)) {
+ soup_add_completion (priv->async_context,
+ idle_start_ssl_completed, data);
+ return;
+ }
+
+ soup_socket_handshake_async (priv->socket, cancellable,
+ start_ssl_completed, data);
+}
+
+/**
+ * soup_connection_disconnect:
+ * @conn: a connection
+ *
+ * Disconnects @conn's socket and emits a %disconnected signal.
+ * After calling this, @conn will be essentially useless.
+ **/
+void
+soup_connection_disconnect (SoupConnection *conn)
+{
+ SoupConnectionPrivate *priv;
+ SoupConnectionState old_state;
+
+ g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ old_state = priv->state;
+ if (old_state != SOUP_CONNECTION_DISCONNECTED)
+ soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
+
+ if (priv->socket) {
+ g_signal_handlers_disconnect_by_func (priv->socket,
+ socket_disconnected, conn);
+ soup_socket_disconnect (priv->socket);
+ g_object_unref (priv->socket);
+ priv->socket = NULL;
+ }
+
+ if (old_state != SOUP_CONNECTION_DISCONNECTED)
+ g_signal_emit (conn, signals[DISCONNECTED], 0);
+}
+
+SoupSocket *
+soup_connection_get_socket (SoupConnection *conn)
+{
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
+
+ return SOUP_CONNECTION_GET_PRIVATE (conn)->socket;
+}
+
+SoupURI *
+soup_connection_get_proxy_uri (SoupConnection *conn)
+{
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
+
+ return SOUP_CONNECTION_GET_PRIVATE (conn)->proxy_uri;
+}
+
+gboolean
+soup_connection_is_via_proxy (SoupConnection *conn)
+{
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+
+ return SOUP_CONNECTION_GET_PRIVATE (conn)->proxy_uri != NULL;
+}
+
+SoupConnectionState
+soup_connection_get_state (SoupConnection *conn)
+{
+ SoupConnectionPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn),
+ SOUP_CONNECTION_DISCONNECTED);
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+#ifdef G_OS_UNIX
+ if (priv->state == SOUP_CONNECTION_IDLE) {
+ GPollFD pfd;
+
+ pfd.fd = soup_socket_get_fd (priv->socket);
+ pfd.events = G_IO_IN;
+ pfd.revents = 0;
+ if (g_poll (&pfd, 1, 0) == 1)
+ soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED);
+ }
+#endif
+ if (priv->state == SOUP_CONNECTION_IDLE &&
+ priv->unused_timeout && priv->unused_timeout < time (NULL))
+ soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED);
+
+ return priv->state;
+}
+
+void
+soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
+{
+ SoupConnectionPrivate *priv;
+ SoupConnectionState old_state;
+
+ g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ g_return_if_fail (state >= SOUP_CONNECTION_NEW &&
+ state <= SOUP_CONNECTION_DISCONNECTED);
+
+ g_object_freeze_notify (G_OBJECT (conn));
+
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ old_state = priv->state;
+ priv->state = state;
+ if ((state == SOUP_CONNECTION_IDLE ||
+ state == SOUP_CONNECTION_DISCONNECTED) &&
+ old_state == SOUP_CONNECTION_IN_USE)
+ clear_current_item (conn);
+
+ g_object_notify (G_OBJECT (conn), "state");
+ g_object_thaw_notify (G_OBJECT (conn));
+}
+
+gboolean
+soup_connection_get_ever_used (SoupConnection *conn)
+{
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+
+ return SOUP_CONNECTION_GET_PRIVATE (conn)->unused_timeout == 0;
+}
+
+gboolean
+soup_connection_get_ssl_fallback (SoupConnection *conn)
+{
+ return SOUP_CONNECTION_GET_PRIVATE (conn)->ssl_fallback;
+}
+
+void
+soup_connection_send_request (SoupConnection *conn,
+ SoupMessageQueueItem *item,
+ SoupMessageCompletionFn completion_cb,
+ gpointer user_data)
+{
+ SoupConnectionPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ g_return_if_fail (item != NULL);
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ g_return_if_fail (priv->state != SOUP_CONNECTION_NEW && priv->state != SOUP_CONNECTION_DISCONNECTED);
+
+ if (item != priv->cur_item)
+ set_current_item (conn, item);
+ soup_message_send_request (item, completion_cb, user_data);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_CONNECTION_H
+#define SOUP_CONNECTION_H 1
+
+#include "soup-types.h"
+#include "soup-message-private.h"
+#include "soup-misc.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CONNECTION (soup_connection_get_type ())
+#define SOUP_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONNECTION, SoupConnection))
+#define SOUP_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONNECTION, SoupConnectionClass))
+#define SOUP_IS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CONNECTION))
+#define SOUP_IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CONNECTION))
+#define SOUP_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONNECTION, SoupConnectionClass))
+
+struct _SoupConnection {
+ GObject parent;
+
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*disconnected) (SoupConnection *);
+
+} SoupConnectionClass;
+
+GType soup_connection_get_type (void);
+
+
+typedef void (*SoupConnectionCallback) (SoupConnection *conn,
+ guint status,
+ gpointer data);
+
+#define SOUP_CONNECTION_REMOTE_ADDRESS "remote-address"
+#define SOUP_CONNECTION_TUNNEL_ADDRESS "tunnel-address"
+#define SOUP_CONNECTION_PROXY_URI "proxy-uri"
+#define SOUP_CONNECTION_SSL_CREDENTIALS "ssl-creds"
+#define SOUP_CONNECTION_SSL_STRICT "ssl-strict"
+#define SOUP_CONNECTION_SSL_FALLBACK "ssl-fallback"
+#define SOUP_CONNECTION_ASYNC_CONTEXT "async-context"
+#define SOUP_CONNECTION_TIMEOUT "timeout"
+#define SOUP_CONNECTION_IDLE_TIMEOUT "idle-timeout"
+#define SOUP_CONNECTION_STATE "state"
+#define SOUP_CONNECTION_MESSAGE "message"
+
+SoupConnection *soup_connection_new (const char *propname1,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void soup_connection_connect_async (SoupConnection *conn,
+ GCancellable *cancellable,
+ SoupConnectionCallback callback,
+ gpointer user_data);
+guint soup_connection_connect_sync (SoupConnection *conn,
+ GCancellable *cancellable);
+SoupAddress *soup_connection_get_tunnel_addr(SoupConnection *conn);
+guint soup_connection_start_ssl_sync (SoupConnection *conn,
+ GCancellable *cancellable);
+void soup_connection_start_ssl_async (SoupConnection *conn,
+ GCancellable *cancellable,
+ SoupConnectionCallback callback,
+ gpointer user_data);
+
+void soup_connection_disconnect (SoupConnection *conn);
+
+SoupSocket *soup_connection_get_socket (SoupConnection *conn);
+SoupURI *soup_connection_get_proxy_uri (SoupConnection *conn);
+gboolean soup_connection_is_via_proxy (SoupConnection *conn);
+
+SoupConnectionState soup_connection_get_state (SoupConnection *conn);
+void soup_connection_set_state (SoupConnection *conn,
+ SoupConnectionState state);
+
+gboolean soup_connection_get_ever_used (SoupConnection *conn);
+
+void soup_connection_send_request (SoupConnection *conn,
+ SoupMessageQueueItem *item,
+ SoupMessageCompletionFn completion_cb,
+ gpointer user_data);
+
+gboolean soup_connection_get_ssl_fallback (SoupConnection *conn);
+
+G_END_DECLS
+
+#endif /* SOUP_CONNECTION_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-content-decoder.c
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <gio/gio.h>
+
+#include "soup-content-decoder.h"
+#include "soup-enum-types.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-content-decoder
+ * @short_description: Content-Encoding handler
+ *
+ * #SoupContentDecoder handles the "Accept-Encoding" header on
+ * outgoing messages, and the "Content-Encoding" header on incoming
+ * ones. If you add it to a session with soup_session_add_feature() or
+ * soup_session_add_feature_by_type(), the session will automatically
+ * use Content-Encoding as appropriate.
+ *
+ * (Note that currently there is no way to (automatically) use
+ * Content-Encoding when sending a request body, or to pick specific
+ * encoding types to support.)
+ *
+ * If #SoupContentDecoder successfully decodes the Content-Encoding,
+ * it will set the %SOUP_MESSAGE_CONTENT_DECODED flag on the message,
+ * and the message body and the chunks in the #SoupMessage::got_chunk
+ * signals will contain the decoded data; however, the message headers
+ * will be unchanged (and so "Content-Encoding" will still be present,
+ * "Content-Length" will describe the original encoded length, etc).
+ *
+ * If "Content-Encoding" contains any encoding types that
+ * #SoupContentDecoder doesn't recognize, then none of the encodings
+ * will be decoded (and the %SOUP_MESSAGE_CONTENT_DECODED flag will
+ * not be set).
+ *
+ * Since: 2.28.2
+ **/
+
+struct _SoupContentDecoderPrivate {
+ GHashTable *decoders;
+};
+
+typedef GConverter * (*SoupContentDecoderCreator) (void);
+
+static void soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+static void request_queued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
+static void request_unqueued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
+
+static void finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_CODE (SoupContentDecoder, soup_content_decoder, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_content_decoder_session_feature_init))
+
+/* This is constant for now */
+#define ACCEPT_ENCODING_HEADER "gzip"
+
+static GConverter *
+gzip_decoder_creator (void)
+{
+ return (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
+}
+
+static void
+soup_content_decoder_init (SoupContentDecoder *decoder)
+{
+ decoder->priv = G_TYPE_INSTANCE_GET_PRIVATE (decoder,
+ SOUP_TYPE_CONTENT_DECODER,
+ SoupContentDecoderPrivate);
+
+ decoder->priv->decoders = g_hash_table_new (g_str_hash, g_str_equal);
+ /* Hardcoded for now */
+ g_hash_table_insert (decoder->priv->decoders, "gzip",
+ gzip_decoder_creator);
+ g_hash_table_insert (decoder->priv->decoders, "x-gzip",
+ gzip_decoder_creator);
+}
+
+static void
+soup_content_decoder_class_init (SoupContentDecoderClass *decoder_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (decoder_class);
+
+ g_type_class_add_private (decoder_class, sizeof (SoupContentDecoderPrivate));
+
+ object_class->finalize = finalize;
+}
+
+static void
+soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ feature_interface->request_queued = request_queued;
+ feature_interface->request_unqueued = request_unqueued;
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (object);
+
+ g_hash_table_destroy (decoder->priv->decoders);
+
+ G_OBJECT_CLASS (soup_content_decoder_parent_class)->finalize (object);
+}
+
+static void
+soup_content_decoder_got_headers_cb (SoupMessage *msg, SoupContentDecoder *decoder)
+{
+ SoupMessagePrivate *msgpriv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ const char *header;
+ GSList *encodings, *e;
+ SoupContentDecoderCreator converter_creator;
+ GConverter *converter;
+
+ header = soup_message_headers_get_list (msg->response_headers,
+ "Content-Encoding");
+ if (!header)
+ return;
+
+ /* Workaround for an apache bug (bgo 613361) */
+ if (!g_ascii_strcasecmp (header, "gzip")) {
+ const char *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
+
+ if (content_type &&
+ (!g_ascii_strcasecmp (content_type, "application/gzip") ||
+ !g_ascii_strcasecmp (content_type, "application/x-gzip")))
+ return;
+ }
+
+ /* OK, really, no one is ever going to use more than one
+ * encoding, but we'll be robust.
+ */
+ encodings = soup_header_parse_list (header);
+ if (!encodings)
+ return;
+
+ for (e = encodings; e; e = e->next) {
+ if (!g_hash_table_lookup (decoder->priv->decoders, e->data)) {
+ soup_header_free_list (encodings);
+ return;
+ }
+ }
+
+ /* msgpriv->decoders should be empty at this point anyway, but
+ * clean it up if it's not.
+ */
+ while (msgpriv->decoders) {
+ g_object_unref (msgpriv->decoders->data);
+ msgpriv->decoders = g_slist_delete_link (msgpriv->decoders, msgpriv->decoders);
+ }
+
+ for (e = encodings; e; e = e->next) {
+ converter_creator = g_hash_table_lookup (decoder->priv->decoders, e->data);
+ converter = converter_creator ();
+
+ /* Content-Encoding lists the codings in the order
+ * they were applied in, so we put decoders in reverse
+ * order so the last-applied will be the first
+ * decoded.
+ */
+ msgpriv->decoders = g_slist_prepend (msgpriv->decoders, converter);
+ }
+ soup_header_free_list (encodings);
+
+ soup_message_set_flags (msg, msgpriv->msg_flags | SOUP_MESSAGE_CONTENT_DECODED);
+}
+
+static void
+request_queued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg)
+{
+ SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (feature);
+
+ if (!soup_message_headers_get_one (msg->request_headers,
+ "Accept-Encoding")) {
+ soup_message_headers_append (msg->request_headers,
+ "Accept-Encoding",
+ ACCEPT_ENCODING_HEADER);
+ }
+
+ g_signal_connect (msg, "got-headers",
+ G_CALLBACK (soup_content_decoder_got_headers_cb),
+ decoder);
+}
+
+static void
+request_unqueued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg)
+{
+ g_signal_handlers_disconnect_by_func (msg, soup_content_decoder_got_headers_cb, feature);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef SOUP_CONTENT_DECODER_H
+#define SOUP_CONTENT_DECODER_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CONTENT_DECODER (soup_content_decoder_get_type ())
+#define SOUP_CONTENT_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONTENT_DECODER, SoupContentDecoder))
+#define SOUP_CONTENT_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONTENT_DECODER, SoupContentDecoderClass))
+#define SOUP_IS_CONTENT_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CONTENT_DECODER))
+#define SOUP_IS_CONTENT_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CONTENT_DECODER))
+#define SOUP_CONTENT_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONTENT_DECODER, SoupContentDecoderClass))
+
+typedef struct _SoupContentDecoderPrivate SoupContentDecoderPrivate;
+
+typedef struct {
+ GObject parent;
+
+ SoupContentDecoderPrivate *priv;
+} SoupContentDecoder;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+ void (*_libsoup_reserved5) (void);
+} SoupContentDecoderClass;
+
+GType soup_content_decoder_get_type (void);
+
+G_END_DECLS
+
+#endif /* SOUP_CONTENT_DECODER_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-content-sniffer.c
+ *
+ * Copyright (C) 2009 Gustavo Noronha Silva.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-content-sniffer.h"
+#include "soup-enum-types.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+/*TIZEN patch*/
+#include "TIZEN.h"
+
+/**
+ * SECTION:soup-content-sniffer
+ * @short_description: Content sniffing for #SoupSession
+ *
+ * A #SoupContentSniffer tries to detect the actual content type of
+ * the files that are being downloaded by looking at some of the data
+ * before the #SoupMessage emits its #SoupMessage::got-headers signal.
+ * #SoupContentSniffer implements #SoupSessionFeature, so you can add
+ * content sniffing to a session with soup_session_add_feature() or
+ * soup_session_add_feature_by_type().
+ *
+ * Since: 2.27.3
+ **/
+
+static char *sniff (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *buffer, GHashTable **params);
+static gsize get_buffer_size (SoupContentSniffer *sniffer);
+
+static void soup_content_sniffer_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+static void request_queued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
+static void request_unqueued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
+
+G_DEFINE_TYPE_WITH_CODE (SoupContentSniffer, soup_content_sniffer, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_content_sniffer_session_feature_init))
+
+static void
+soup_content_sniffer_init (SoupContentSniffer *content_sniffer)
+{
+}
+
+static void
+soup_content_sniffer_class_init (SoupContentSnifferClass *content_sniffer_class)
+{
+ content_sniffer_class->sniff = sniff;
+ content_sniffer_class->get_buffer_size = get_buffer_size;
+}
+
+static void
+soup_content_sniffer_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ feature_interface->request_queued = request_queued;
+ feature_interface->request_unqueued = request_unqueued;
+}
+
+/**
+ * soup_content_sniffer_new:
+ *
+ * Creates a new #SoupContentSniffer.
+ *
+ * Returns: a new #SoupContentSniffer
+ *
+ * Since: 2.27.3
+ **/
+SoupContentSniffer *
+soup_content_sniffer_new ()
+{
+ return g_object_new (SOUP_TYPE_CONTENT_SNIFFER, NULL);
+}
+
+/**
+ * soup_content_sniffer_sniff:
+ * @sniffer: a #SoupContentSniffer
+ * @msg: the message to sniff
+ * @buffer: a buffer containing the start of @msg's response body
+ * @params: (element-type utf8 utf8) (out) (transfer full) (allow-none): return
+ * location for Content-Type parameters (eg, "charset"), or %NULL
+ *
+ * Sniffs @buffer to determine its Content-Type. The result may also
+ * be influenced by the Content-Type declared in @msg's response
+ * headers.
+ *
+ * Return value: the sniffed Content-Type of @buffer; this will never be %NULL,
+ * but may be "application/octet-stream".
+ */
+char *
+soup_content_sniffer_sniff (SoupContentSniffer *sniffer,
+ SoupMessage *msg, SoupBuffer *buffer,
+ GHashTable **params)
+{
+ g_return_val_if_fail (SOUP_IS_CONTENT_SNIFFER (sniffer), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+ g_return_val_if_fail (buffer != NULL, NULL);
+
+ return SOUP_CONTENT_SNIFFER_GET_CLASS (sniffer)->sniff (sniffer, msg, buffer, params);
+}
+
+/* This table is based on the HTML5 spec;
+ * See 2.7.4 Content-Type sniffing: unknown type
+ */
+typedef struct {
+ /* @has_ws is TRUE if @pattern contains "generic" whitespace */
+ gboolean has_ws;
+ const guchar *mask;
+ const guchar *pattern;
+ guint pattern_length;
+ const char *sniffed_type;
+ gboolean scriptable;
+} SoupContentSnifferPattern;
+
+#if ENABLE(TIZEN_FIX_CONTENT_SNIFFER_PATTERN)
+/* This table is updated by TIZEN team(steve.jun@samsung.com), based on draft-abarth-mime-sniff-06
+ * (http://tools.ietf.org/html/draft-abarth-mime-sniff-06);
+ * See 5. Unknown Type
+ */
+static SoupContentSnifferPattern types_table[] = {
+ // <!DOCTYPE\xHTML
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x21\x44\x4F\x43\x54\x59\x50\x45\x20\x48\x54\x4D\x4C",
+ 14,
+ "text/html",
+ TRUE },
+ // <HTML
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x48\x54\x4D\x4C",
+ 5,
+ "text/html",
+ TRUE },
+ // <HEAD
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x48\x45\x41\x44",
+ 5,
+ "text/html",
+ TRUE },
+ // <SCRIPT
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x53\x43\x52\x49\x50\x54",
+ 7,
+ "text/html",
+ TRUE },
+ // <IFRAME
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x49\x46\x52\x41\x4d\x45",
+ 7,
+ "text/html",
+ TRUE },
+ // <H1
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xFF\xFF",
+ (const guchar *)" \x3C\x48\x31",
+ 3,
+ "text/html",
+ TRUE },
+ // <DIV
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x44\x49\x56",
+ 4,
+ "text/html",
+ TRUE },
+ // <FONT
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x46\x4f\x4e\x54",
+ 5,
+ "text/html",
+ TRUE },
+ // <TABLE
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x54\x41\x42\x4c\x45",
+ 6,
+ "text/html",
+ TRUE },
+ // <A
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xFF",
+ (const guchar *)" \x3C\x41",
+ 2,
+ "text/html",
+ TRUE },
+ // <STYLE
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x53\x54\x59\x4c\x45",
+ 6,
+ "text/html",
+ TRUE },
+ // <TITLE
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x54\x49\x54\x4c\x45",
+ 6,
+ "text/html",
+ TRUE },
+ // <B
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xFF",
+ (const guchar *)" \x3C\x42",
+ 2,
+ "text/html",
+ TRUE },
+ // <BODY
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x42\x4f\x44\x59",
+ 5,
+ "text/html",
+ TRUE },
+ // <BR
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xFF",
+ (const guchar *)" \x3C\x42\x52",
+ 3,
+ "text/html",
+ TRUE },
+ // <P
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xFF",
+ (const guchar *)" \x3C\x50",
+ 2,
+ "text/html",
+ TRUE },
+ // <!--
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)" \x3C\x21\x2d\x2d",
+ 4,
+ "text/html",
+ TRUE },
+ // <?xml (Note the case sensitivity and lack of trailing _>)
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)" \x3C\x3f\x78\x6d\x6c",
+ 5,
+ "text/xml",
+ TRUE },
+ // The string "%PDF-", the PDF signature.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x25\x50\x44\x46\x2D",
+ 5,
+ "application/pdf",
+ TRUE },
+ // The string "%!PS-Adobe-", the PostScript signature.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x25\x21\x50\x53\x2D\x41\x64\x6F\x62\x65\x2D",
+ 11,
+ "application/postscript",
+ FALSE },
+ // UTF-16BE BOM
+ { FALSE,
+ (const guchar *)"\xFF\xFF\x00\x00",
+ (const guchar *)"\xFE\xFF\x00\x00",
+ 4,
+ "text/plain",
+ FALSE },
+ // UTF-16LE BOM
+ { FALSE,
+ (const guchar *)"\xFF\xFF\x00\x00",
+ (const guchar *)"\xFF\xFE\x00\x00",
+ 4,
+ "text/plain",
+ FALSE },
+ // UTF-8 BOM
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\x00",
+ (const guchar *)"\xEF\xBB\xBF\x00",
+ 4,
+ "text/plain",
+ FALSE },
+ // The string "GIF87a", a GIF signature.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x47\x49\x46\x38\x37\x61",
+ 6,
+ "image/gif",
+ FALSE },
+ // The string "GIF89a", a GIF signature.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x47\x49\x46\x38\x39\x61",
+ 6,
+ "image/gif",
+ FALSE },
+ // The PNG signature.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A",
+ 8,
+ "image/png",
+ FALSE },
+ // A JPEG SOI marker followed by a octet of another marker.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF",
+ (const guchar *)"\xFF\xD8\xFF",
+ 3,
+ "image/jpeg",
+ FALSE },
+ // The string "BM", a BMP signature.
+ { FALSE,
+ (const guchar *)"\xFF\xFF",
+ (const guchar *)"\x42\x4D",
+ 2,
+ "image/bmp",
+ FALSE },
+ // "RIFF" followed by four bytes, followed by "WEBPVP".
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x52\x49\x46\x46\x00\x00\x00\x00\x57\x45\x42\x50\x56\x50",
+ 14,
+ "image/webp",
+ FALSE },
+ // A Windows Icon signature.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x00\x00\x01\x00",
+ 4,
+ "image/vnd.microsoft.icon",
+ FALSE },
+ // An Ogg Vorbis audio or video signature.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x4F\x67\x67\x53\x00",
+ 5,
+ "application/ogg",
+ FALSE },
+ // "RIFF" followed by four bytes, followed by "WAVE".
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x52\x49\x46\x46\x00\x00\x00\x00\x57\x41\x56\x45",
+ 12,
+ "audio/x-wave",
+ FALSE },
+ // The WebM signature [TODO: Use more octets?] vidow: typo(!)
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x1A\x45\xDF\xA3",
+ 4,
+ "vidow/webm",
+ FALSE },
+ // A RAR archive.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x52\x61\x72\x20\x1A\x07\x00",
+ 7,
+ "application/x-rar-compressed",
+ FALSE },
+ // A ZIP archive.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x50\x4B\x03\x04",
+ 4,
+ "application/zip",
+ FALSE },
+ // A GZIP archive.
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF",
+ (const guchar *)"\x1F\x8B\x08",
+ 3,
+ "application/x-gzip",
+ FALSE }
+};
+#else
+static SoupContentSnifferPattern types_table[] = {
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF",
+ (const guchar *)"\x3C\x21\x44\x4F\x43\x54\x59\x50\x45\x20\x48\x54\x4D\x4C",
+ 14,
+ "text/html",
+ TRUE },
+
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF",
+ (const guchar *)" \x3C\x48\x54\x4D\x4C",
+ 5,
+ "text/html",
+ TRUE },
+
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF",
+ (const guchar *)" \x3C\x48\x45\x41\x44",
+ 5,
+ "text/html",
+ TRUE },
+
+ { TRUE,
+ (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
+ (const guchar *)" \x3C\x53\x43\x52\x49\x50\x54",
+ 7,
+ "text/html",
+ TRUE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x25\x50\x44\x46\x2D",
+ 5,
+ "application/pdf",
+ TRUE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x25\x21\x50\x53\x2D\x41\x64\x6F\x62\x65\x2D",
+ 11,
+ "application/postscript",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\x00\x00",
+ (const guchar *)"\xFE\xFF\x00\x00",
+ 4,
+ "text/plain",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\x00\x00",
+ (const guchar *)"\xFF\xFF\x00\x00",
+ 4,
+ "text/plain",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\x00",
+ (const guchar *)"\xEF\xBB\xBF\x00",
+ 4,
+ "text/plain",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x47\x49\x46\x38\x37\x61",
+ 6,
+ "image/gif",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x47\x49\x46\x38\x39\x61",
+ 6,
+ "image/gif",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A",
+ 8,
+ "image/png",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF",
+ (const guchar *)"\xFF\xD8\xFF",
+ 3,
+ "image/jpeg",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF",
+ (const guchar *)"\x42\x4D",
+ 2,
+ "image/bmp",
+ FALSE },
+
+ { FALSE,
+ (const guchar *)"\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x00\x00\x01\x00",
+ 4,
+ "image/vnd.microsoft.icon",
+ FALSE }
+};
+#endif
+
+/* Whether a given byte looks like it might be part of binary content.
+ * Source: HTML5 spec; borrowed from the Chromium mime sniffer code,
+ * which is BSD-licensed
+ */
+static char byte_looks_binary[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, /* 0x00 - 0x0F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, /* 0x10 - 0x1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0 - 0xCF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0 - 0xDF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0 - 0xEF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xF0 - 0xFF */
+};
+
+/* HTML5: 2.7.4 Content-Type sniffing: unknown type */
+static char*
+sniff_unknown (SoupContentSniffer *sniffer, SoupMessage *msg,
+ SoupBuffer *buffer, gboolean for_text_or_binary)
+{
+ const guchar *resource = (const guchar *)buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (types_table); i++) {
+ SoupContentSnifferPattern *type_row = &(types_table[i]);
+
+ /* The scriptable types should be skiped for the text
+ * or binary path, but considered for other paths */
+ if (for_text_or_binary && type_row->scriptable)
+ continue;
+
+ if (type_row->has_ws) {
+ int index_stream = 0;
+ int index_pattern = 0;
+ gboolean skip_row = FALSE;
+
+ while ((index_stream < resource_length) &&
+ (index_pattern <= type_row->pattern_length)) {
+ /* Skip insignificant white space ("WS" in the spec) */
+ if (type_row->pattern[index_pattern] == ' ') {
+ if (resource[index_stream] == '\x09' ||
+ resource[index_stream] == '\x0a' ||
+ resource[index_stream] == '\x0c' ||
+ resource[index_stream] == '\x0d' ||
+ resource[index_stream] == '\x20')
+ index_stream++;
+ else
+ index_pattern++;
+ } else {
+ if ((type_row->mask[index_pattern] & resource[index_stream]) != type_row->pattern[index_pattern]) {
+ skip_row = TRUE;
+ break;
+ }
+ index_pattern++;
+ index_stream++;
+ }
+ }
+
+ if (skip_row)
+ continue;
+
+ if (index_pattern > type_row->pattern_length)
+ return g_strdup (type_row->sniffed_type);
+ } else {
+ int j;
+
+ if (resource_length < type_row->pattern_length)
+ continue;
+
+ for (j = 0; j < type_row->pattern_length; j++) {
+ if ((type_row->mask[j] & resource[j]) != type_row->pattern[j])
+ break;
+ }
+
+ /* This means our comparison above matched completely */
+ if (j == type_row->pattern_length)
+ return g_strdup (type_row->sniffed_type);
+ }
+ }
+
+ if (for_text_or_binary)
+ return g_strdup ("application/octet-stream");
+
+ for (i = 0; i < resource_length; i++) {
+ if (byte_looks_binary[resource[i]])
+ return g_strdup ("application/octet-stream");
+ }
+
+ return g_strdup ("text/plain");
+}
+
+/* HTML5: 2.7.3 Content-Type sniffing: text or binary */
+static char*
+sniff_text_or_binary (SoupContentSniffer *sniffer, SoupMessage *msg,
+ SoupBuffer *buffer)
+{
+ const guchar *resource = (const guchar *)buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ gboolean looks_binary = FALSE;
+ int i;
+
+ /* Detecting UTF-16BE, UTF-16LE, or UTF-8 BOMs means it's text/plain */
+ if (resource_length >= 4) {
+ if ((resource[0] == 0xFE && resource[1] == 0xFF) ||
+ (resource[0] == 0xFF && resource[1] == 0xFE) ||
+ (resource[0] == 0xEF && resource[1] == 0xBB && resource[2] == 0xBF))
+ return g_strdup ("text/plain");
+ }
+
+ /* Look to see if any of the first n bytes looks binary */
+ for (i = 0; i < resource_length; i++) {
+ if (byte_looks_binary[resource[i]]) {
+ looks_binary = TRUE;
+ break;
+ }
+ }
+
+ if (!looks_binary)
+ return g_strdup ("text/plain");
+
+ return sniff_unknown (sniffer, msg, buffer, TRUE);
+}
+
+static char*
+sniff_images (SoupContentSniffer *sniffer, SoupMessage *msg,
+ SoupBuffer *buffer, const char *content_type)
+{
+ const guchar *resource = (const guchar *)buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (types_table); i++) {
+ SoupContentSnifferPattern *type_row = &(types_table[i]);
+
+ if (resource_length < type_row->pattern_length)
+ continue;
+
+ if (!g_str_has_prefix (type_row->sniffed_type, "image/"))
+ continue;
+
+ /* All of the image types use all-\xFF for the mask,
+ * so we can just memcmp.
+ */
+ if (memcmp (type_row->pattern, resource, type_row->pattern_length) == 0)
+ return g_strdup (type_row->sniffed_type);
+ }
+
+ return g_strdup (content_type);
+}
+
+static char*
+sniff_feed_or_html (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *buffer)
+{
+ const guchar *resource = (const guchar *)buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ int pos = 0;
+
+ if (resource_length < 3)
+ goto text_html;
+
+ /* Skip a leading UTF-8 BOM */
+ if (resource[0] == 0xEF && resource[1] == 0xBB && resource[2] == 0xBF)
+ pos = 3;
+
+ look_for_tag:
+ if (pos > resource_length)
+ goto text_html;
+
+ /* Skip insignificant white space */
+ while ((resource[pos] == '\x09') ||
+ (resource[pos] == '\x20') ||
+ (resource[pos] == '\x0A') ||
+ (resource[pos] == '\x0D')) {
+ pos++;
+
+ if (pos > resource_length)
+ goto text_html;
+ }
+
+ /* != < */
+ if (resource[pos] != '\x3C')
+ return g_strdup ("text/html");
+
+ pos++;
+
+ if ((pos + 2) > resource_length)
+ goto text_html;
+
+ /* Skipping comments */
+ if ((resource[pos] == '\x2D') ||
+ (resource[pos+1] == '\x2D') ||
+ (resource[pos+2] == '\x3E')) {
+ pos = pos + 3;
+
+ if ((pos + 2) > resource_length)
+ goto text_html;
+
+ while ((resource[pos] != '\x2D') &&
+ (resource[pos+1] != '\x2D') &&
+ (resource[pos+2] != '\x3E')) {
+ pos++;
+
+ if ((pos + 2) > resource_length)
+ goto text_html;
+ }
+
+ goto look_for_tag;
+ }
+
+ if (pos > resource_length)
+ goto text_html;
+
+ /* == ! */
+ if (resource[pos] == '\x21') {
+ do {
+ pos++;
+
+ if (pos > resource_length)
+ goto text_html;
+ } while (resource[pos] != '\x3E');
+
+ pos++;
+
+ goto look_for_tag;
+ } else if (resource[pos] == '\x3F') { /* ? */
+ do {
+ pos++;
+
+ if ((pos + 1) > resource_length)
+ goto text_html;
+ } while ((resource[pos] != '\x3F') &&
+ (resource[pos+1] != '\x3E'));
+
+ pos = pos + 2;
+
+ goto look_for_tag;
+ }
+
+ if ((pos + 2) > resource_length)
+ goto text_html;
+
+ if ((resource[pos] == '\x72') &&
+ (resource[pos+1] == '\x73') &&
+ (resource[pos+2] == '\x73'))
+ return g_strdup ("application/rss+xml");
+
+ if ((pos + 3) > resource_length)
+ goto text_html;
+
+ if ((resource[pos] == '\x66') &&
+ (resource[pos+1] == '\x65') &&
+ (resource[pos+2] == '\x65') &&
+ (resource[pos+3] == '\x64'))
+ return g_strdup ("application/atom+xml");
+
+ text_html:
+ return g_strdup ("text/html");
+}
+
+static char*
+sniff (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *buffer, GHashTable **params)
+{
+ const char *content_type;
+
+ content_type = soup_message_headers_get_content_type (msg->response_headers, params);
+
+ /* These comparisons are done in an ASCII-case-insensitive
+ * manner because the spec requires it */
+ if ((content_type == NULL) ||
+ !g_ascii_strcasecmp (content_type, "unknown/unknown") ||
+ !g_ascii_strcasecmp (content_type, "application/unknown") ||
+ !g_ascii_strcasecmp (content_type, "*/*"))
+ return sniff_unknown (sniffer, msg, buffer, FALSE);
+
+ if (g_str_has_suffix (content_type, "+xml") ||
+ !g_ascii_strcasecmp (content_type, "text/xml") ||
+ !g_ascii_strcasecmp (content_type, "application/xml"))
+ return g_strdup (content_type);
+
+ /* 2.7.5 Content-Type sniffing: image
+ * The spec says:
+ *
+ * If the resource's official type is "image/svg+xml", then
+ * the sniffed type of the resource is its official type (an
+ * XML type)
+ *
+ * The XML case is handled by the if above; if you refactor
+ * this code, keep this in mind.
+ */
+ if (!g_ascii_strncasecmp (content_type, "image/", 6))
+ return sniff_images (sniffer, msg, buffer, content_type);
+
+ /* If we got text/plain, use text_or_binary */
+ if (g_str_equal (content_type, "text/plain")) {
+ return sniff_text_or_binary (sniffer, msg, buffer);
+ }
+
+ if (!g_ascii_strcasecmp (content_type, "text/html"))
+ return sniff_feed_or_html (sniffer, msg, buffer);
+
+ return g_strdup (content_type);
+}
+
+static gsize
+get_buffer_size (SoupContentSniffer *sniffer)
+{
+ return 512;
+}
+
+static void
+soup_content_sniffer_got_headers_cb (SoupMessage *msg, SoupContentSniffer *sniffer)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupContentSnifferClass *content_sniffer_class = SOUP_CONTENT_SNIFFER_GET_CLASS (sniffer);
+
+ priv->bytes_for_sniffing = content_sniffer_class->get_buffer_size (sniffer);
+}
+
+static void
+request_queued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ priv->sniffer = g_object_ref (feature);
+ g_signal_connect (msg, "got-headers",
+ G_CALLBACK (soup_content_sniffer_got_headers_cb),
+ feature);
+}
+
+static void
+request_unqueued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ g_object_unref (priv->sniffer);
+ priv->sniffer = NULL;
+
+ g_signal_handlers_disconnect_by_func (msg, soup_content_sniffer_got_headers_cb, feature);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Gustavo Noronha Silva.
+ */
+
+#ifndef SOUP_CONTENT_SNIFFER_H
+#define SOUP_CONTENT_SNIFFER_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CONTENT_SNIFFER (soup_content_sniffer_get_type ())
+#define SOUP_CONTENT_SNIFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONTENT_SNIFFER, SoupContentSniffer))
+#define SOUP_CONTENT_SNIFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONTENT_SNIFFER, SoupContentSnifferClass))
+#define SOUP_IS_CONTENT_SNIFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CONTENT_SNIFFER))
+#define SOUP_IS_CONTENT_SNIFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CONTENT_SNIFFER))
+#define SOUP_CONTENT_SNIFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONTENT_SNIFFER, SoupContentSnifferClass))
+
+typedef struct _SoupContentSnifferPrivate SoupContentSnifferPrivate;
+
+typedef struct {
+ GObject parent;
+
+ SoupContentSnifferPrivate *priv;
+} SoupContentSniffer;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ char* (*sniff) (SoupContentSniffer *sniffer,
+ SoupMessage *msg,
+ SoupBuffer *buffer,
+ GHashTable **params);
+ gsize (*get_buffer_size) (SoupContentSniffer *sniffer);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+ void (*_libsoup_reserved5) (void);
+} SoupContentSnifferClass;
+
+GType soup_content_sniffer_get_type (void);
+
+SoupContentSniffer *soup_content_sniffer_new (void);
+
+char *soup_content_sniffer_sniff (SoupContentSniffer *sniffer,
+ SoupMessage *msg,
+ SoupBuffer *buffer,
+ GHashTable **params);
+
+G_END_DECLS
+
+#endif /* SOUP_CONTENT_SNIFFER_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cookie-jar-sqlite.c: ff sqlite-based cookie storage
+ *
+ * Using danw's soup-cookie-jar-text as template
+ * Copyright (C) 2008 Diego Escalante Urrelo
+ * Copyright (C) 2009 Collabora Ltd.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sqlite3.h>
+
+#include "soup-cookie-jar-sqlite.h"
+#include "soup-cookie.h"
+#include "soup-date.h"
+
+/**
+ * SECTION:soup-cookie-jar-sqlite
+ * @short_description: SQLite-based Cookie Jar
+ *
+ * #SoupCookieJarSqlite is a #SoupCookieJar that reads cookies from and
+ * writes them to an SQLite file in the new Mozilla format.
+ **/
+
+enum {
+ PROP_0,
+
+ PROP_FILENAME,
+
+ LAST_PROP
+};
+
+typedef struct {
+ char *filename;
+ sqlite3 *db;
+} SoupCookieJarSqlitePrivate;
+
+#define SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqlitePrivate))
+
+G_DEFINE_TYPE (SoupCookieJarSqlite, soup_cookie_jar_sqlite, SOUP_TYPE_COOKIE_JAR)
+
+static void load (SoupCookieJar *jar);
+static void changed (SoupCookieJar *jar,
+ SoupCookie *old_cookie,
+ SoupCookie *new_cookie);
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+soup_cookie_jar_sqlite_init (SoupCookieJarSqlite *sqlite)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupCookieJarSqlitePrivate *priv =
+ SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
+
+ g_free (priv->filename);
+
+ if (priv->db)
+ sqlite3_close (priv->db);
+
+ G_OBJECT_CLASS (soup_cookie_jar_sqlite_parent_class)->finalize (object);
+}
+
+static void
+soup_cookie_jar_sqlite_class_init (SoupCookieJarSqliteClass *sqlite_class)
+{
+ SoupCookieJarClass *cookie_jar_class =
+ SOUP_COOKIE_JAR_CLASS (sqlite_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (sqlite_class);
+
+ g_type_class_add_private (sqlite_class, sizeof (SoupCookieJarSqlitePrivate));
+
+ cookie_jar_class->changed = changed;
+
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /**
+ * SOUP_COOKIE_JAR_SQLITE_FILENAME:
+ *
+ * Alias for the #SoupCookieJarSqlite:filename property. (The
+ * cookie-storage filename.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_FILENAME,
+ g_param_spec_string (SOUP_COOKIE_JAR_SQLITE_FILENAME,
+ "Filename",
+ "Cookie-storage filename",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarSqlitePrivate *priv =
+ SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FILENAME:
+ priv->filename = g_value_dup_string (value);
+ load (SOUP_COOKIE_JAR (object));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarSqlitePrivate *priv =
+ SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FILENAME:
+ g_value_set_string (value, priv->filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_cookie_jar_sqlite_new:
+ * @filename: the filename to read to/write from, or %NULL
+ * @read_only: %TRUE if @filename is read-only
+ *
+ * Creates a #SoupCookieJarSqlite.
+ *
+ * @filename will be read in at startup to create an initial set of
+ * cookies. If @read_only is %FALSE, then the non-session cookies will
+ * be written to @filename when the 'changed' signal is emitted from
+ * the jar. (If @read_only is %TRUE, then the cookie jar will only be
+ * used for this session, and changes made to it will be lost when the
+ * jar is destroyed.)
+ *
+ * Return value: the new #SoupCookieJar
+ *
+ * Since: 2.26
+ **/
+SoupCookieJar *
+soup_cookie_jar_sqlite_new (const char *filename, gboolean read_only)
+{
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ return g_object_new (SOUP_TYPE_COOKIE_JAR_SQLITE,
+ SOUP_COOKIE_JAR_SQLITE_FILENAME, filename,
+ SOUP_COOKIE_JAR_READ_ONLY, read_only,
+ NULL);
+}
+
+#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;"
+#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
+#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
+#define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;"
+
+enum {
+ COL_ID,
+ COL_NAME,
+ COL_VALUE,
+ COL_HOST,
+ COL_PATH,
+ COL_EXPIRY,
+ COL_LAST_ACCESS,
+ COL_SECURE,
+ COL_HTTP_ONLY,
+ N_COL,
+};
+
+static int
+callback (void *data, int argc, char **argv, char **colname)
+{
+ SoupCookie *cookie = NULL;
+ SoupCookieJar *jar = SOUP_COOKIE_JAR (data);
+
+ char *name, *value, *host, *path;
+ gulong expire_time;
+ time_t now;
+ int max_age;
+ gboolean http_only = FALSE, secure = FALSE;
+
+ now = time (NULL);
+
+ name = argv[COL_NAME];
+ value = argv[COL_VALUE];
+ host = argv[COL_HOST];
+ path = argv[COL_PATH];
+ expire_time = strtoul (argv[COL_EXPIRY], NULL, 10);
+
+ if (now >= expire_time)
+ return 0;
+ max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
+
+ http_only = (g_strcmp0 (argv[COL_HTTP_ONLY], "1") == 0);
+ secure = (g_strcmp0 (argv[COL_SECURE], "1") == 0);
+
+ cookie = soup_cookie_new (name, value, host, path, max_age);
+
+ if (secure)
+ soup_cookie_set_secure (cookie, TRUE);
+ if (http_only)
+ soup_cookie_set_http_only (cookie, TRUE);
+
+ soup_cookie_jar_add_cookie (jar, cookie);
+
+ return 0;
+}
+
+static void
+try_create_table (sqlite3 *db)
+{
+ char *error = NULL;
+
+ if (sqlite3_exec (db, CREATE_TABLE, NULL, NULL, &error)) {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+}
+
+static void
+exec_query_with_try_create_table (sqlite3 *db,
+ const char *sql,
+ int (*callback)(void*,int,char**,char**),
+ void *argument)
+{
+ char *error = NULL;
+ gboolean try_create = TRUE;
+
+try_exec:
+ if (sqlite3_exec (db, sql, callback, argument, &error)) {
+ if (try_create) {
+ try_create = FALSE;
+ try_create_table (db);
+ sqlite3_free (error);
+ error = NULL;
+ goto try_exec;
+ } else {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+ }
+}
+
+/* Follows sqlite3 convention; returns TRUE on error */
+static gboolean
+open_db (SoupCookieJar *jar)
+{
+ SoupCookieJarSqlitePrivate *priv =
+ SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
+
+ char *error = NULL;
+
+ if (sqlite3_open (priv->filename, &priv->db)) {
+ sqlite3_close (priv->db);
+ priv->db = NULL;
+ g_warning ("Can't open %s", priv->filename);
+ return TRUE;
+ }
+
+ if (sqlite3_exec (priv->db, "PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;", NULL, NULL, &error)) {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+
+ return FALSE;
+}
+
+static void
+load (SoupCookieJar *jar)
+{
+ SoupCookieJarSqlitePrivate *priv =
+ SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
+
+ if (priv->db == NULL) {
+ if (open_db (jar))
+ return;
+ }
+
+ exec_query_with_try_create_table (priv->db, QUERY_ALL, callback, jar);
+}
+
+static void
+changed (SoupCookieJar *jar,
+ SoupCookie *old_cookie,
+ SoupCookie *new_cookie)
+{
+ SoupCookieJarSqlitePrivate *priv =
+ SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
+ char *query;
+
+ if (priv->db == NULL) {
+ if (open_db (jar))
+ return;
+ }
+
+ if (old_cookie) {
+ query = sqlite3_mprintf (QUERY_DELETE,
+ old_cookie->name,
+ old_cookie->domain);
+ exec_query_with_try_create_table (priv->db, query, NULL, NULL);
+ sqlite3_free (query);
+ }
+
+ if (new_cookie && new_cookie->expires) {
+ gulong expires;
+
+ expires = (gulong)soup_date_to_time_t (new_cookie->expires);
+ query = sqlite3_mprintf (QUERY_INSERT,
+ new_cookie->name,
+ new_cookie->value,
+ new_cookie->domain,
+ new_cookie->path,
+ expires,
+ new_cookie->secure,
+ new_cookie->http_only);
+ exec_query_with_try_create_table (priv->db, query, NULL, NULL);
+ sqlite3_free (query);
+ }
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Diego Escalante Urrelo
+ */
+
+#ifndef SOUP_COOKIE_JAR_SQLITE_H
+#define SOUP_COOKIE_JAR_SQLITE_H 1
+
+#include <libsoup/soup-cookie-jar.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_COOKIE_JAR_SQLITE (soup_cookie_jar_sqlite_get_type ())
+#define SOUP_COOKIE_JAR_SQLITE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqlite))
+#define SOUP_COOKIE_JAR_SQLITE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqliteClass))
+#define SOUP_IS_COOKIE_JAR_SQLITE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE))
+#define SOUP_IS_COOKIE_JAR_SQLITE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE))
+#define SOUP_COOKIE_JAR_SQLITE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqliteClass))
+
+typedef struct {
+ SoupCookieJar parent;
+
+} SoupCookieJarSqlite;
+
+typedef struct {
+ SoupCookieJarClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupCookieJarSqliteClass;
+
+#define SOUP_COOKIE_JAR_SQLITE_FILENAME "filename"
+
+GType soup_cookie_jar_sqlite_get_type (void);
+
+SoupCookieJar *soup_cookie_jar_sqlite_new (const char *filename,
+ gboolean read_only);
+
+G_END_DECLS
+
+#endif /* SOUP_COOKIE_JAR_SQLITE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cookie-jar-text.c: cookies.txt-based cookie storage
+ *
+ * Copyright (C) 2007, 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-cookie-jar-text.h"
+#include "soup-cookie.h"
+#include "soup-date.h"
+
+/**
+ * SECTION:soup-cookie-jar-text
+ * @short_description: Text-file-based ("cookies.txt") Cookie Jar
+ *
+ * #SoupCookieJarText is a #SoupCookieJar that reads cookies from and
+ * writes them to a text file in the Mozilla "cookies.txt" format.
+ **/
+
+enum {
+ PROP_0,
+
+ PROP_FILENAME,
+
+ LAST_PROP
+};
+
+typedef struct {
+ char *filename;
+
+} SoupCookieJarTextPrivate;
+#define SOUP_COOKIE_JAR_TEXT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarTextPrivate))
+
+G_DEFINE_TYPE (SoupCookieJarText, soup_cookie_jar_text, SOUP_TYPE_COOKIE_JAR)
+
+static void load (SoupCookieJar *jar);
+static void changed (SoupCookieJar *jar,
+ SoupCookie *old_cookie,
+ SoupCookie *new_cookie);
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+soup_cookie_jar_text_init (SoupCookieJarText *text)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupCookieJarTextPrivate *priv =
+ SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
+
+ g_free (priv->filename);
+
+ G_OBJECT_CLASS (soup_cookie_jar_text_parent_class)->finalize (object);
+}
+
+static void
+soup_cookie_jar_text_class_init (SoupCookieJarTextClass *text_class)
+{
+ SoupCookieJarClass *cookie_jar_class =
+ SOUP_COOKIE_JAR_CLASS (text_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (text_class);
+
+ g_type_class_add_private (text_class, sizeof (SoupCookieJarTextPrivate));
+
+ cookie_jar_class->changed = changed;
+
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /**
+ * SOUP_COOKIE_JAR_TEXT_FILENAME:
+ *
+ * Alias for the #SoupCookieJarText:filename property. (The
+ * cookie-storage filename.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_FILENAME,
+ g_param_spec_string (SOUP_COOKIE_JAR_TEXT_FILENAME,
+ "Filename",
+ "Cookie-storage filename",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarTextPrivate *priv =
+ SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FILENAME:
+ priv->filename = g_value_dup_string (value);
+ load (SOUP_COOKIE_JAR (object));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarTextPrivate *priv =
+ SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FILENAME:
+ g_value_set_string (value, priv->filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_cookie_jar_text_new:
+ * @filename: the filename to read to/write from
+ * @read_only: %TRUE if @filename is read-only
+ *
+ * Creates a #SoupCookieJarText.
+ *
+ * @filename will be read in at startup to create an initial set of
+ * cookies. If @read_only is %FALSE, then the non-session cookies will
+ * be written to @filename when the 'changed' signal is emitted from
+ * the jar. (If @read_only is %TRUE, then the cookie jar will only be
+ * used for this session, and changes made to it will be lost when the
+ * jar is destroyed.)
+ *
+ * Return value: the new #SoupCookieJar
+ *
+ * Since: 2.26
+ **/
+SoupCookieJar *
+soup_cookie_jar_text_new (const char *filename, gboolean read_only)
+{
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ return g_object_new (SOUP_TYPE_COOKIE_JAR_TEXT,
+ SOUP_COOKIE_JAR_TEXT_FILENAME, filename,
+ SOUP_COOKIE_JAR_READ_ONLY, read_only,
+ NULL);
+}
+
+static SoupCookie*
+parse_cookie (char *line, time_t now)
+{
+ char **result;
+ SoupCookie *cookie = NULL;
+ gboolean http_only;
+ gulong expire_time;
+ int max_age;
+ char *host, *path, *secure, *expires, *name, *value;
+
+ if (g_str_has_prefix (line, "#HttpOnly_")) {
+ http_only = TRUE;
+ line += strlen ("#HttpOnly_");
+ } else if (*line == '#' || g_ascii_isspace (*line))
+ return cookie;
+ else
+ http_only = FALSE;
+
+ result = g_strsplit (line, "\t", -1);
+ if (g_strv_length (result) != 7)
+ goto out;
+
+ /* Check this first */
+ expires = result[4];
+ expire_time = strtoul (expires, NULL, 10);
+ if (now >= expire_time)
+ goto out;
+ max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
+
+ host = result[0];
+
+ /* result[1] is not used because it's redundat; it's a boolean
+ * value regarding whether the cookie should be used for
+ * sub-domains of the domain that is set for the cookie. It is
+ * TRUE if host starts with '.', and FALSE otherwise.
+ */
+
+ path = result[2];
+ secure = result[3];
+
+ name = result[5];
+ value = result[6];
+
+ cookie = soup_cookie_new (name, value, host, path, max_age);
+
+ if (strcmp (secure, "FALSE") != 0)
+ soup_cookie_set_secure (cookie, TRUE);
+ if (http_only)
+ soup_cookie_set_http_only (cookie, TRUE);
+
+ out:
+ g_strfreev (result);
+
+ return cookie;
+}
+
+static void
+parse_line (SoupCookieJar *jar, char *line, time_t now)
+{
+ SoupCookie *cookie;
+
+ cookie = parse_cookie (line, now);
+ if (cookie)
+ soup_cookie_jar_add_cookie (jar, cookie);
+}
+
+static void
+load (SoupCookieJar *jar)
+{
+ SoupCookieJarTextPrivate *priv =
+ SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
+ char *contents = NULL, *line, *p;
+ gsize length = 0;
+ time_t now = time (NULL);
+
+ /* FIXME: error? */
+ if (!g_file_get_contents (priv->filename, &contents, &length, NULL))
+ return;
+
+ line = contents;
+ for (p = contents; *p; p++) {
+ /* \r\n comes out as an extra empty line and gets ignored */
+ if (*p == '\r' || *p == '\n') {
+ *p = '\0';
+ parse_line (jar, line, now);
+ line = p + 1;
+ }
+ }
+ parse_line (jar, line, now);
+
+ g_free (contents);
+}
+
+static void
+write_cookie (FILE *out, SoupCookie *cookie)
+{
+ fseek (out, 0, SEEK_END);
+
+ fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
+ cookie->http_only ? "#HttpOnly_" : "",
+ cookie->domain,
+ *cookie->domain == '.' ? "TRUE" : "FALSE",
+ cookie->path,
+ cookie->secure ? "TRUE" : "FALSE",
+ (gulong)soup_date_to_time_t (cookie->expires),
+ cookie->name,
+ cookie->value);
+}
+
+static void
+delete_cookie (const char *filename, SoupCookie *cookie)
+{
+ char *contents = NULL, *line, *p;
+ gsize length = 0;
+ FILE *f;
+ SoupCookie *c;
+ time_t now = time (NULL);
+
+ if (!g_file_get_contents (filename, &contents, &length, NULL))
+ return;
+
+ f = fopen (filename, "w");
+ if (!f) {
+ g_free (contents);
+ return;
+ }
+
+ line = contents;
+ for (p = contents; *p; p++) {
+ /* \r\n comes out as an extra empty line and gets ignored */
+ if (*p == '\r' || *p == '\n') {
+ *p = '\0';
+ c = parse_cookie (line, now);
+ line = p + 1;
+ if (!c)
+ continue;
+ if (!soup_cookie_equal (cookie, c))
+ write_cookie (f, c);
+ soup_cookie_free (c);
+ }
+ }
+ c = parse_cookie (line, now);
+ if (c) {
+ if (!soup_cookie_equal (cookie, c))
+ write_cookie (f, c);
+ soup_cookie_free (c);
+ }
+
+ g_free (contents);
+ fclose (f);
+}
+
+static void
+changed (SoupCookieJar *jar,
+ SoupCookie *old_cookie,
+ SoupCookie *new_cookie)
+{
+ FILE *out;
+ SoupCookieJarTextPrivate *priv =
+ SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
+
+ /* We can sort of ignore the semantics of the 'changed'
+ * signal here and simply delete the old cookie if present
+ * and write the new cookie if present. That will do the
+ * right thing for all 'added', 'deleted' and 'modified'
+ * meanings.
+ */
+ /* Also, delete_cookie takes the filename and write_cookie
+ * a FILE pointer. Seems more convenient that way considering
+ * the implementations of the functions
+ */
+ if (old_cookie)
+ delete_cookie (priv->filename, old_cookie);
+
+ if (new_cookie) {
+ gboolean write_header = FALSE;
+
+ if (!g_file_test (priv->filename, G_FILE_TEST_EXISTS))
+ write_header = TRUE;
+
+ out = fopen (priv->filename, "a");
+ if (!out) {
+ /* FIXME: error? */
+ return;
+ }
+
+ if (write_header) {
+ fprintf (out, "# HTTP Cookie File\n");
+ fprintf (out, "# http://www.netscape.com/newsref/std/cookie_spec.html\n");
+ fprintf (out, "# This is a generated file! Do not edit.\n");
+ fprintf (out, "# To delete cookies, use the Cookie Manager.\n\n");
+ }
+
+ if (new_cookie->expires)
+ write_cookie (out, new_cookie);
+
+ if (fclose (out) != 0) {
+ /* FIXME: error? */
+ return;
+ }
+ }
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_COOKIE_JAR_TEXT_H
+#define SOUP_COOKIE_JAR_TEXT_H 1
+
+#include <libsoup/soup-cookie-jar.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_COOKIE_JAR_TEXT (soup_cookie_jar_text_get_type ())
+#define SOUP_COOKIE_JAR_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarText))
+#define SOUP_COOKIE_JAR_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarTextClass))
+#define SOUP_IS_COOKIE_JAR_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_TEXT))
+#define SOUP_IS_COOKIE_JAR_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_TEXT))
+#define SOUP_COOKIE_JAR_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarTextClass))
+
+typedef struct {
+ SoupCookieJar parent;
+
+} SoupCookieJarText;
+
+typedef struct {
+ SoupCookieJarClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupCookieJarTextClass;
+
+#define SOUP_COOKIE_JAR_TEXT_FILENAME "filename"
+
+GType soup_cookie_jar_text_get_type (void);
+
+SoupCookieJar *soup_cookie_jar_text_new (const char *filename,
+ gboolean read_only);
+
+G_END_DECLS
+
+#endif /* SOUP_COOKIE_JAR_TEXT_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cookie-jar.c
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "soup-cookie.h"
+#include "soup-cookie-jar.h"
+#include "soup-date.h"
+#include "soup-enum-types.h"
+#include "soup-marshal.h"
+#include "soup-message.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-cookie-jar
+ * @short_description: Automatic cookie handling for #SoupSession
+ *
+ * A #SoupCookieJar stores #SoupCookie<!-- -->s and arrange for them
+ * to be sent with the appropriate #SoupMessage<!-- -->s.
+ * #SoupCookieJar implements #SoupSessionFeature, so you can add a
+ * cookie jar to a session with soup_session_add_feature() or
+ * soup_session_add_feature_by_type().
+ *
+ * Note that the base #SoupCookieJar class does not support any form
+ * of long-term cookie persistence.
+ **/
+
+static void soup_cookie_jar_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static void request_queued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg);
+static void request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket);
+static void request_unqueued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg);
+
+G_DEFINE_TYPE_WITH_CODE (SoupCookieJar, soup_cookie_jar, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_cookie_jar_session_feature_init))
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+
+ PROP_READ_ONLY,
+ PROP_ACCEPT_POLICY,
+
+ LAST_PROP
+};
+
+typedef struct {
+ gboolean constructed, read_only;
+ GHashTable *domains, *serials;
+ guint serial;
+ SoupCookieJarAcceptPolicy accept_policy;
+} SoupCookieJarPrivate;
+#define SOUP_COOKIE_JAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR, SoupCookieJarPrivate))
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+soup_cookie_jar_init (SoupCookieJar *jar)
+{
+ SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+ priv->domains = g_hash_table_new_full (soup_str_case_hash,
+ soup_str_case_equal,
+ g_free, NULL);
+ priv->serials = g_hash_table_new (NULL, NULL);
+ priv->accept_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
+}
+
+static void
+constructed (GObject *object)
+{
+ SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (object);
+
+ priv->constructed = TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (object);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, priv->domains);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ soup_cookies_free (value);
+ g_hash_table_destroy (priv->domains);
+ g_hash_table_destroy (priv->serials);
+
+ G_OBJECT_CLASS (soup_cookie_jar_parent_class)->finalize (object);
+}
+
+static void
+soup_cookie_jar_class_init (SoupCookieJarClass *jar_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (jar_class);
+
+ g_type_class_add_private (jar_class, sizeof (SoupCookieJarPrivate));
+
+ object_class->constructed = constructed;
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /**
+ * SoupCookieJar::changed
+ * @jar: the #SoupCookieJar
+ * @old_cookie: the old #SoupCookie value
+ * @new_cookie: the new #SoupCookie value
+ *
+ * Emitted when @jar changes. If a cookie has been added,
+ * @new_cookie will contain the newly-added cookie and
+ * @old_cookie will be %NULL. If a cookie has been deleted,
+ * @old_cookie will contain the to-be-deleted cookie and
+ * @new_cookie will be %NULL. If a cookie has been changed,
+ * @old_cookie will contain its old value, and @new_cookie its
+ * new value.
+ **/
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupCookieJarClass, changed),
+ NULL, NULL,
+ soup_marshal_NONE__BOXED_BOXED,
+ G_TYPE_NONE, 2,
+ SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE,
+ SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ /**
+ * SOUP_COOKIE_JAR_READ_ONLY:
+ *
+ * Alias for the #SoupCookieJar:read-only property. (Whether
+ * or not the cookie jar is read-only.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_READ_ONLY,
+ g_param_spec_boolean (SOUP_COOKIE_JAR_READ_ONLY,
+ "Read-only",
+ "Whether or not the cookie jar is read-only",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * SOUP_COOKIE_JAR_ACCEPT_POLICY:
+ *
+ * Alias for the #SoupCookieJar:accept-policy property.
+ *
+ * Since: 2.30
+ */
+ /**
+ * SoupCookieJar:accept-policy:
+ *
+ * The policy the jar should follow to accept or reject cookies
+ *
+ * Since: 2.30
+ */
+ g_object_class_install_property (
+ object_class, PROP_ACCEPT_POLICY,
+ g_param_spec_enum (SOUP_COOKIE_JAR_ACCEPT_POLICY,
+ "Accept-policy",
+ "The policy the jar should follow to accept or reject cookies",
+ SOUP_TYPE_COOKIE_JAR_ACCEPT_POLICY,
+ SOUP_COOKIE_JAR_ACCEPT_ALWAYS,
+ G_PARAM_READWRITE));
+}
+
+static void
+soup_cookie_jar_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ feature_interface->request_queued = request_queued;
+ feature_interface->request_started = request_started;
+ feature_interface->request_unqueued = request_unqueued;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarPrivate *priv =
+ SOUP_COOKIE_JAR_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_READ_ONLY:
+ priv->read_only = g_value_get_boolean (value);
+ break;
+ case PROP_ACCEPT_POLICY:
+ priv->accept_policy = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarPrivate *priv =
+ SOUP_COOKIE_JAR_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_READ_ONLY:
+ g_value_set_boolean (value, priv->read_only);
+ break;
+ case PROP_ACCEPT_POLICY:
+ g_value_set_enum (value, priv->accept_policy);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_cookie_jar_new:
+ *
+ * Creates a new #SoupCookieJar. The base #SoupCookieJar class does
+ * not support persistent storage of cookies; use a subclass for that.
+ *
+ * Returns: a new #SoupCookieJar
+ *
+ * Since: 2.24
+ **/
+SoupCookieJar *
+soup_cookie_jar_new (void)
+{
+ return g_object_new (SOUP_TYPE_COOKIE_JAR, NULL);
+}
+
+void
+soup_cookie_jar_save (SoupCookieJar *jar)
+{
+ /* Does nothing, obsolete */
+}
+
+static void
+soup_cookie_jar_changed (SoupCookieJar *jar,
+ SoupCookie *old, SoupCookie *new)
+{
+ SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+ if (old && old != new)
+ g_hash_table_remove (priv->serials, old);
+ if (new) {
+ priv->serial++;
+ g_hash_table_insert (priv->serials, new, GUINT_TO_POINTER (priv->serial));
+ }
+
+ if (priv->read_only || !priv->constructed)
+ return;
+
+ g_signal_emit (jar, signals[CHANGED], 0, old, new);
+}
+
+static int
+compare_cookies (gconstpointer a, gconstpointer b, gpointer jar)
+{
+ SoupCookie *ca = (SoupCookie *)a;
+ SoupCookie *cb = (SoupCookie *)b;
+ SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+ int alen, blen;
+ guint aserial, bserial;
+
+ /* "Cookies with longer path fields are listed before cookies
+ * with shorter path field."
+ */
+ alen = ca->path ? strlen (ca->path) : 0;
+ blen = cb->path ? strlen (cb->path) : 0;
+ if (alen != blen)
+ return blen - alen;
+
+ /* "Among cookies that have equal length path fields, cookies
+ * with earlier creation dates are listed before cookies with
+ * later creation dates."
+ */
+ aserial = GPOINTER_TO_UINT (g_hash_table_lookup (priv->serials, ca));
+ bserial = GPOINTER_TO_UINT (g_hash_table_lookup (priv->serials, cb));
+ return aserial - bserial;
+}
+
+/**
+ * soup_cookie_jar_get_cookies:
+ * @jar: a #SoupCookieJar
+ * @uri: a #SoupURI
+ * @for_http: whether or not the return value is being passed directly
+ * to an HTTP operation
+ *
+ * Retrieves (in Cookie-header form) the list of cookies that would
+ * be sent with a request to @uri.
+ *
+ * If @for_http is %TRUE, the return value will include cookies marked
+ * "HttpOnly" (that is, cookies that the server wishes to keep hidden
+ * from client-side scripting operations such as the JavaScript
+ * document.cookies property). Since #SoupCookieJar sets the Cookie
+ * header itself when making the actual HTTP request, you should
+ * almost certainly be setting @for_http to %FALSE if you are calling
+ * this.
+ *
+ * Return value: the cookies, in string form, or %NULL if there are no
+ * cookies for @uri.
+ *
+ * Since: 2.24
+ **/
+char *
+soup_cookie_jar_get_cookies (SoupCookieJar *jar, SoupURI *uri,
+ gboolean for_http)
+{
+ SoupCookieJarPrivate *priv;
+ GSList *cookies, *domain_cookies;
+ char *domain, *cur, *next_domain, *result;
+ GSList *new_head, *cookies_to_remove = NULL, *p;
+
+ g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
+ priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ if (!uri->host)
+ return NULL;
+
+ /* The logic here is a little weird, but the plan is that if
+ * uri->host is "www.foo.com", we will end up looking up
+ * cookies for ".www.foo.com", "www.foo.com", ".foo.com", and
+ * ".com", in that order. (Logic stolen from Mozilla.)
+ */
+ cookies = NULL;
+ domain = cur = g_strdup_printf (".%s", uri->host);
+ next_domain = domain + 1;
+ do {
+ new_head = domain_cookies = g_hash_table_lookup (priv->domains, cur);
+ while (domain_cookies) {
+ GSList *next = domain_cookies->next;
+ SoupCookie *cookie = domain_cookies->data;
+
+ if (cookie->expires && soup_date_is_past (cookie->expires)) {
+ cookies_to_remove = g_slist_append (cookies_to_remove,
+ cookie);
+ new_head = g_slist_delete_link (new_head, domain_cookies);
+ g_hash_table_insert (priv->domains,
+ g_strdup (cur),
+ new_head);
+ } else if (soup_cookie_applies_to_uri (cookie, uri) &&
+ (for_http || !cookie->http_only))
+ cookies = g_slist_append (cookies, cookie);
+
+ domain_cookies = next;
+ }
+ cur = next_domain;
+ if (cur)
+ next_domain = strchr (cur + 1, '.');
+ } while (cur);
+ g_free (domain);
+
+ for (p = cookies_to_remove; p; p = p->next) {
+ SoupCookie *cookie = p->data;
+
+ soup_cookie_jar_changed (jar, cookie, NULL);
+ soup_cookie_free (cookie);
+ }
+ g_slist_free (cookies_to_remove);
+
+ if (cookies) {
+ cookies = g_slist_sort_with_data (cookies, compare_cookies, jar);
+ result = soup_cookies_to_cookie_header (cookies);
+ g_slist_free (cookies);
+
+ if (!*result) {
+ g_free (result);
+ result = NULL;
+ }
+ return result;
+ } else
+ return NULL;
+}
+
+/**
+ * soup_cookie_jar_add_cookie:
+ * @jar: a #SoupCookieJar
+ * @cookie: a #SoupCookie
+ *
+ * Adds @cookie to @jar, emitting the 'changed' signal if we are modifying
+ * an existing cookie or adding a valid new cookie ('valid' means
+ * that the cookie's expire date is not in the past).
+ *
+ * @cookie will be 'stolen' by the jar, so don't free it afterwards.
+ *
+ * Since: 2.26
+ **/
+void
+soup_cookie_jar_add_cookie (SoupCookieJar *jar, SoupCookie *cookie)
+{
+ SoupCookieJarPrivate *priv;
+ GSList *old_cookies, *oc, *last = NULL;
+ SoupCookie *old_cookie;
+
+ g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+ g_return_if_fail (cookie != NULL);
+
+ priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+ old_cookies = g_hash_table_lookup (priv->domains, cookie->domain);
+ for (oc = old_cookies; oc; oc = oc->next) {
+ old_cookie = oc->data;
+ if (!strcmp (cookie->name, old_cookie->name) &&
+ !g_strcmp0 (cookie->path, old_cookie->path)) {
+ if (cookie->expires && soup_date_is_past (cookie->expires)) {
+ /* The new cookie has an expired date,
+ * this is the way the the server has
+ * of telling us that we have to
+ * remove the cookie.
+ */
+ old_cookies = g_slist_delete_link (old_cookies, oc);
+ g_hash_table_insert (priv->domains,
+ g_strdup (cookie->domain),
+ old_cookies);
+ soup_cookie_jar_changed (jar, old_cookie, NULL);
+ soup_cookie_free (old_cookie);
+ soup_cookie_free (cookie);
+ } else {
+ oc->data = cookie;
+ soup_cookie_jar_changed (jar, old_cookie, cookie);
+ soup_cookie_free (old_cookie);
+ }
+
+ return;
+ }
+ last = oc;
+ }
+
+ /* The new cookie is... a new cookie */
+ if (cookie->expires && soup_date_is_past (cookie->expires)) {
+ soup_cookie_free (cookie);
+ return;
+ }
+
+ if (last)
+ last->next = g_slist_append (NULL, cookie);
+ else {
+ old_cookies = g_slist_append (NULL, cookie);
+ g_hash_table_insert (priv->domains, g_strdup (cookie->domain),
+ old_cookies);
+ }
+
+ soup_cookie_jar_changed (jar, NULL, cookie);
+}
+
+/**
+ * soup_cookie_jar_set_cookie:
+ * @jar: a #SoupCookieJar
+ * @uri: the URI setting the cookie
+ * @cookie: the stringified cookie to set
+ *
+ * Adds @cookie to @jar, exactly as though it had appeared in a
+ * Set-Cookie header returned from a request to @uri.
+ *
+ * Keep in mind that if the #SoupCookieJarAcceptPolicy
+ * %SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY is set you'll need to use
+ * soup_cookie_jar_set_cookie_with_first_party(), otherwise the jar
+ * will have no way of knowing if the cookie is being set by a third
+ * party or not.
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_jar_set_cookie (SoupCookieJar *jar, SoupURI *uri,
+ const char *cookie)
+{
+ SoupCookie *soup_cookie;
+ SoupCookieJarPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (cookie != NULL);
+
+ if (!uri->host)
+ return;
+
+ priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+ if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
+ return;
+
+ g_return_if_fail (priv->accept_policy != SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY);
+
+ soup_cookie = soup_cookie_parse (cookie, uri);
+ if (soup_cookie) {
+ /* will steal or free soup_cookie */
+ soup_cookie_jar_add_cookie (jar, soup_cookie);
+ }
+}
+
+/**
+ * soup_cookie_jar_set_cookie_with_first_party:
+ * @jar: a #SoupCookieJar
+ * @uri: the URI setting the cookie
+ * @first_party: the URI for the main document
+ * @cookie: the stringified cookie to set
+ *
+ * Adds @cookie to @jar, exactly as though it had appeared in a
+ * Set-Cookie header returned from a request to @uri. @first_party
+ * will be used to reject cookies coming from third party resources in
+ * case such a security policy is set in the @jar.
+ *
+ * Since: 2.30
+ **/
+void
+soup_cookie_jar_set_cookie_with_first_party (SoupCookieJar *jar,
+ SoupURI *uri,
+ SoupURI *first_party,
+ const char *cookie)
+{
+ SoupCookie *soup_cookie;
+ SoupCookieJarPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (first_party != NULL);
+ g_return_if_fail (cookie != NULL);
+
+ if (!uri->host)
+ return;
+
+ priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+ if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
+ return;
+
+ soup_cookie = soup_cookie_parse (cookie, uri);
+ if (soup_cookie) {
+ if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS ||
+ soup_cookie_domain_matches (soup_cookie, first_party->host)) {
+ /* will steal or free soup_cookie */
+ soup_cookie_jar_add_cookie (jar, soup_cookie);
+ } else {
+ soup_cookie_free (soup_cookie);
+ }
+ }
+}
+
+static void
+process_set_cookie_header (SoupMessage *msg, gpointer user_data)
+{
+ SoupCookieJar *jar = user_data;
+ SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+ GSList *new_cookies, *nc;
+
+ if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
+ return;
+
+ new_cookies = soup_cookies_from_response (msg);
+ for (nc = new_cookies; nc; nc = nc->next) {
+ SoupURI *first_party = soup_message_get_first_party (msg);
+
+ if ((priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY &&
+ first_party != NULL && first_party->host &&
+ soup_cookie_domain_matches (nc->data, first_party->host)) ||
+ priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS)
+ soup_cookie_jar_add_cookie (jar, nc->data);
+ else
+ soup_cookie_free (nc->data);
+ }
+ g_slist_free (new_cookies);
+}
+
+static void
+request_queued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg)
+{
+ soup_message_add_header_handler (msg, "got-headers",
+ "Set-Cookie",
+ G_CALLBACK (process_set_cookie_header),
+ feature);
+}
+
+static void
+request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket)
+{
+ SoupCookieJar *jar = SOUP_COOKIE_JAR (feature);
+ char *cookies;
+
+ cookies = soup_cookie_jar_get_cookies (jar, soup_message_get_uri (msg), TRUE);
+ if (cookies) {
+ soup_message_headers_replace (msg->request_headers,
+ "Cookie", cookies);
+ g_free (cookies);
+ } else
+ soup_message_headers_remove (msg->request_headers, "Cookie");
+}
+
+static void
+request_unqueued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg)
+{
+ g_signal_handlers_disconnect_by_func (msg, process_set_cookie_header, feature);
+}
+
+/**
+ * soup_cookie_jar_all_cookies:
+ * @jar: a #SoupCookieJar
+ *
+ * Constructs a #GSList with every cookie inside the @jar.
+ * The cookies in the list are a copy of the original, so
+ * you have to free them when you are done with them.
+ *
+ * Return value: (transfer full) (element-type Soup.Cookie): a #GSList
+ * with all the cookies in the @jar.
+ *
+ * Since: 2.26
+ **/
+GSList *
+soup_cookie_jar_all_cookies (SoupCookieJar *jar)
+{
+ SoupCookieJarPrivate *priv;
+ GHashTableIter iter;
+ GSList *l = NULL;
+ gpointer key, value;
+
+ g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
+
+ priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+ g_hash_table_iter_init (&iter, priv->domains);
+
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GSList *p, *cookies = value;
+ for (p = cookies; p; p = p->next)
+ l = g_slist_prepend (l, soup_cookie_copy (p->data));
+ }
+
+ return l;
+}
+
+/**
+ * soup_cookie_jar_delete_cookie:
+ * @jar: a #SoupCookieJar
+ * @cookie: a #SoupCookie
+ *
+ * Deletes @cookie from @jar, emitting the 'changed' signal.
+ *
+ * Since: 2.26
+ **/
+void
+soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
+ SoupCookie *cookie)
+{
+ SoupCookieJarPrivate *priv;
+ GSList *cookies, *p;
+ char *domain;
+
+ g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+ g_return_if_fail (cookie != NULL);
+
+ priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+ domain = g_strdup (cookie->domain);
+
+ cookies = g_hash_table_lookup (priv->domains, domain);
+ if (cookies == NULL)
+ return;
+
+ for (p = cookies; p; p = p->next ) {
+ SoupCookie *c = (SoupCookie*)p->data;
+ if (soup_cookie_equal (cookie, c)) {
+ cookies = g_slist_delete_link (cookies, p);
+ g_hash_table_insert (priv->domains,
+ domain,
+ cookies);
+ soup_cookie_jar_changed (jar, c, NULL);
+ soup_cookie_free (c);
+ return;
+ }
+ }
+}
+
+/**
+ * SoupCookieJarAcceptPolicy:
+ * @SOUP_COOKIE_JAR_ACCEPT_ALWAYS: accept all cookies unconditionally.
+ * @SOUP_COOKIE_JAR_ACCEPT_NEVER: reject all cookies unconditionally.
+ * @SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY: accept all cookies set by
+ * the main document loaded in the application using libsoup. An
+ * example of the most common case, web browsers, would be: If
+ * http://www.example.com is the page loaded, accept all cookies set
+ * by example.com, but if a resource from http://www.third-party.com
+ * is loaded from that page reject any cookie that it could try to
+ * set. For libsoup to be able to tell apart first party cookies from
+ * the rest, the application must call soup_message_set_first_party()
+ * on each outgoing #SoupMessage, setting the #SoupURI of the main
+ * document. If no first party is set in a message when this policy is
+ * in effect, cookies will be assumed to be third party by default.
+ *
+ * Since: 2.30
+ */
+
+/**
+ * soup_cookie_jar_get_accept_policy:
+ * @jar: a #SoupCookieJar
+ *
+ * Returns: the #SoupCookieJarAcceptPolicy set in the @jar
+ *
+ * Since: 2.30
+ **/
+SoupCookieJarAcceptPolicy
+soup_cookie_jar_get_accept_policy (SoupCookieJar *jar)
+{
+ SoupCookieJarPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), SOUP_COOKIE_JAR_ACCEPT_ALWAYS);
+
+ priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+ return priv->accept_policy;
+}
+
+/**
+ * soup_cookie_jar_set_accept_policy:
+ * @jar: a #SoupCookieJar
+ * @policy: a #SoupCookieJarAcceptPolicy
+ *
+ * Sets @policy as the cookie acceptance policy for @jar.
+ *
+ * Since: 2.30
+ **/
+void
+soup_cookie_jar_set_accept_policy (SoupCookieJar *jar,
+ SoupCookieJarAcceptPolicy policy)
+{
+ SoupCookieJarPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+
+ priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+ if (priv->accept_policy != policy) {
+ priv->accept_policy = policy;
+ g_object_notify (G_OBJECT (jar), SOUP_COOKIE_JAR_ACCEPT_POLICY);
+ }
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_COOKIE_JAR_H
+#define SOUP_COOKIE_JAR_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_COOKIE_JAR (soup_cookie_jar_get_type ())
+#define SOUP_COOKIE_JAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_COOKIE_JAR, SoupCookieJar))
+#define SOUP_COOKIE_JAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_COOKIE_JAR, SoupCookieJarClass))
+#define SOUP_IS_COOKIE_JAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_COOKIE_JAR))
+#define SOUP_IS_COOKIE_JAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_COOKIE_JAR))
+#define SOUP_COOKIE_JAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_COOKIE_JAR, SoupCookieJarClass))
+
+struct _SoupCookieJar {
+ GObject parent;
+
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ void (*save) (SoupCookieJar *jar);
+
+ /* signals */
+ void (*changed) (SoupCookieJar *jar,
+ SoupCookie *old_cookie,
+ SoupCookie *new_cookie);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+} SoupCookieJarClass;
+
+#define SOUP_COOKIE_JAR_READ_ONLY "read-only"
+#define SOUP_COOKIE_JAR_ACCEPT_POLICY "accept-policy"
+
+typedef enum {
+ SOUP_COOKIE_JAR_ACCEPT_ALWAYS,
+ SOUP_COOKIE_JAR_ACCEPT_NEVER,
+ SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
+} SoupCookieJarAcceptPolicy;
+
+GType soup_cookie_jar_get_type (void);
+SoupCookieJar * soup_cookie_jar_new (void);
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+void soup_cookie_jar_save (SoupCookieJar *jar);
+#endif
+char * soup_cookie_jar_get_cookies (SoupCookieJar *jar,
+ SoupURI *uri,
+ gboolean for_http);
+void soup_cookie_jar_set_cookie (SoupCookieJar *jar,
+ SoupURI *uri,
+ const char *cookie);
+void soup_cookie_jar_set_cookie_with_first_party (SoupCookieJar *jar,
+ SoupURI *uri,
+ SoupURI *first_party,
+ const char *cookie);
+void soup_cookie_jar_add_cookie (SoupCookieJar *jar,
+ SoupCookie *cookie);
+void soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
+ SoupCookie *cookie);
+GSList * soup_cookie_jar_all_cookies (SoupCookieJar *jar);
+void soup_cookie_jar_set_accept_policy (SoupCookieJar *jar,
+ SoupCookieJarAcceptPolicy policy);
+SoupCookieJarAcceptPolicy soup_cookie_jar_get_accept_policy (SoupCookieJar *jar);
+
+G_END_DECLS
+
+#endif /* SOUP_COOKIE_JAR_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cookie.c
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-cookie.h"
+#include "soup-date.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-message-headers.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-cookie
+ * @short_description: HTTP Cookies
+ * @see_also: #SoupMessage
+ *
+ * #SoupCookie implements HTTP cookies, primarily as described by
+ * <ulink
+ * url="http://wp.netscape.com/newsref/std/cookie_spec.html">the
+ * original Netscape cookie specification</ulink>, but with slight
+ * modifications based on <ulink
+ * url="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</ulink>, <ulink
+ * url="http://msdn2.microsoft.com/en-us/library/ms533046.aspx">Microsoft's
+ * HttpOnly extension attribute</ulink>, and observed real-world usage
+ * (and, in particular, based on what Firefox does).
+ *
+ * To have a #SoupSession handle cookies for your appliction
+ * automatically, use a #SoupCookieJar.
+ **/
+
+/**
+ * SoupCookie:
+ * @name: the cookie name
+ * @value: the cookie value
+ * @domain: the "domain" attribute, or else the hostname that the
+ * cookie came from.
+ * @path: the "path" attribute, or %NULL
+ * @expires: the cookie expiration time, or %NULL for a session cookie
+ * @secure: %TRUE if the cookie should only be tranferred over SSL
+ * @http_only: %TRUE if the cookie should not be exposed to scripts
+ *
+ * An HTTP cookie.
+ *
+ * @name and @value will be set for all cookies. If the cookie is
+ * generated from a string that appears to have no name, then @name
+ * will be the empty string.
+ *
+ * @domain and @path give the host or domain, and path within that
+ * host/domain, to restrict this cookie to. If @domain starts with
+ * ".", that indicates a domain (which matches the string after the
+ * ".", or any hostname that has @domain as a suffix). Otherwise, it
+ * is a hostname and must match exactly.
+ *
+ * @expires will be non-%NULL if the cookie uses either the original
+ * "expires" attribute, or the "max-age" attribute specified in RFC
+ * 2109. If @expires is %NULL, it indicates that neither "expires" nor
+ * "max-age" was specified, and the cookie expires at the end of the
+ * session.
+ *
+ * If @http_only is set, the cookie should not be exposed to untrusted
+ * code (eg, javascript), so as to minimize the danger posed by
+ * cross-site scripting attacks.
+ *
+ * Since: 2.24
+ **/
+
+GType
+soup_cookie_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupCookie"),
+ (GBoxedCopyFunc) soup_cookie_copy,
+ (GBoxedFreeFunc) soup_cookie_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
+
+/**
+ * soup_cookie_copy:
+ * @cookie: a #SoupCookie
+ *
+ * Copies @cookie.
+ *
+ * Return value: a copy of @cookie
+ *
+ * Since: 2.24
+ **/
+SoupCookie *
+soup_cookie_copy (SoupCookie *cookie)
+{
+ SoupCookie *copy = g_slice_new0 (SoupCookie);
+
+ copy->name = g_strdup (cookie->name);
+ copy->value = g_strdup (cookie->value);
+ copy->domain = g_strdup (cookie->domain);
+ copy->path = g_strdup (cookie->path);
+ if (cookie->expires)
+ copy->expires = soup_date_copy(cookie->expires);
+ copy->secure = cookie->secure;
+ copy->http_only = cookie->http_only;
+
+ return copy;
+}
+
+/**
+ * soup_cookie_domain_matches:
+ * @cookie: a #SoupCookie
+ * @host: a URI
+ *
+ * Checks if the @cookie's domain and @host match in the sense that
+ * @cookie should be sent when making a request to @host, or that
+ * @cookie should be accepted when receiving a response from @host.
+ *
+ * Return value: %TRUE if the domains match, %FALSE otherwise
+ *
+ * Since: 2.30
+ **/
+gboolean
+soup_cookie_domain_matches (SoupCookie *cookie, const char *host)
+{
+ char *match;
+ int dlen;
+ const char *domain;
+
+ g_return_val_if_fail (cookie != NULL, FALSE);
+ g_return_val_if_fail (host != NULL, FALSE);
+
+ domain = cookie->domain;
+
+ if (!g_ascii_strcasecmp (domain, host))
+ return TRUE;
+ if (*domain != '.')
+ return FALSE;
+ if (!g_ascii_strcasecmp (domain + 1, host))
+ return TRUE;
+ dlen = strlen (domain);
+ while ((match = strstr (host, domain))) {
+ if (!match[dlen])
+ return TRUE;
+ host = match + 1;
+ }
+ return FALSE;
+}
+
+static inline const char *
+skip_lws (const char *s)
+{
+ while (g_ascii_isspace (*s))
+ s++;
+ return s;
+}
+
+static inline const char *
+unskip_lws (const char *s, const char *start)
+{
+ while (s > start && g_ascii_isspace (*(s - 1)))
+ s--;
+ return s;
+}
+
+#define is_attr_ender(ch) ((ch) < ' ' || (ch) == ';' || (ch) == ',' || (ch) == '=')
+#define is_value_ender(ch) ((ch) < ' ' || (ch) == ';')
+
+static char *
+parse_value (const char **val_p)
+{
+ const char *start, *end, *p;
+ char *value;
+
+ p = *val_p;
+ if (*p == '=')
+ p++;
+ start = skip_lws (p);
+ for (p = start; !is_value_ender (*p); p++)
+ ;
+ end = unskip_lws (p, start);
+ value = g_strndup (start, end - start);
+
+ *val_p = p;
+ return value;
+}
+
+static SoupDate *
+parse_date (const char **val_p)
+{
+ char *value;
+ SoupDate *date;
+
+ value = parse_value (val_p);
+ date = soup_date_new_from_string (value);
+ g_free (value);
+ return date;
+}
+
+static SoupCookie *
+parse_one_cookie (const char *header, SoupURI *origin)
+{
+ const char *start, *end, *p;
+ gboolean has_value;
+ SoupCookie *cookie;
+
+ g_return_val_if_fail (origin == NULL || origin->host, NULL);
+
+ cookie = g_slice_new0 (SoupCookie);
+
+ /* Parse the NAME */
+ start = skip_lws (header);
+ for (p = start; !is_attr_ender (*p); p++)
+ ;
+ if (*p == '=') {
+ end = unskip_lws (p, start);
+ cookie->name = g_strndup (start, end - start);
+ } else {
+ /* No NAME; Set cookie->name to "" and then rewind to
+ * re-parse the string as a VALUE.
+ */
+ cookie->name = g_strdup ("");
+ p = start;
+ }
+
+ /* Parse the VALUE */
+ cookie->value = parse_value (&p);
+
+ /* Parse attributes */
+ while (*p == ';') {
+ start = skip_lws (p + 1);
+ for (p = start; !is_attr_ender (*p); p++)
+ ;
+ end = unskip_lws (p, start);
+
+ has_value = (*p == '=');
+#define MATCH_NAME(name) ((end - start == strlen (name)) && !g_ascii_strncasecmp (start, name, end - start))
+
+ if (MATCH_NAME ("domain") && has_value) {
+ cookie->domain = parse_value (&p);
+ if (!*cookie->domain) {
+ g_free (cookie->domain);
+ cookie->domain = NULL;
+ }
+ } else if (MATCH_NAME ("expires") && has_value) {
+ cookie->expires = parse_date (&p);
+ } else if (MATCH_NAME ("httponly")) {
+ cookie->http_only = TRUE;
+ } else if (MATCH_NAME ("max-age") && has_value) {
+ char *max_age_str = parse_value (&p), *mae;
+ long max_age = strtol (max_age_str, &mae, 10);
+ if (!*mae) {
+ if (max_age < 0)
+ max_age = 0;
+ soup_cookie_set_max_age (cookie, max_age);
+ }
+ g_free (max_age_str);
+ } else if (MATCH_NAME ("path") && has_value) {
+ cookie->path = parse_value (&p);
+ if (*cookie->path != '/') {
+ g_free (cookie->path);
+ cookie->path = NULL;
+ }
+ } else if (MATCH_NAME ("secure")) {
+ cookie->secure = TRUE;
+ } else {
+ /* Ignore unknown attributes, but we still have
+ * to skip over the value.
+ */
+ if (has_value)
+ g_free (parse_value (&p));
+ }
+ }
+
+ if (cookie->domain) {
+ /* Domain must have at least one '.' (not counting an
+ * initial one. (We check this now, rather than
+ * bailing out sooner, because we don't want to force
+ * any cookies after this one in the Set-Cookie header
+ * to be discarded.)
+ */
+ if (!strchr (cookie->domain + 1, '.')) {
+ soup_cookie_free (cookie);
+ return NULL;
+ }
+
+ /* If the domain string isn't an IP addr, and doesn't
+ * start with a '.', prepend one.
+ */
+ if (!g_hostname_is_ip_address (cookie->domain) &&
+ cookie->domain[0] != '.') {
+ char *tmp = g_strdup_printf (".%s", cookie->domain);
+ g_free (cookie->domain);
+ cookie->domain = tmp;
+ }
+ }
+
+ if (origin) {
+ /* Sanity-check domain */
+ if (cookie->domain) {
+ if (!soup_cookie_domain_matches (cookie, origin->host)) {
+ soup_cookie_free (cookie);
+ return NULL;
+ }
+ } else
+ cookie->domain = g_strdup (origin->host);
+
+ /* The original cookie spec didn't say that pages
+ * could only set cookies for paths they were under.
+ * RFC 2109 adds that requirement, but some sites
+ * depend on the old behavior
+ * (https://bugzilla.mozilla.org/show_bug.cgi?id=156725#c20).
+ * So we don't check the path.
+ */
+
+ if (!cookie->path) {
+ char *slash;
+
+ slash = strrchr (origin->path, '/');
+ if (!slash || slash == origin->path)
+ cookie->path = g_strdup ("/");
+ else {
+ cookie->path = g_strndup (origin->path,
+ slash - origin->path);
+ }
+ }
+ }
+
+ return cookie;
+}
+
+static SoupCookie *
+cookie_new_internal (const char *name, const char *value,
+ const char *domain, const char *path,
+ int max_age)
+{
+ SoupCookie *cookie;
+
+ cookie = g_slice_new0 (SoupCookie);
+ cookie->name = g_strdup (name);
+ cookie->value = g_strdup (value);
+ cookie->domain = g_strdup (domain);
+ cookie->path = g_strdup (path);
+ soup_cookie_set_max_age (cookie, max_age);
+
+ return cookie;
+}
+
+/**
+ * soup_cookie_new:
+ * @name: cookie name
+ * @value: cookie value
+ * @domain: cookie domain or hostname
+ * @path: cookie path, or %NULL
+ * @max_age: max age of the cookie, or -1 for a session cookie
+ *
+ * Creates a new #SoupCookie with the given attributes. (Use
+ * soup_cookie_set_secure() and soup_cookie_set_http_only() if you
+ * need to set those attributes on the returned cookie.)
+ *
+ * @max_age is used to set the "expires" attribute on the cookie; pass
+ * -1 to not include the attribute (indicating that the cookie expires
+ * with the current session), 0 for an already-expired cookie, or a
+ * lifetime in seconds. You can use the constants
+ * %SOUP_COOKIE_MAX_AGE_ONE_HOUR, %SOUP_COOKIE_MAX_AGE_ONE_DAY,
+ * %SOUP_COOKIE_MAX_AGE_ONE_WEEK and %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or
+ * multiples thereof) to calculate this value. (If you really care
+ * about setting the exact time that the cookie will expire, use
+ * soup_cookie_set_expires().)
+ *
+ * Return value: a new #SoupCookie.
+ *
+ * Since: 2.24
+ **/
+SoupCookie *
+soup_cookie_new (const char *name, const char *value,
+ const char *domain, const char *path,
+ int max_age)
+{
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (value != NULL, NULL);
+
+ /* We ought to return if domain is NULL too, but this used to
+ * do be incorrectly documented as legal, and it wouldn't
+ * break anything as long as you called
+ * soup_cookie_set_domain() immediately after. So we warn but
+ * don't return, to discourage that behavior but not actually
+ * break anyone doing it.
+ */
+ g_warn_if_fail (domain != NULL);
+
+ return cookie_new_internal (name, value, domain, path, max_age);
+}
+
+/**
+ * soup_cookie_parse:
+ * @header: a cookie string (eg, the value of a Set-Cookie header)
+ * @origin: origin of the cookie, or %NULL
+ *
+ * Parses @header and returns a #SoupCookie. (If @header contains
+ * multiple cookies, only the first one will be parsed.)
+ *
+ * If @header does not have "path" or "domain" attributes, they will
+ * be defaulted from @origin. If @origin is %NULL, path will default
+ * to "/", but domain will be left as %NULL. Note that this is not a
+ * valid state for a #SoupCookie, and you will need to fill in some
+ * appropriate string for the domain if you want to actually make use
+ * of the cookie.
+ *
+ * Return value: a new #SoupCookie, or %NULL if it could not be
+ * parsed, or contained an illegal "domain" attribute for a cookie
+ * originating from @origin.
+ *
+ * Since: 2.24
+ **/
+SoupCookie *
+soup_cookie_parse (const char *cookie, SoupURI *origin)
+{
+ return parse_one_cookie (cookie, origin);
+}
+
+/**
+ * soup_cookie_get_name:
+ * @cookie: a #SoupCookie
+ *
+ * Gets @cookie's name
+ *
+ * Return value: @cookie's name
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_cookie_get_name (SoupCookie *cookie)
+{
+ return cookie->name;
+}
+
+/**
+ * soup_cookie_set_name:
+ * @cookie: a #SoupCookie
+ * @name: the new name
+ *
+ * Sets @cookie's name to @name
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_set_name (SoupCookie *cookie, const char *name)
+{
+ g_free (cookie->name);
+ cookie->name = g_strdup (name);
+}
+
+/**
+ * soup_cookie_get_value:
+ * @cookie: a #SoupCookie
+ *
+ * Gets @cookie's value
+ *
+ * Return value: @cookie's value
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_cookie_get_value (SoupCookie *cookie)
+{
+ return cookie->value;
+}
+
+/**
+ * soup_cookie_set_value:
+ * @cookie: a #SoupCookie
+ * @value: the new value
+ *
+ * Sets @cookie's value to @value
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_set_value (SoupCookie *cookie, const char *value)
+{
+ g_free (cookie->value);
+ cookie->value = g_strdup (value);
+}
+
+/**
+ * soup_cookie_get_domain:
+ * @cookie: a #SoupCookie
+ *
+ * Gets @cookie's domain
+ *
+ * Return value: @cookie's domain
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_cookie_get_domain (SoupCookie *cookie)
+{
+ return cookie->domain;
+}
+
+/**
+ * soup_cookie_set_domain:
+ * @cookie: a #SoupCookie
+ * @domain: the new domain
+ *
+ * Sets @cookie's domain to @domain
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_set_domain (SoupCookie *cookie, const char *domain)
+{
+ g_free (cookie->domain);
+ cookie->domain = g_strdup (domain);
+}
+
+/**
+ * soup_cookie_get_path:
+ * @cookie: a #SoupCookie
+ *
+ * Gets @cookie's path
+ *
+ * Return value: @cookie's path
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_cookie_get_path (SoupCookie *cookie)
+{
+ return cookie->path;
+}
+
+/**
+ * soup_cookie_set_path:
+ * @cookie: a #SoupCookie
+ * @path: the new path
+ *
+ * Sets @cookie's path to @path
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_set_path (SoupCookie *cookie, const char *path)
+{
+ g_free (cookie->path);
+ cookie->path = g_strdup (path);
+}
+
+/**
+ * soup_cookie_set_max_age:
+ * @cookie: a #SoupCookie
+ * @max_age: the new max age
+ *
+ * Sets @cookie's max age to @max_age. If @max_age is -1, the cookie
+ * is a session cookie, and will expire at the end of the client's
+ * session. Otherwise, it is the number of seconds until the cookie
+ * expires. You can use the constants %SOUP_COOKIE_MAX_AGE_ONE_HOUR,
+ * %SOUP_COOKIE_MAX_AGE_ONE_DAY, %SOUP_COOKIE_MAX_AGE_ONE_WEEK and
+ * %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or multiples thereof) to calculate
+ * this value. (A value of 0 indicates that the cookie should be
+ * considered already-expired.)
+ *
+ * (This sets the same property as soup_cookie_set_expires().)
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_set_max_age (SoupCookie *cookie, int max_age)
+{
+ if (cookie->expires)
+ soup_date_free (cookie->expires);
+
+ if (max_age == -1)
+ cookie->expires = NULL;
+ else if (max_age == 0) {
+ /* Use a date way in the past, to protect against
+ * clock skew.
+ */
+ cookie->expires = soup_date_new (1970, 1, 1, 0, 0, 0);
+ } else
+ cookie->expires = soup_date_new_from_now (max_age);
+}
+
+/**
+ * SOUP_COOKIE_MAX_AGE_ONE_HOUR:
+ *
+ * A constant corresponding to 1 hour, for use with soup_cookie_new()
+ * and soup_cookie_set_max_age().
+ *
+ * Since: 2.24
+ **/
+/**
+ * SOUP_COOKIE_MAX_AGE_ONE_DAY:
+ *
+ * A constant corresponding to 1 day, for use with soup_cookie_new()
+ * and soup_cookie_set_max_age().
+ *
+ * Since: 2.24
+ **/
+/**
+ * SOUP_COOKIE_MAX_AGE_ONE_WEEK:
+ *
+ * A constant corresponding to 1 week, for use with soup_cookie_new()
+ * and soup_cookie_set_max_age().
+ *
+ * Since: 2.24
+ **/
+/**
+ * SOUP_COOKIE_MAX_AGE_ONE_YEAR:
+ *
+ * A constant corresponding to 1 year, for use with soup_cookie_new()
+ * and soup_cookie_set_max_age().
+ *
+ * Since: 2.24
+ **/
+
+/**
+ * soup_cookie_get_expires:
+ * @cookie: a #SoupCookie
+ *
+ * Gets @cookie's expiration time
+ *
+ * Return value: (transfer none): @cookie's expiration time, which is
+ * owned by @cookie and should not be modified or freed.
+ *
+ * Since: 2.32
+ **/
+SoupDate *
+soup_cookie_get_expires (SoupCookie *cookie)
+{
+ return cookie->expires;
+}
+
+/**
+ * soup_cookie_set_expires:
+ * @cookie: a #SoupCookie
+ * @expires: the new expiration time, or %NULL
+ *
+ * Sets @cookie's expiration time to @expires. If @expires is %NULL,
+ * @cookie will be a session cookie and will expire at the end of the
+ * client's session.
+ *
+ * (This sets the same property as soup_cookie_set_max_age().)
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_set_expires (SoupCookie *cookie, SoupDate *expires)
+{
+ if (cookie->expires)
+ soup_date_free (cookie->expires);
+
+ if (expires)
+ cookie->expires = soup_date_copy (expires);
+ else
+ cookie->expires = NULL;
+}
+
+/**
+ * soup_cookie_get_secure:
+ * @cookie: a #SoupCookie
+ *
+ * Gets @cookie's secure attribute
+ *
+ * Return value: @cookie's secure attribute
+ *
+ * Since: 2.32
+ **/
+gboolean
+soup_cookie_get_secure (SoupCookie *cookie)
+{
+ return cookie->secure;
+}
+
+/**
+ * soup_cookie_set_secure:
+ * @cookie: a #SoupCookie
+ * @secure: the new value for the secure attribute
+ *
+ * Sets @cookie's secure attribute to @secure. If %TRUE, @cookie will
+ * only be transmitted from the client to the server over secure
+ * (https) connections.
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_set_secure (SoupCookie *cookie, gboolean secure)
+{
+ cookie->secure = secure;
+}
+
+/**
+ * soup_cookie_get_http_only:
+ * @cookie: a #SoupCookie
+ *
+ * Gets @cookie's HttpOnly attribute
+ *
+ * Return value: @cookie's HttpOnly attribute
+ *
+ * Since: 2.32
+ **/
+gboolean
+soup_cookie_get_http_only (SoupCookie *cookie)
+{
+ return cookie->http_only;
+}
+
+/**
+ * soup_cookie_set_http_only:
+ * @cookie: a #SoupCookie
+ * @http_only: the new value for the HttpOnly attribute
+ *
+ * Sets @cookie's HttpOnly attribute to @http_only. If %TRUE, @cookie
+ * will be marked as "http only", meaning it should not be exposed to
+ * web page scripts or other untrusted code.
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_set_http_only (SoupCookie *cookie, gboolean http_only)
+{
+ cookie->http_only = http_only;
+}
+
+static void
+serialize_cookie (SoupCookie *cookie, GString *header, gboolean set_cookie)
+{
+ if (!*cookie->name && !*cookie->value)
+ return;
+
+ if (header->len) {
+ if (set_cookie)
+ g_string_append (header, ", ");
+ else
+ g_string_append (header, "; ");
+ }
+
+ if (set_cookie || *cookie->name) {
+ g_string_append (header, cookie->name);
+ g_string_append (header, "=");
+ }
+ g_string_append (header, cookie->value);
+ if (!set_cookie)
+ return;
+
+ if (cookie->expires) {
+ char *timestamp;
+
+ g_string_append (header, "; expires=");
+ timestamp = soup_date_to_string (cookie->expires,
+ SOUP_DATE_COOKIE);
+ g_string_append (header, timestamp);
+ g_free (timestamp);
+ }
+ if (cookie->path) {
+ g_string_append (header, "; path=");
+ g_string_append (header, cookie->path);
+ }
+ if (cookie->domain) {
+ g_string_append (header, "; domain=");
+ g_string_append (header, cookie->domain);
+ }
+ if (cookie->secure)
+ g_string_append (header, "; secure");
+ if (cookie->secure)
+ g_string_append (header, "; HttpOnly");
+}
+
+/**
+ * soup_cookie_to_set_cookie_header:
+ * @cookie: a #SoupCookie
+ *
+ * Serializes @cookie in the format used by the Set-Cookie header
+ * (ie, for sending a cookie from a #SoupServer to a client).
+ *
+ * Return value: the header
+ *
+ * Since: 2.24
+ **/
+char *
+soup_cookie_to_set_cookie_header (SoupCookie *cookie)
+{
+ GString *header = g_string_new (NULL);
+
+ serialize_cookie (cookie, header, TRUE);
+ return g_string_free (header, FALSE);
+}
+
+/**
+ * soup_cookie_to_cookie_header:
+ * @cookie: a #SoupCookie
+ *
+ * Serializes @cookie in the format used by the Cookie header (ie, for
+ * returning a cookie from a #SoupSession to a server).
+ *
+ * Return value: the header
+ *
+ * Since: 2.24
+ **/
+char *
+soup_cookie_to_cookie_header (SoupCookie *cookie)
+{
+ GString *header = g_string_new (NULL);
+
+ serialize_cookie (cookie, header, FALSE);
+ return g_string_free (header, FALSE);
+}
+
+/**
+ * soup_cookie_free:
+ * @cookie: a #SoupCookie
+ *
+ * Frees @cookie
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookie_free (SoupCookie *cookie)
+{
+ g_return_if_fail (cookie != NULL);
+
+ g_free (cookie->name);
+ g_free (cookie->value);
+ g_free (cookie->domain);
+ g_free (cookie->path);
+
+ if (cookie->expires)
+ soup_date_free (cookie->expires);
+
+ g_slice_free (SoupCookie, cookie);
+}
+
+/**
+ * soup_cookies_from_response:
+ * @msg: a #SoupMessage containing a "Set-Cookie" response header
+ *
+ * Parses @msg's Set-Cookie response headers and returns a #GSList of
+ * #SoupCookie<!-- -->s. Cookies that do not specify "path" or
+ * "domain" attributes will have their values defaulted from @msg.
+ *
+ * Return value: (element-type SoupCookie) (transfer full): a #GSList
+ * of #SoupCookie<!-- -->s, which can be freed with
+ * soup_cookies_free().
+ *
+ * Since: 2.24
+ **/
+GSList *
+soup_cookies_from_response (SoupMessage *msg)
+{
+ SoupURI *origin;
+ const char *name, *value;
+ SoupCookie *cookie;
+ GSList *cookies = NULL;
+ SoupMessageHeadersIter iter;
+
+ origin = soup_message_get_uri (msg);
+
+ /* We have to use soup_message_headers_iter rather than
+ * soup_message_headers_get_list() since Set-Cookie isn't
+ * properly mergeable/unmergeable.
+ */
+ soup_message_headers_iter_init (&iter, msg->response_headers);
+ while (soup_message_headers_iter_next (&iter, &name, &value)) {
+ if (g_ascii_strcasecmp (name, "Set-Cookie") != 0)
+ continue;
+
+ cookie = parse_one_cookie (value, origin);
+ if (cookie)
+ cookies = g_slist_prepend (cookies, cookie);
+ }
+ return g_slist_reverse (cookies);
+}
+
+/**
+ * soup_cookies_from_request:
+ * @msg: a #SoupMessage containing a "Cookie" request header
+ *
+ * Parses @msg's Cookie request header and returns a #GSList of
+ * #SoupCookie<!-- -->s. As the "Cookie" header, unlike "Set-Cookie",
+ * only contains cookie names and values, none of the other
+ * #SoupCookie fields will be filled in. (Thus, you can't generally
+ * pass a cookie returned from this method directly to
+ * soup_cookies_to_response().)
+ *
+ * Return value: (element-type SoupCookie) (transfer full): a #GSList
+ * of #SoupCookie<!-- -->s, which can be freed with
+ * soup_cookies_free().
+ *
+ * Since: 2.24
+ **/
+GSList *
+soup_cookies_from_request (SoupMessage *msg)
+{
+ SoupCookie *cookie;
+ GSList *cookies = NULL;
+ GHashTable *params;
+ GHashTableIter iter;
+ gpointer name, value;
+ const char *header;
+
+ header = soup_message_headers_get_one (msg->request_headers, "Cookie");
+ if (!header)
+ return NULL;
+
+ params = soup_header_parse_semi_param_list (header);
+ g_hash_table_iter_init (&iter, params);
+ while (g_hash_table_iter_next (&iter, &name, &value)) {
+ if (name && value) {
+ cookie = cookie_new_internal (name, value,
+ NULL, NULL, 0);
+ cookies = g_slist_prepend (cookies, cookie);
+ }
+ }
+ soup_header_free_param_list (params);
+
+ return g_slist_reverse (cookies);
+}
+
+/**
+ * soup_cookies_to_response:
+ * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
+ * @msg: a #SoupMessage
+ *
+ * Appends a "Set-Cookie" response header to @msg for each cookie in
+ * @cookies. (This is in addition to any other "Set-Cookie" headers
+ * @msg may already have.)
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookies_to_response (GSList *cookies, SoupMessage *msg)
+{
+ GString *header;
+
+ header = g_string_new (NULL);
+ while (cookies) {
+ serialize_cookie (cookies->data, header, TRUE);
+ soup_message_headers_append (msg->response_headers,
+ "Set-Cookie", header->str);
+ g_string_truncate (header, 0);
+ cookies = cookies->next;
+ }
+ g_string_free (header, TRUE);
+}
+
+/**
+ * soup_cookies_to_request:
+ * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
+ * @msg: a #SoupMessage
+ *
+ * Adds the name and value of each cookie in @cookies to @msg's
+ * "Cookie" request. (If @msg already has a "Cookie" request header,
+ * these cookies will be appended to the cookies already present. Be
+ * careful that you do not append the same cookies twice, eg, when
+ * requeuing a message.)
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookies_to_request (GSList *cookies, SoupMessage *msg)
+{
+ GString *header;
+
+ header = g_string_new (soup_message_headers_get_one (msg->request_headers,
+ "Cookie"));
+ while (cookies) {
+ serialize_cookie (cookies->data, header, FALSE);
+ cookies = cookies->next;
+ }
+ soup_message_headers_replace (msg->request_headers,
+ "Cookie", header->str);
+ g_string_free (header, TRUE);
+}
+
+/**
+ * soup_cookies_free: (skip)
+ * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
+ *
+ * Frees @cookies.
+ *
+ * Since: 2.24
+ **/
+void
+soup_cookies_free (GSList *cookies)
+{
+ GSList *c;
+
+ for (c = cookies; c; c = c->next)
+ soup_cookie_free (c->data);
+ g_slist_free (cookies);
+}
+
+/**
+ * soup_cookies_to_cookie_header:
+ * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
+ *
+ * Serializes a #GSList of #SoupCookie into a string suitable for
+ * setting as the value of the "Cookie" header.
+ *
+ * Return value: the serialization of @cookies
+ *
+ * Since: 2.24
+ **/
+char *
+soup_cookies_to_cookie_header (GSList *cookies)
+{
+ GString *str;
+
+ g_return_val_if_fail (cookies != NULL, NULL);
+
+ str = g_string_new (NULL);
+ while (cookies) {
+ serialize_cookie (cookies->data, str, FALSE);
+ cookies = cookies->next;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+/**
+ * soup_cookie_applies_to_uri:
+ * @cookie: a #SoupCookie
+ * @uri: a #SoupURI
+ *
+ * Tests if @cookie should be sent to @uri.
+ *
+ * (At the moment, this does not check that @cookie's domain matches
+ * @uri, because it assumes that the caller has already done that.
+ * But don't rely on that; it may change in the future.)
+ *
+ * Return value: %TRUE if @cookie should be sent to @uri, %FALSE if
+ * not
+ *
+ * Since: 2.24
+ **/
+gboolean
+soup_cookie_applies_to_uri (SoupCookie *cookie, SoupURI *uri)
+{
+ int plen;
+
+ if (cookie->secure && uri->scheme != SOUP_URI_SCHEME_HTTPS)
+ return FALSE;
+
+ if (cookie->expires && soup_date_is_past (cookie->expires))
+ return FALSE;
+
+ /* uri->path is required to be non-NULL */
+ g_return_val_if_fail (uri->path != NULL, FALSE);
+
+ plen = strlen (cookie->path);
+ if (plen == 0)
+ return TRUE;
+ if (strncmp (cookie->path, uri->path, plen) != 0)
+ return FALSE;
+ if (cookie->path[plen - 1] != '/' &&
+ uri->path[plen] && uri->path[plen] != '/')
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+soup_cookie_equal (SoupCookie *cookie1, SoupCookie *cookie2)
+{
+ g_return_val_if_fail (cookie1, FALSE);
+ g_return_val_if_fail (cookie2, FALSE);
+
+ return (!strcmp (cookie1->name, cookie2->name) &&
+ !strcmp (cookie1->value, cookie2->value) &&
+ !strcmp (cookie1->path, cookie2->path));
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2007, 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_COOKIE_H
+#define SOUP_COOKIE_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+struct _SoupCookie {
+ char *name;
+ char *value;
+ char *domain;
+ char *path;
+ SoupDate *expires;
+ gboolean secure;
+ gboolean http_only;
+};
+
+GType soup_cookie_get_type (void);
+#define SOUP_TYPE_COOKIE (soup_cookie_get_type())
+
+#define SOUP_COOKIE_MAX_AGE_ONE_HOUR (60 * 60)
+#define SOUP_COOKIE_MAX_AGE_ONE_DAY (SOUP_COOKIE_MAX_AGE_ONE_HOUR * 24)
+#define SOUP_COOKIE_MAX_AGE_ONE_WEEK (SOUP_COOKIE_MAX_AGE_ONE_DAY * 7)
+#define SOUP_COOKIE_MAX_AGE_ONE_YEAR (SOUP_COOKIE_MAX_AGE_ONE_DAY * 365.2422)
+
+SoupCookie *soup_cookie_new (const char *name,
+ const char *value,
+ const char *domain,
+ const char *path,
+ int max_age);
+SoupCookie *soup_cookie_parse (const char *header,
+ SoupURI *origin);
+SoupCookie *soup_cookie_copy (SoupCookie *cookie);
+
+const char *soup_cookie_get_name (SoupCookie *cookie);
+void soup_cookie_set_name (SoupCookie *cookie,
+ const char *name);
+const char *soup_cookie_get_value (SoupCookie *cookie);
+void soup_cookie_set_value (SoupCookie *cookie,
+ const char *value);
+const char *soup_cookie_get_domain (SoupCookie *cookie);
+void soup_cookie_set_domain (SoupCookie *cookie,
+ const char *domain);
+const char *soup_cookie_get_path (SoupCookie *cookie);
+void soup_cookie_set_path (SoupCookie *cookie,
+ const char *path);
+void soup_cookie_set_max_age (SoupCookie *cookie,
+ int max_age);
+SoupDate *soup_cookie_get_expires (SoupCookie *cookie);
+void soup_cookie_set_expires (SoupCookie *cookie,
+ SoupDate *expires);
+gboolean soup_cookie_get_secure (SoupCookie *cookie);
+void soup_cookie_set_secure (SoupCookie *cookie,
+ gboolean secure);
+gboolean soup_cookie_get_http_only (SoupCookie *cookie);
+void soup_cookie_set_http_only (SoupCookie *cookie,
+ gboolean http_only);
+
+char *soup_cookie_to_set_cookie_header (SoupCookie *cookie);
+char *soup_cookie_to_cookie_header (SoupCookie *cookie);
+
+gboolean soup_cookie_applies_to_uri (SoupCookie *cookie,
+ SoupURI *uri);
+gboolean soup_cookie_equal (SoupCookie *cookie1,
+ SoupCookie *cookie2);
+
+void soup_cookie_free (SoupCookie *cookie);
+
+GSList *soup_cookies_from_response (SoupMessage *msg);
+GSList *soup_cookies_from_request (SoupMessage *msg);
+
+void soup_cookies_to_response (GSList *cookies,
+ SoupMessage *msg);
+void soup_cookies_to_request (GSList *cookies,
+ SoupMessage *msg);
+
+void soup_cookies_free (GSList *cookies);
+
+char *soup_cookies_to_cookie_header (GSList *cookies);
+
+gboolean soup_cookie_domain_matches (SoupCookie *cookie,
+ const char *host);
+
+G_END_DECLS
+
+#endif /* SOUP_COOKIE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-date.c: Date/time handling
+ *
+ * Copyright (C) 2005, Novell, Inc.
+ * Copyright (C) 2007, Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include "soup-date.h"
+
+/**
+ * SoupDate:
+ * @year: the year, 1 to 9999
+ * @month: the month, 1 to 12
+ * @day: day of the month, 1 to 31
+ * @hour: hour of the day, 0 to 23
+ * @minute: minute, 0 to 59
+ * @second: second, 0 to 59 (or up to 61 in the case of leap seconds)
+ * @utc: %TRUE if the date is in UTC
+ * @offset: offset from UTC
+
+ * A date and time. The date is assumed to be in the (proleptic)
+ * Gregorian calendar. The time is in UTC if @utc is %TRUE. Otherwise,
+ * the time is a local time, and @offset gives the offset from UTC in
+ * minutes (such that adding @offset to the time would give the
+ * correct UTC time). If @utc is %FALSE and @offset is 0, then the
+ * %SoupDate represents a "floating" time with no associated timezone
+ * information.
+ **/
+
+/* Do not internationalize */
+static const char *const months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* Do not internationalize */
+static const char *const days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static const int nonleap_days_in_month[] = {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static const int nonleap_days_before[] = {
+ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
+};
+
+static inline gboolean
+is_leap_year (int year)
+{
+ return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
+}
+
+/* Computes the number of days since proleptic Gregorian 0000-12-31.
+ * (That is, 0001-01-01 is "1", and 1970-01-01 is 719163.
+ */
+static int
+rata_die_day (SoupDate *date)
+{
+ int day;
+
+ day = (date->year - 1) * 365 + ((date->year - 1) / 4) -
+ ((date->year - 1) / 100) + ((date->year - 1) / 400);
+ day += nonleap_days_before[date->month] + date->day;
+ if (is_leap_year (date->year) && date->month > 2)
+ day++;
+ return day;
+}
+
+#define TIME_T_EPOCH_RATA_DIE_DAY 719163
+
+static inline int
+days_in_month (int month, int year)
+{
+ if (month == 2 && is_leap_year (year))
+ return 29;
+ else
+ return nonleap_days_in_month[month];
+}
+
+GType
+soup_date_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupDate"),
+ (GBoxedCopyFunc) soup_date_copy,
+ (GBoxedFreeFunc) soup_date_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
+
+static void
+soup_date_fixup (SoupDate *date)
+{
+ /* We only correct date->second if it's negative or too high
+ * to be a leap second.
+ */
+ if (date->second < 0 || date->second > 61) {
+ date->minute += date->second / 60;
+ date->second %= 60;
+ if (date->second < 0)
+ date->second += 60;
+ }
+
+ if (date->minute < 0 || date->minute > 59) {
+ date->hour += date->minute / 60;
+ date->minute %= 60;
+ if (date->minute < 0)
+ date->minute += 60;
+ }
+
+ if (date->hour < 0 || date->hour > 23) {
+ date->day += date->hour / 24;
+ date->hour %= 24;
+ if (date->hour < 0)
+ date->hour += 24;
+ }
+
+ /* Have to make sure month is valid before we can look at the
+ * day.
+ */
+ if (date->month < 1 || date->month > 12) {
+ date->year += ((date->month - 1) / 12) + 1;
+ date->month = ((date->month - 1) % 12) + 1;
+ if (date->month < 1)
+ date->month += 12;
+ }
+
+ if (date->day < 0) {
+ while (date->day < 0) {
+ if (date->month == 1) {
+ date->month = 12;
+ date->year--;
+ } else
+ date->month--;
+ date->day += days_in_month (date->month, date->year);
+ }
+ } else {
+ while (date->day > days_in_month (date->month, date->year)) {
+ date->day -= days_in_month (date->month, date->year);
+ if (date->month == 12) {
+ date->month = 1;
+ date->year++;
+ } else
+ date->month++;
+ }
+ }
+}
+
+/**
+ * soup_date_new:
+ * @year: the year (1-9999)
+ * @month: the month (1-12)
+ * @day: the day of the month (1-31, as appropriate for @month)
+ * @hour: the hour (0-23)
+ * @minute: the minute (0-59)
+ * @second: the second (0-59, or up to 61 for leap seconds)
+ *
+ * Creates a #SoupDate representing the indicated time, UTC.
+ *
+ * Return value: a new #SoupDate
+ **/
+SoupDate *
+soup_date_new (int year, int month, int day,
+ int hour, int minute, int second)
+{
+ SoupDate *date = g_slice_new (SoupDate);
+
+ date->year = year;
+ date->month = month;
+ date->day = day;
+ date->hour = hour;
+ date->minute = minute;
+ date->second = second;
+ date->utc = TRUE;
+ date->offset = 0;
+
+ return date;
+}
+
+/**
+ * soup_date_new_from_now:
+ * @offset_seconds: offset from current time
+ *
+ * Creates a #SoupDate representing a time @offset_seconds after the
+ * current time (or before it, if @offset_seconds is negative). If
+ * offset_seconds is 0, returns the current time.
+ *
+ * If @offset_seconds would indicate a time not expressible as a
+ * #time_t, the return value will be clamped into range.
+ *
+ * Return value: a new #SoupDate
+ **/
+SoupDate *
+soup_date_new_from_now (int offset_seconds)
+{
+ time_t now = time (NULL);
+ time_t then = now + offset_seconds;
+
+ if (sizeof (time_t) == 4) {
+ if (offset_seconds < 0 && then > now)
+ return soup_date_new_from_time_t (-G_MAXINT);
+ else if (offset_seconds > 0 && then < now)
+ return soup_date_new_from_time_t (G_MAXINT);
+ }
+ return soup_date_new_from_time_t (then);
+}
+
+static gboolean
+parse_iso8601_date (SoupDate *date, const char *date_string)
+{
+ gulong val;
+
+ if (strlen (date_string) < 15)
+ return FALSE;
+ if (date_string[4] == '-' &&
+ date_string[7] == '-' &&
+ date_string[10] == 'T') {
+ /* YYYY-MM-DD */
+ date->year = atoi (date_string);
+ date->month = atoi (date_string + 5);
+ date->day = atoi (date_string + 8);
+ date_string += 11;
+ } else if (date_string[8] == 'T') {
+ /* YYYYMMDD */
+ val = atoi (date_string);
+ date->year = val / 10000;
+ date->month = (val % 10000) / 100;
+ date->day = val % 100;
+ date_string += 9;
+ } else
+ return FALSE;
+
+ if (strlen (date_string) >= 8 &&
+ date_string[2] == ':' && date_string[5] == ':') {
+ /* HH:MM:SS */
+ date->hour = atoi (date_string);
+ date->minute = atoi (date_string + 3);
+ date->second = atoi (date_string + 6);
+ date_string += 8;
+ } else if (strlen (date_string) >= 6) {
+ /* HHMMSS */
+ val = strtoul (date_string, (char **)&date_string, 10);
+ date->hour = val / 10000;
+ date->minute = (val % 10000) / 100;
+ date->second = val % 100;
+ } else
+ return FALSE;
+
+ if (*date_string == '.' || *date_string == ',')
+ (void) strtoul (date_string + 1, (char **)&date_string, 10);
+
+ if (*date_string == 'Z') {
+ date_string++;
+ date->utc = TRUE;
+ date->offset = 0;
+ } else if (*date_string == '+' || *date_string == '-') {
+ int sign = (*date_string == '+') ? -1 : 1;
+ val = strtoul (date_string + 1, (char **)&date_string, 10);
+ if (*date_string == ':')
+ val = 60 * val + strtoul (date_string + 1, (char **)&date_string, 10);
+ else
+ val = 60 * (val / 100) + (val % 100);
+ date->offset = sign * val;
+ date->utc = !val;
+ } else {
+ date->offset = 0;
+ date->utc = FALSE;
+ }
+
+ return !*date_string;
+}
+
+static inline gboolean
+parse_day (SoupDate *date, const char **date_string)
+{
+ char *end;
+
+ date->day = strtoul (*date_string, &end, 10);
+ if (end == (char *)*date_string)
+ return FALSE;
+
+ while (*end == ' ' || *end == '-')
+ end++;
+ *date_string = end;
+ return TRUE;
+}
+
+static inline gboolean
+parse_month (SoupDate *date, const char **date_string)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (months); i++) {
+ if (!g_ascii_strncasecmp (*date_string, months[i], 3)) {
+ date->month = i + 1;
+ *date_string += 3;
+ while (**date_string == ' ' || **date_string == '-')
+ (*date_string)++;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static inline gboolean
+parse_year (SoupDate *date, const char **date_string)
+{
+ char *end;
+
+ date->year = strtoul (*date_string, &end, 10);
+ if (end == (char *)*date_string)
+ return FALSE;
+
+ if (end == (char *)*date_string + 2) {
+ if (date->year < 70)
+ date->year += 2000;
+ else
+ date->year += 1900;
+ } else if (end == (char *)*date_string + 3)
+ date->year += 1900;
+
+ while (*end == ' ' || *end == '-')
+ end++;
+ *date_string = end;
+ return TRUE;
+}
+
+static inline gboolean
+parse_time (SoupDate *date, const char **date_string)
+{
+ char *p, *end;
+
+ date->hour = strtoul (*date_string, &end, 10);
+ if (end == (char *)*date_string || *end++ != ':')
+ return FALSE;
+ p = end;
+ date->minute = strtoul (p, &end, 10);
+ if (end == p || *end++ != ':')
+ return FALSE;
+ p = end;
+ date->second = strtoul (p, &end, 10);
+ if (end == p)
+ return FALSE;
+ p = end;
+
+ while (*p == ' ')
+ p++;
+ *date_string = p;
+ return TRUE;
+}
+
+static inline gboolean
+parse_timezone (SoupDate *date, const char **date_string)
+{
+ if (!**date_string) {
+ date->utc = FALSE;
+ date->offset = 0;
+ } else if (**date_string == '+' || **date_string == '-') {
+ gulong val;
+ int sign = (**date_string == '+') ? -1 : 1;
+ val = strtoul (*date_string + 1, (char **)date_string, 10);
+ if (**date_string == ':')
+ val = 60 * val + strtoul (*date_string + 1, (char **)date_string, 10);
+ else
+ val = 60 * (val / 100) + (val % 100);
+ date->offset = sign * val;
+ date->utc = (sign == -1) && !val;
+ } else if (**date_string == 'Z') {
+ date->offset = 0;
+ date->utc = TRUE;
+ (*date_string)++;
+ } else if (!strcmp (*date_string, "GMT") ||
+ !strcmp (*date_string, "UTC")) {
+ date->offset = 0;
+ date->utc = TRUE;
+ (*date_string) += 3;
+ } else if (strchr ("ECMP", **date_string) &&
+ ((*date_string)[1] == 'D' || (*date_string)[1] == 'S') &&
+ (*date_string)[2] == 'T') {
+ date->offset = -60 * (5 * strcspn ("ECMP", *date_string));
+ if ((*date_string)[1] == 'D')
+ date->offset += 60;
+ date->utc = FALSE;
+ } else
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean
+parse_textual_date (SoupDate *date, const char *date_string)
+{
+ /* If it starts with a word, it must be a weekday, which we skip */
+ if (g_ascii_isalpha (*date_string)) {
+ while (g_ascii_isalpha (*date_string))
+ date_string++;
+ if (*date_string == ',')
+ date_string++;
+ while (g_ascii_isspace (*date_string))
+ date_string++;
+ }
+
+ /* If there's now another word, this must be an asctime-date */
+ if (g_ascii_isalpha (*date_string)) {
+ /* (Sun) Nov 6 08:49:37 1994 */
+ if (!parse_month (date, &date_string) ||
+ !parse_day (date, &date_string) ||
+ !parse_time (date, &date_string) ||
+ !parse_year (date, &date_string))
+ return FALSE;
+
+ /* There shouldn't be a timezone, but check anyway */
+ parse_timezone (date, &date_string);
+ } else {
+ /* Non-asctime date, so some variation of
+ * (Sun,) 06 Nov 1994 08:49:37 GMT
+ */
+ if (!parse_day (date, &date_string) ||
+ !parse_month (date, &date_string) ||
+ !parse_year (date, &date_string) ||
+ !parse_time (date, &date_string))
+ return FALSE;
+
+ /* This time there *should* be a timezone, but we
+ * survive if there isn't.
+ */
+ parse_timezone (date, &date_string);
+ }
+ return TRUE;
+}
+
+/**
+ * SoupDateFormat:
+ * @SOUP_DATE_HTTP: RFC 1123 format, used by the HTTP "Date" header. Eg
+ * "Sun, 06 Nov 1994 08:49:37 GMT"
+ * @SOUP_DATE_COOKIE: The format for the "Expires" timestamp in the
+ * Netscape cookie specification. Eg, "Sun, 06-Nov-1994 08:49:37 GMT".
+ * @SOUP_DATE_RFC2822: RFC 2822 format, eg "Sun, 6 Nov 1994 09:49:37 -0100"
+ * @SOUP_DATE_ISO8601_COMPACT: ISO 8601 date/time with no optional
+ * punctuation. Eg, "19941106T094937-0100".
+ * @SOUP_DATE_ISO8601_FULL: ISO 8601 date/time with all optional
+ * punctuation. Eg, "1994-11-06T09:49:37-01:00".
+ * @SOUP_DATE_ISO8601_XMLRPC: ISO 8601 date/time as used by XML-RPC.
+ * Eg, "19941106T09:49:37".
+ * @SOUP_DATE_ISO8601: An alias for @SOUP_DATE_ISO8601_FULL.
+ *
+ * Date formats that soup_date_to_string() can use.
+ *
+ * @SOUP_DATE_HTTP and @SOUP_DATE_COOKIE always coerce the time to
+ * UTC. @SOUP_DATE_ISO8601_XMLRPC uses the time as given, ignoring the
+ * offset completely. @SOUP_DATE_RFC2822 and the other ISO 8601
+ * variants use the local time, appending the offset information if
+ * available.
+ *
+ * This enum may be extended with more values in future releases.
+ **/
+
+/**
+ * soup_date_new_from_string:
+ * @date_string: the date in some plausible format
+ *
+ * Parses @date_string and tries to extract a date from it. This
+ * recognizes all of the "HTTP-date" formats from RFC 2616, all ISO
+ * 8601 formats containing both a time and a date, RFC 2822 dates,
+ * and reasonable approximations thereof. (Eg, it is lenient about
+ * whitespace, leading "0"s, etc.)
+ *
+ * Return value: a new #SoupDate, or %NULL if @date_string could not
+ * be parsed.
+ **/
+SoupDate *
+soup_date_new_from_string (const char *date_string)
+{
+ SoupDate *date;
+ gboolean success;
+
+ g_return_val_if_fail (date_string != NULL, NULL);
+
+ date = g_slice_new (SoupDate);
+
+ while (g_ascii_isspace (*date_string))
+ date_string++;
+
+ /* If it starts with a digit, it's either an ISO 8601 date, or
+ * an RFC2822 date without the optional weekday; in the later
+ * case, there will be a month name later on, so look for one
+ * of the month-start letters.
+ */
+ if (g_ascii_isdigit (*date_string) &&
+ !strpbrk (date_string, "JFMASOND"))
+ success = parse_iso8601_date (date, date_string);
+ else
+ success = parse_textual_date (date, date_string);
+
+ if (!success) {
+ g_slice_free (SoupDate, date);
+ return NULL;
+ }
+
+ if (date->year < 1 || date->year > 9999 ||
+ date->month < 1 || date->month > 12 ||
+ date->day < 1 ||
+ date->day > days_in_month (date->month, date->year) ||
+ date->hour < 0 || date->hour > 24 ||
+ date->minute < 0 || date->minute > 59 ||
+ date->second < 0 || date->second > 61) {
+ soup_date_free (date);
+ return NULL;
+ }
+ if (date->hour == 24) {
+ /* ISO8601 allows this explicitly. We allow it for
+ * other types as well just for simplicity.
+ */
+ if (date->minute == 0 && date->second == 0)
+ soup_date_fixup (date);
+ else {
+ soup_date_free (date);
+ return NULL;
+ }
+ }
+
+ return date;
+}
+
+/**
+ * soup_date_new_from_time_t:
+ * @when: a #time_t
+ *
+ * Creates a #SoupDate corresponding to @when
+ *
+ * Return value: a new #SoupDate
+ **/
+SoupDate *
+soup_date_new_from_time_t (time_t when)
+{
+ struct tm tm;
+
+#ifdef HAVE_GMTIME_R
+ gmtime_r (&when, &tm);
+#else
+ tm = *gmtime (&when);
+#endif
+
+ return soup_date_new (tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+static const char *
+soup_date_weekday (SoupDate *date)
+{
+ /* Proleptic Gregorian 0001-01-01 was a Monday, which
+ * corresponds to 1 in the days[] array.
+ */
+ return days[rata_die_day (date) % 7];
+}
+
+/**
+ * soup_date_to_string:
+ * @date: a #SoupDate
+ * @format: the format to generate the date in
+ *
+ * Converts @date to a string in the format described by @format.
+ *
+ * Return value: @date as a string
+ **/
+char *
+soup_date_to_string (SoupDate *date, SoupDateFormat format)
+{
+ g_return_val_if_fail (date != NULL, NULL);
+
+ if (format == SOUP_DATE_HTTP || format == SOUP_DATE_COOKIE) {
+ /* HTTP and COOKIE formats require UTC timestamp, so coerce
+ * @date if it's non-UTC.
+ */
+ SoupDate utcdate;
+
+ if (date->offset != 0) {
+ memcpy (&utcdate, date, sizeof (SoupDate));
+ utcdate.minute += utcdate.offset;
+ utcdate.offset = 0;
+ utcdate.utc = TRUE;
+ soup_date_fixup (&utcdate);
+ date = &utcdate;
+ }
+
+ switch (format) {
+ case SOUP_DATE_HTTP:
+ /* "Sun, 06 Nov 1994 08:49:37 GMT" */
+ return g_strdup_printf (
+ "%s, %02d %s %04d %02d:%02d:%02d GMT",
+ soup_date_weekday (date), date->day,
+ months[date->month - 1], date->year,
+ date->hour, date->minute, date->second);
+
+ case SOUP_DATE_COOKIE:
+ /* "Sun, 06-Nov-1994 08:49:37 GMT" */
+ return g_strdup_printf (
+ "%s, %02d-%s-%04d %02d:%02d:%02d GMT",
+ soup_date_weekday (date), date->day,
+ months[date->month - 1], date->year,
+ date->hour, date->minute, date->second);
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+ } else if (format == SOUP_DATE_ISO8601_XMLRPC) {
+ /* Always "floating", ignore offset */
+ return g_strdup_printf ("%04d%02d%02dT%02d:%02d:%02d",
+ date->year, date->month, date->day,
+ date->hour, date->minute, date->second);
+ } else {
+ int hour_offset, minute_offset;
+ char zone[8], sign;
+
+ /* For other ISO8601 formats or RFC2822, use the
+ * offset given in @date. For ISO8601 formats, use "Z"
+ * for UTC, +-offset for non-UTC, and nothing for
+ * floating. For RFC2822, use +-offset for UTC or
+ * non-UTC, and -0000 for floating.
+ */
+ hour_offset = abs (date->offset) / 60;
+ minute_offset = abs (date->offset) - hour_offset * 60;
+
+ switch (format) {
+ case SOUP_DATE_ISO8601_COMPACT:
+ /* "19941106T084937[zone]" */
+ if (date->utc)
+ strcpy (zone, "Z");
+ else if (date->offset) {
+ g_snprintf (zone, sizeof (zone), "%c%02d%02d",
+ date->offset > 0 ? '-' : '+',
+ hour_offset, minute_offset);
+ } else
+ *zone = '\0';
+
+ return g_strdup_printf (
+ "%04d%02d%02dT%02d%02d%02d%s",
+ date->year, date->month, date->day,
+ date->hour, date->minute, date->second,
+ zone);
+
+ case SOUP_DATE_ISO8601_FULL:
+ /* "1994-11-06T08:49:37[zone]" */
+ if (date->utc)
+ strcpy (zone, "Z");
+ else if (date->offset) {
+ g_snprintf (zone, sizeof (zone), "%c%02d:%02d",
+ date->offset > 0 ? '-' : '+',
+ hour_offset, minute_offset);
+ } else
+ *zone = '\0';
+
+ return g_strdup_printf (
+ "%04d-%02d-%02dT%02d:%02d:%02d%s",
+ date->year, date->month, date->day,
+ date->hour, date->minute, date->second,
+ zone);
+
+ case SOUP_DATE_RFC2822:
+ /* "Sun, 6 Nov 1994 09:49:37 -0100" */
+ if (date->offset)
+ sign = (date->offset > 0) ? '-' : '+';
+ else
+ sign = date->utc ? '+' : '-';
+ return g_strdup_printf (
+ "%s, %d %s %04d %02d:%02d:%02d %c%02d%02d",
+ soup_date_weekday (date), date->day,
+ months[date->month - 1], date->year,
+ date->hour, date->minute, date->second,
+ sign, hour_offset, minute_offset);
+
+ default:
+ return NULL;
+ }
+ }
+}
+
+/**
+ * soup_date_to_time_t:
+ * @date: a #SoupDate
+ *
+ * Converts @date to a %time_t.
+ *
+ * If @date is not representable as a %time_t, it will be clamped into
+ * range. (In particular, some HTTP cookies have expiration dates
+ * after "Y2.038k" (2038-01-19T03:14:07Z).)
+ *
+ * Return value: @date as a %time_t
+ **/
+time_t
+soup_date_to_time_t (SoupDate *date)
+{
+ time_t tt;
+ GTimeVal val;
+
+ g_return_val_if_fail (date != NULL, 0);
+
+ /* FIXME: offset, etc */
+
+ if (date->year < 1970)
+ return 0;
+
+ /* If the year is later than 2038, we're guaranteed to
+ * overflow a 32-bit time_t. (If it's exactly 2038, we'll
+ * *probably* overflow, but only by a little, and it's easiest
+ * to test that at the end by seeing if the result has turned
+ * negative.)
+ */
+ if (sizeof (time_t) == 4 && date->year > 2038)
+ return (time_t)0x7fffffff;
+
+ soup_date_to_timeval (date, &val);
+ tt = val.tv_sec;
+
+ if (sizeof (time_t) == 4 && tt < 0)
+ return (time_t)0x7fffffff;
+ return tt;
+}
+
+/**
+ * soup_date_to_timeval:
+ * @date: a #SoupDate
+ * @time: (out): a #GTimeVal structure in which to store the converted time.
+ *
+ * Converts @date to a #GTimeVal.
+ *
+ * Since: 2.24
+ */
+void
+soup_date_to_timeval (SoupDate *date, GTimeVal *time)
+{
+ g_return_if_fail (date != NULL);
+ g_return_if_fail (time != NULL);
+
+ /* FIXME: offset, etc */
+
+ time->tv_sec = rata_die_day (date) - TIME_T_EPOCH_RATA_DIE_DAY;
+ time->tv_sec = ((((time->tv_sec * 24) + date->hour) * 60) + date->minute) * 60 + date->second;
+ time->tv_usec = 0;
+}
+
+/**
+ * soup_date_is_past:
+ * @date: a #SoupDate
+ *
+ * Determines if @date is in the past.
+ *
+ * Return value: %TRUE if @date is in the past
+ *
+ * Since: 2.24
+ **/
+gboolean
+soup_date_is_past (SoupDate *date)
+{
+ g_return_val_if_fail (date != NULL, TRUE);
+
+ /* optimization */
+ if (date->year < 2010)
+ return TRUE;
+
+ return soup_date_to_time_t (date) < time (NULL);
+}
+
+/**
+ * soup_date_get_year:
+ * @date: a #SoupDate
+ *
+ * Gets @date's year.
+ *
+ * Return value: @date's year
+ *
+ * Since: 2.32
+ **/
+int
+soup_date_get_year (SoupDate *date)
+{
+ return date->year;
+}
+
+/**
+ * soup_date_get_month:
+ * @date: a #SoupDate
+ *
+ * Gets @date's month.
+ *
+ * Return value: @date's month
+ *
+ * Since: 2.32
+ **/
+int
+soup_date_get_month (SoupDate *date)
+{
+ return date->month;
+}
+
+/**
+ * soup_date_get_day:
+ * @date: a #SoupDate
+ *
+ * Gets @date's day.
+ *
+ * Return value: @date's day
+ *
+ * Since: 2.32
+ **/
+int
+soup_date_get_day (SoupDate *date)
+{
+ return date->day;
+}
+
+/**
+ * soup_date_get_hour:
+ * @date: a #SoupDate
+ *
+ * Gets @date's hour.
+ *
+ * Return value: @date's hour
+ *
+ * Since: 2.32
+ **/
+int
+soup_date_get_hour (SoupDate *date)
+{
+ return date->hour;
+}
+
+/**
+ * soup_date_get_minute:
+ * @date: a #SoupDate
+ *
+ * Gets @date's minute.
+ *
+ * Return value: @date's minute
+ *
+ * Since: 2.32
+ **/
+int
+soup_date_get_minute (SoupDate *date)
+{
+ return date->minute;
+}
+
+/**
+ * soup_date_get_second:
+ * @date: a #SoupDate
+ *
+ * Gets @date's second.
+ *
+ * Return value: @date's second
+ *
+ * Since: 2.32
+ **/
+int
+soup_date_get_second (SoupDate *date)
+{
+ return date->second;
+}
+
+/**
+ * soup_date_get_utc:
+ * @date: a #SoupDate
+ *
+ * Gets @date's UTC flag
+ *
+ * Return value: %TRUE if @date is UTC.
+ *
+ * Since: 2.32
+ **/
+gboolean
+soup_date_get_utc (SoupDate *date)
+{
+ return date->utc;
+}
+
+/**
+ * soup_date_get_offset:
+ * @date: a #SoupDate
+ *
+ * Gets @date's offset from UTC.
+ *
+ * Return value: @date's offset from UTC. If soup_date_get_utc()
+ * returns %FALSE but soup_date_get_offset() returns 0, that means the
+ * date is a "floating" time with no associated offset information.
+ *
+ * Since: 2.32
+ **/
+int
+soup_date_get_offset (SoupDate *date)
+{
+ return date->offset;
+}
+
+/**
+ * soup_date_copy:
+ * @date: a #SoupDate
+ *
+ * Copies @date.
+ **/
+SoupDate *
+soup_date_copy (SoupDate *date)
+{
+ SoupDate *copy;
+
+ g_return_val_if_fail (date != NULL, NULL);
+
+ copy = g_slice_new (SoupDate);
+ memcpy (copy, date, sizeof (SoupDate));
+ return copy;
+}
+
+/**
+ * soup_date_free:
+ * @date: a #SoupDate
+ *
+ * Frees @date.
+ **/
+void
+soup_date_free (SoupDate *date)
+{
+ g_return_if_fail (date != NULL);
+
+ g_slice_free (SoupDate, date);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_DATE_H
+#define SOUP_DATE_H 1
+
+#include <time.h>
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+struct _SoupDate {
+ int year;
+ int month;
+ int day;
+
+ int hour;
+ int minute;
+ int second;
+
+ gboolean utc;
+ int offset;
+};
+
+typedef enum {
+ SOUP_DATE_HTTP = 1,
+ SOUP_DATE_COOKIE,
+ SOUP_DATE_RFC2822,
+ SOUP_DATE_ISO8601_COMPACT,
+ SOUP_DATE_ISO8601_FULL,
+ SOUP_DATE_ISO8601 = SOUP_DATE_ISO8601_FULL,
+ SOUP_DATE_ISO8601_XMLRPC
+} SoupDateFormat;
+
+GType soup_date_get_type (void);
+#define SOUP_TYPE_DATE (soup_date_get_type ())
+
+SoupDate *soup_date_new (int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second);
+SoupDate *soup_date_new_from_string (const char *date_string);
+SoupDate *soup_date_new_from_time_t (time_t when);
+SoupDate *soup_date_new_from_now (int offset_seconds);
+
+char *soup_date_to_string (SoupDate *date,
+ SoupDateFormat format);
+time_t soup_date_to_time_t (SoupDate *date);
+void soup_date_to_timeval (SoupDate *date,
+ GTimeVal *time);
+
+gboolean soup_date_is_past (SoupDate *date);
+
+int soup_date_get_year (SoupDate *date);
+int soup_date_get_month (SoupDate *date);
+int soup_date_get_day (SoupDate *date);
+int soup_date_get_hour (SoupDate *date);
+int soup_date_get_minute (SoupDate *date);
+int soup_date_get_second (SoupDate *date);
+int soup_date_get_utc (SoupDate *date);
+int soup_date_get_offset (SoupDate *date);
+
+SoupDate *soup_date_copy (SoupDate *date);
+void soup_date_free (SoupDate *date);
+
+G_END_DECLS
+
+#endif /* SOUP_DATE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-directory-input-stream.h"
+
+#include <libsoup/soup.h>
+#include <stdio.h>
+#include <string.h>
+
+#define INIT_STRING "<html><head><title>OMG!</title></head><body><table>"
+#define EXIT_STRING "</table></html>"
+
+G_DEFINE_TYPE (SoupDirectoryInputStream, soup_directory_input_stream, G_TYPE_INPUT_STREAM)
+
+static SoupBuffer *
+soup_directory_input_stream_parse_info (SoupDirectoryInputStream *stream,
+ GFileInfo *info)
+{
+ SoupBuffer *buffer;
+ GString *string;
+ const char *s;
+ char *escaped, *path, *xml_string;
+
+ if (!g_file_info_get_name (info))
+ return NULL;
+
+ s = g_file_info_get_display_name (info);
+ if (!s) {
+ s = g_file_info_get_name (info);
+ /* FIXME: convert somehow? */
+ if (!g_utf8_validate (s, -1, NULL))
+ return NULL;
+ }
+ string = g_string_new ("<tr>");
+
+ xml_string = g_markup_escape_text (s, -1);
+ escaped = g_uri_escape_string (g_file_info_get_name (info), NULL, FALSE);
+ path = g_strconcat (stream->uri, "/", escaped, NULL);
+ g_free (escaped);
+ g_string_append_printf (string, "<td><a href=\"%s\">%s</a></td>", path, xml_string);
+ g_free (path);
+ g_free (xml_string);
+ g_string_append (string, "</tr>");
+
+ buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len);
+ g_string_free (string, FALSE);
+
+ return buffer;
+}
+
+static SoupBuffer *
+soup_directory_input_stream_read_next_file (SoupDirectoryInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFileInfo *info;
+ SoupBuffer *buffer;
+ GError *err = NULL;
+
+ do {
+ info = g_file_enumerator_next_file (stream->enumerator, cancellable, &err);
+ if (info == NULL) {
+ if (err) {
+ g_propagate_error (error, err);
+ return NULL;
+ } else if (!stream->done) {
+ stream->done = TRUE;
+ return soup_buffer_new (SOUP_MEMORY_STATIC,
+ EXIT_STRING,
+ sizeof (EXIT_STRING));
+ } else {
+ return NULL;
+ }
+ }
+
+ buffer = soup_directory_input_stream_parse_info (stream, info);
+ g_object_unref (info);
+ } while (buffer == NULL);
+
+ return buffer;
+}
+
+static gssize
+soup_directory_input_stream_read (GInputStream *input,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupDirectoryInputStream *stream = SOUP_DIRECTORY_INPUT_STREAM (input);
+ gsize total, size;
+
+ for (total = 0; total < count; total += size) {
+ if (stream->buffer == NULL) {
+ stream->buffer = soup_directory_input_stream_read_next_file (stream, cancellable, error);
+ if (stream->buffer == NULL) {
+ /* FIXME: Is this correct or should we forward the error? */
+ if (total)
+ g_clear_error (error);
+ return total;
+ }
+ }
+
+ size = MIN (stream->buffer->length, count - total);
+ memcpy ((char *)buffer + total, stream->buffer->data, size);
+ if (size == stream->buffer->length) {
+ soup_buffer_free (stream->buffer);
+ stream->buffer = NULL;
+ } else {
+ SoupBuffer *sub = soup_buffer_new_subbuffer (stream->buffer,
+ size,
+ stream->buffer->length - size);
+ soup_buffer_free (stream->buffer);
+ stream->buffer = sub;
+ }
+ }
+
+ return total;
+}
+
+static gboolean
+soup_directory_input_stream_close (GInputStream *input,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupDirectoryInputStream *stream = SOUP_DIRECTORY_INPUT_STREAM (input);
+ gboolean result;
+
+ if (stream->buffer) {
+ soup_buffer_free (stream->buffer);
+ stream->buffer = NULL;
+ }
+
+ result = g_file_enumerator_close (stream->enumerator,
+ cancellable,
+ error);
+ g_object_unref (stream->enumerator);
+ stream->enumerator = NULL;
+
+ g_free (stream->uri);
+ stream->uri = NULL;
+
+ return result;
+}
+
+static void
+soup_directory_input_stream_class_init (SoupDirectoryInputStreamClass *stream_class)
+{
+ GInputStreamClass *inputstream_class = G_INPUT_STREAM_CLASS (stream_class);
+
+ inputstream_class->read_fn = soup_directory_input_stream_read;
+ inputstream_class->close_fn = soup_directory_input_stream_close;
+}
+
+static void
+soup_directory_input_stream_init (SoupDirectoryInputStream *stream)
+{
+ stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC,
+ INIT_STRING,
+ sizeof (INIT_STRING));
+}
+
+GInputStream *
+soup_directory_input_stream_new (GFileEnumerator *enumerator,
+ SoupURI *uri)
+{
+ GInputStream *stream;
+
+ g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ stream = g_object_new (SOUP_TYPE_DIRECTORY_INPUT_STREAM, NULL);
+
+ SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator);
+ SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE);
+
+ return stream;
+}
+
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SOUP_DIRECTORY_INPUT_STREAM_H
+#define SOUP_DIRECTORY_INPUT_STREAM_H 1
+
+#include <gio/gio.h>
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_DIRECTORY_INPUT_STREAM (soup_directory_input_stream_get_type ())
+#define SOUP_DIRECTORY_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_DIRECTORY_INPUT_STREAM, SoupDirectoryInputStream))
+#define SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_DIRECTORY_INPUT_STREAM, SoupDirectoryInputStreamClass))
+#define SOUP_IS_DIRECTORY_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_DIRECTORY_INPUT_STREAM))
+#define SOUP_IS_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_DIRECTORY_INPUT_STREAM))
+#define SOUP_DIRECTORY_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_DIRECTORY_INPUT_STREAM, SoupDirectoryInputStreamClass))
+
+typedef struct _SoupDirectoryInputStream SoupDirectoryInputStream;
+typedef struct _SoupDirectoryInputStreamClass SoupDirectoryInputStreamClass;
+
+struct _SoupDirectoryInputStream {
+ GInputStream parent;
+
+ GFileEnumerator *enumerator;
+ char *uri;
+ SoupBuffer *buffer;
+ gboolean done;
+};
+
+struct _SoupDirectoryInputStreamClass {
+ GInputStreamClass parent_class;
+};
+
+GType soup_directory_input_stream_get_type (void);
+
+GInputStream *soup_directory_input_stream_new (GFileEnumerator *enumerator,
+ SoupURI *uri);
+
+
+G_END_DECLS
+
+#endif /* SOUP_DIRECTORY_INPUT_STREAM_H */
--- /dev/null
+/*** BEGIN file-header ***/
+#include "soup.h"
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+#include "soup-cache.h"
+#include "soup-requester.h"
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type (void)
+{
+ static GType etype = 0;
+ if (G_UNLIKELY (etype == 0)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ }
+ return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
--- /dev/null
+/*** BEGIN file-header ***/
+#ifndef __SOUP_ENUM_TYPES_H__
+#define __SOUP_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define SOUP_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __SOUP_ENUM_TYPES_H__ */
+/*** END file-tail ***/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* soup-form.c : utility functions for HTML forms */
+
+/*
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-form.h"
+#include "soup-message.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-form
+ * @short_description: HTML form handling
+ * @see_also: #SoupMultipart
+ *
+ * libsoup contains several help methods for processing HTML forms as
+ * defined by <ulink
+ * url="http://www.w3.org/TR/html401/interact/forms.html#h-17.13">the
+ * HTML 4.01 specification</ulink>.
+ **/
+
+/**
+ * SOUP_FORM_MIME_TYPE_URLENCODED:
+ *
+ * A macro containing the value
+ * <literal>"application/x-www-form-urlencoded"</literal>; the default
+ * MIME type for POSTing HTML form data.
+ *
+ * Since: 2.26
+ **/
+
+/**
+ * SOUP_FORM_MIME_TYPE_MULTIPART:
+ *
+ * A macro containing the value
+ * <literal>"multipart/form-data"</literal>; the MIME type used for
+ * posting form data that contains files to be uploaded.
+ *
+ * Since: 2.26
+ **/
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+static gboolean
+form_decode (char *part)
+{
+ unsigned char *s, *d;
+
+ s = d = (unsigned char *)part;
+ do {
+ if (*s == '%') {
+ if (!g_ascii_isxdigit (s[1]) ||
+ !g_ascii_isxdigit (s[2]))
+ return FALSE;
+ *d++ = HEXCHAR (s);
+ s += 2;
+ } else if (*s == '+')
+ *d++ = ' ';
+ else
+ *d++ = *s;
+ } while (*s++);
+
+ return TRUE;
+}
+
+/**
+ * soup_form_decode:
+ * @encoded_form: data of type "application/x-www-form-urlencoded"
+ *
+ * Decodes @form, which is an urlencoded dataset as defined in the
+ * HTML 4.01 spec.
+ *
+ * Return value: (element-type utf8 utf8) (transfer full): a hash
+ * table containing the name/value pairs from @encoded_form, which you
+ * can free with g_hash_table_destroy().
+ **/
+GHashTable *
+soup_form_decode (const char *encoded_form)
+{
+ GHashTable *form_data_set;
+ char **pairs, *eq, *name, *value;
+ int i;
+
+ form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+ pairs = g_strsplit (encoded_form, "&", -1);
+ for (i = 0; pairs[i]; i++) {
+ name = pairs[i];
+ eq = strchr (name, '=');
+ if (eq) {
+ *eq = '\0';
+ value = eq + 1;
+ } else
+ value = NULL;
+ if (!value || !form_decode (name) || !form_decode (value)) {
+ g_free (name);
+ continue;
+ }
+
+ g_hash_table_replace (form_data_set, name, value);
+ }
+ g_free (pairs);
+
+ return form_data_set;
+}
+
+/**
+ * soup_form_decode_multipart:
+ * @msg: a #SoupMessage containing a "multipart/form-data" request body
+ * @file_control_name: the name of the HTML file upload control, or %NULL
+ * @filename: (out): return location for the name of the uploaded file
+ * @content_type: (out): return location for the MIME type of the uploaded file
+ * @file: (out): return location for the uploaded file data
+ *
+ * Decodes the "multipart/form-data" request in @msg; this is a
+ * convenience method for the case when you have a single file upload
+ * control in a form. (Or when you don't have any file upload
+ * controls, but are still using "multipart/form-data" anyway.) Pass
+ * the name of the file upload control in @file_control_name, and
+ * soup_form_decode_multipart() will extract the uploaded file data
+ * into @filename, @content_type, and @file. All of the other form
+ * control data will be returned (as strings, as with
+ * soup_form_decode()) in the returned #GHashTable.
+ *
+ * You may pass %NULL for @filename and/or @content_type if you do not
+ * care about those fields. soup_form_decode_multipart() may also
+ * return %NULL in those fields if the client did not provide that
+ * information. You must free the returned filename and content-type
+ * with g_free(), and the returned file data with soup_buffer_free().
+ *
+ * If you have a form with more than one file upload control, you will
+ * need to decode it manually, using soup_multipart_new_from_message()
+ * and soup_multipart_get_part().
+ *
+ * Return value: (element-type utf8 utf8) (transfer full): a hash
+ * table containing the name/value pairs (other than
+ * @file_control_name) from @msg, which you can free with
+ * g_hash_table_destroy(). On error, it will return %NULL.
+ *
+ * Since: 2.26
+ **/
+GHashTable *
+soup_form_decode_multipart (SoupMessage *msg, const char *file_control_name,
+ char **filename, char **content_type,
+ SoupBuffer **file)
+{
+ SoupMultipart *multipart;
+ GHashTable *form_data_set, *params;
+ SoupMessageHeaders *part_headers;
+ SoupBuffer *part_body;
+ char *disposition, *name;
+ int i;
+
+ multipart = soup_multipart_new_from_message (msg->request_headers,
+ msg->request_body);
+ if (!multipart)
+ return NULL;
+
+ if (filename)
+ *filename = NULL;
+ if (content_type)
+ *content_type = NULL;
+ *file = NULL;
+
+ form_data_set = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+ for (i = 0; i < soup_multipart_get_length (multipart); i++) {
+ soup_multipart_get_part (multipart, i, &part_headers, &part_body);
+ if (!soup_message_headers_get_content_disposition (
+ part_headers, &disposition, ¶ms))
+ continue;
+ name = g_hash_table_lookup (params, "name");
+ if (g_ascii_strcasecmp (disposition, "form-data") != 0 ||
+ !name) {
+ g_free (disposition);
+ g_hash_table_destroy (params);
+ continue;
+ }
+
+ if (!strcmp (name, file_control_name)) {
+ if (filename)
+ *filename = g_strdup (g_hash_table_lookup (params, "filename"));
+ if (content_type)
+ *content_type = g_strdup (soup_message_headers_get_content_type (part_headers, NULL));
+ if (file)
+ *file = soup_buffer_copy (part_body);
+ } else {
+ g_hash_table_insert (form_data_set,
+ g_strdup (name),
+ g_strndup (part_body->data,
+ part_body->length));
+ }
+
+ g_free (disposition);
+ g_hash_table_destroy (params);
+ }
+
+ soup_multipart_free (multipart);
+ return form_data_set;
+}
+
+static void
+append_form_encoded (GString *str, const char *in)
+{
+ const unsigned char *s = (const unsigned char *)in;
+
+ while (*s) {
+ if (*s == ' ') {
+ g_string_append_c (str, '+');
+ s++;
+ } else if (!g_ascii_isalnum (*s))
+ g_string_append_printf (str, "%%%02X", (int)*s++);
+ else
+ g_string_append_c (str, *s++);
+ }
+}
+
+static void
+encode_pair (GString *str, const char *name, const char *value)
+{
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (value != NULL);
+
+ if (str->len)
+ g_string_append_c (str, '&');
+ append_form_encoded (str, name);
+ g_string_append_c (str, '=');
+ append_form_encoded (str, value);
+}
+
+static void
+hash_encode_foreach (gpointer name, gpointer value, gpointer str)
+{
+ encode_pair (str, name, value);
+}
+
+/**
+ * soup_form_encode:
+ * @first_field: name of the first form field
+ * @...: value of @first_field, followed by additional field names
+ * and values, terminated by %NULL.
+ *
+ * Encodes the given field names and values into a value of type
+ * "application/x-www-form-urlencoded", as defined in the HTML 4.01
+ * spec.
+ *
+ * This method requires you to know the names of the form fields (or
+ * at the very least, the total number of fields) at compile time; for
+ * working with dynamic forms, use soup_form_encode_hash() or
+ * soup_form_encode_datalist().
+ *
+ * Return value: the encoded form
+ **/
+char *
+soup_form_encode (const char *first_field, ...)
+{
+ va_list args;
+ char *encoded;
+
+ va_start (args, first_field);
+ encoded = soup_form_encode_valist (first_field, args);
+ va_end (args);
+
+ return encoded;
+}
+
+/**
+ * soup_form_encode_hash:
+ * @form_data_set: (element-type utf8 utf8): a hash table containing
+ * name/value pairs (as strings)
+ *
+ * Encodes @form_data_set into a value of type
+ * "application/x-www-form-urlencoded", as defined in the HTML 4.01
+ * spec.
+ *
+ * Note that the HTML spec states that "The control names/values are
+ * listed in the order they appear in the document." Since this method
+ * takes a hash table, it cannot enforce that; if you care about the
+ * ordering of the form fields, use soup_form_encode_datalist().
+ *
+ * Return value: the encoded form
+ **/
+char *
+soup_form_encode_hash (GHashTable *form_data_set)
+{
+ GString *str = g_string_new (NULL);
+
+ g_hash_table_foreach (form_data_set, hash_encode_foreach, str);
+ return g_string_free (str, FALSE);
+}
+
+static void
+datalist_encode_foreach (GQuark key_id, gpointer value, gpointer str)
+{
+ encode_pair (str, g_quark_to_string (key_id), value);
+}
+
+/**
+ * soup_form_encode_datalist:
+ * @form_data_set: a datalist containing name/value pairs
+ *
+ * Encodes @form_data_set into a value of type
+ * "application/x-www-form-urlencoded", as defined in the HTML 4.01
+ * spec. Unlike soup_form_encode_hash(), this preserves the ordering
+ * of the form elements, which may be required in some situations.
+ *
+ * Return value: the encoded form
+ **/
+char *
+soup_form_encode_datalist (GData **form_data_set)
+{
+ GString *str = g_string_new (NULL);
+
+ g_datalist_foreach (form_data_set, datalist_encode_foreach, str);
+ return g_string_free (str, FALSE);
+}
+
+/**
+ * soup_form_encode_valist:
+ * @first_field: name of the first form field
+ * @args: pointer to additional values, as in soup_form_encode()
+ *
+ * See soup_form_encode(). This is mostly an internal method, used by
+ * various other methods such as soup_uri_set_query_from_fields() and
+ * soup_form_request_new().
+ *
+ * Return value: the encoded form
+ **/
+char *
+soup_form_encode_valist (const char *first_field, va_list args)
+{
+ GString *str = g_string_new (NULL);
+ const char *name, *value;
+
+ name = first_field;
+ value = va_arg (args, const char *);
+ while (name && value) {
+ encode_pair (str, name, value);
+
+ name = va_arg (args, const char *);
+ if (name)
+ value = va_arg (args, const char *);
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+static SoupMessage *
+soup_form_request_for_data (const char *method, const char *uri_string,
+ char *form_data)
+{
+ SoupMessage *msg;
+ SoupURI *uri;
+
+ uri = soup_uri_new (uri_string);
+ if (!uri)
+ return NULL;
+
+ if (!strcmp (method, "GET")) {
+ g_free (uri->query);
+ uri->query = form_data;
+
+ msg = soup_message_new_from_uri (method, uri);
+ } else if (!strcmp (method, "POST") || !strcmp (method, "PUT")) {
+ msg = soup_message_new_from_uri (method, uri);
+
+ soup_message_set_request (
+ msg, SOUP_FORM_MIME_TYPE_URLENCODED,
+ SOUP_MEMORY_TAKE,
+ form_data, strlen (form_data));
+ } else {
+ g_warning ("invalid method passed to soup_form_request_new");
+ g_free (form_data);
+
+ /* Don't crash */
+ msg = soup_message_new_from_uri (method, uri);
+ }
+ soup_uri_free (uri);
+
+ return msg;
+}
+
+/**
+ * soup_form_request_new:
+ * @method: the HTTP method, either "GET" or "POST"
+ * @uri: the URI to send the form data to
+ * @first_field: name of the first form field
+ * @...: value of @first_field, followed by additional field names
+ * and values, terminated by %NULL.
+ *
+ * Creates a new %SoupMessage and sets it up to send the given data
+ * to @uri via @method. (That is, if @method is "GET", it will encode
+ * the form data into @uri's query field, and if @method is "POST", it
+ * will encode it into the %SoupMessage's request_body.)
+ *
+ * Return value: (transfer full): the new %SoupMessage
+ **/
+SoupMessage *
+soup_form_request_new (const char *method, const char *uri,
+ const char *first_field, ...)
+{
+ va_list args;
+ char *form_data;
+
+ va_start (args, first_field);
+ form_data = soup_form_encode_valist (first_field, args);
+ va_end (args);
+
+ return soup_form_request_for_data (method, uri, form_data);
+}
+
+/**
+ * soup_form_request_new_from_hash:
+ * @method: the HTTP method, either "GET" or "POST"
+ * @uri: the URI to send the form data to
+ * @form_data_set: (element-type utf8 utf8): the data to send to @uri
+ *
+ * Creates a new %SoupMessage and sets it up to send @form_data_set to
+ * @uri via @method, as with soup_form_request_new().
+ *
+ * Return value: (transfer full): the new %SoupMessage
+ **/
+SoupMessage *
+soup_form_request_new_from_hash (const char *method, const char *uri,
+ GHashTable *form_data_set)
+{
+ return soup_form_request_for_data (
+ method, uri, soup_form_encode_hash (form_data_set));
+}
+
+/**
+ * soup_form_request_new_from_datalist:
+ * @method: the HTTP method, either "GET" or "POST"
+ * @uri: the URI to send the form data to
+ * @form_data_set: the data to send to @uri
+ *
+ * Creates a new %SoupMessage and sets it up to send @form_data_set to
+ * @uri via @method, as with soup_form_request_new().
+ *
+ * Return value: (transfer full): the new %SoupMessage
+ **/
+SoupMessage *
+soup_form_request_new_from_datalist (const char *method, const char *uri,
+ GData **form_data_set)
+{
+ return soup_form_request_for_data (
+ method, uri, soup_form_encode_datalist (form_data_set));
+}
+
+/**
+ * soup_form_request_new_from_multipart:
+ * @uri: the URI to send the form data to
+ * @multipart: a "multipart/form-data" #SoupMultipart
+ *
+ * Creates a new %SoupMessage and sets it up to send @multipart to
+ * @uri via POST.
+ *
+ * To send a <literal>"multipart/form-data"</literal> POST, first
+ * create a #SoupMultipart, using %SOUP_FORM_MIME_TYPE_MULTIPART as
+ * the MIME type. Then use soup_multipart_append_form_string() and
+ * soup_multipart_append_form_file() to add the value of each form
+ * control to the multipart. (These are just convenience methods, and
+ * you can use soup_multipart_append_part() if you need greater
+ * control over the part headers.) Finally, call
+ * soup_form_request_new_from_multipart() to serialize the multipart
+ * structure and create a #SoupMessage.
+ *
+ * Return value: (transfer full): the new %SoupMessage
+ *
+ * Since: 2.26
+ **/
+SoupMessage *
+soup_form_request_new_from_multipart (const char *uri,
+ SoupMultipart *multipart)
+{
+ SoupMessage *msg;
+
+ msg = soup_message_new ("POST", uri);
+ soup_multipart_to_message (multipart, msg->request_headers,
+ msg->request_body);
+ return msg;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_FORM_H
+#define SOUP_FORM_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-multipart.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_FORM_MIME_TYPE_URLENCODED "application/x-www-form-urlencoded"
+#define SOUP_FORM_MIME_TYPE_MULTIPART "multipart/form-data"
+
+GHashTable *soup_form_decode (const char *encoded_form);
+GHashTable *soup_form_decode_multipart (SoupMessage *msg,
+ const char *file_control_name,
+ char **filename,
+ char **content_type,
+ SoupBuffer **file);
+
+char *soup_form_encode (const char *first_field,
+ ...) G_GNUC_NULL_TERMINATED;
+char *soup_form_encode_hash (GHashTable *form_data_set);
+char *soup_form_encode_datalist (GData **form_data_set);
+char *soup_form_encode_valist (const char *first_field,
+ va_list args);
+
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+/* Compatibility with libsoup 2.3.0 */
+#define soup_form_decode_urlencoded soup_form_decode
+#define soup_form_encode_urlencoded soup_form_encode_hash
+#define soup_form_encode_urlencoded_list soup_form_encode_datalist
+#endif
+
+SoupMessage *soup_form_request_new (const char *method,
+ const char *uri,
+ const char *first_field,
+ ...) G_GNUC_NULL_TERMINATED;
+SoupMessage *soup_form_request_new_from_hash (const char *method,
+ const char *uri,
+ GHashTable *form_data_set);
+SoupMessage *soup_form_request_new_from_datalist (const char *method,
+ const char *uri,
+ GData **form_data_set);
+SoupMessage *soup_form_request_new_from_multipart (const char *uri,
+ SoupMultipart *multipart);
+
+G_END_DECLS
+
+#endif /* SOUP_FORM_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-gnome-features.c: GNOME-specific features
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-gnome-features.h"
+
+/**
+ * SOUP_TYPE_PROXY_RESOLVER_GNOME:
+ *
+ * This returns the #GType of a #SoupProxyResolver that can be used to
+ * resolve HTTP proxies for GNOME applications. You can add this to
+ * a session using soup_session_add_feature_by_type() or by using the
+ * %SOUP_SESSION_ADD_FEATURE_BY_TYPE construct-time property.
+ *
+ * This feature is included in %SOUP_TYPE_GNOME_FEATURES_2_26, so if
+ * you are using that feature, you do not need to include this feature
+ * separately.
+ *
+ * Since: 2.26
+ **/
+/* This is actually declared in soup-proxy-resolver-gnome now */
+
+/**
+ * SOUP_TYPE_GNOME_FEATURES_2_26:
+ *
+ * This returns the #GType of a #SoupSessionFeature that automatically
+ * adds all of the GNOME features defined for libsoup 2.26 (which is
+ * just %SOUP_TYPE_PROXY_RESOLVER_GNOME).
+ *
+ * You can add this to a session using
+ * soup_session_add_feature_by_type() or by using the
+ * %SOUP_SESSION_ADD_FEATURE_BY_TYPE construct-time property.
+ *
+ * Since: 2.26
+ **/
+GType
+soup_gnome_features_2_26_get_type (void)
+{
+ /* Eventually this needs to be a special SoupSessionFeature
+ * class that registers other features. But for now we can
+ * just do this:
+ */
+ return SOUP_TYPE_PROXY_RESOLVER_GNOME;
+}
+
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_GNOME_FEATURES_H
+#define SOUP_GNOME_FEATURES_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+GType soup_proxy_resolver_gnome_get_type (void);
+#define SOUP_TYPE_PROXY_RESOLVER_GNOME (soup_proxy_resolver_gnome_get_type ())
+
+GType soup_gnome_features_2_26_get_type (void);
+#define SOUP_TYPE_GNOME_FEATURES_2_26 (soup_gnome_features_2_26_get_type ())
+
+#ifndef G_OS_WIN32
+#ifdef LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+GType soup_password_manager_gnome_get_type (void);
+#define SOUP_TYPE_PASSWORD_MANAGER_GNOME (soup_password_manager_gnome_get_type ())
+#endif /* LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY */
+#endif
+
+G_END_DECLS
+
+#endif /* SOUP_GNOME_FEATURES_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_GNOME_H
+#define SOUP_GNOME_H 1
+
+#include <libsoup/soup.h>
+
+#include <libsoup/soup-cookie-jar-sqlite.h>
+#include <libsoup/soup-gnome-features.h>
+
+#endif /* SOUP_GNOME_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-headers.c: HTTP message header parsing
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "soup-headers.h"
+#include "soup-misc.h"
+#include "soup-uri.h"
+
+/**
+ * soup_headers_parse:
+ * @str: the header string (including the Request-Line or Status-Line,
+ * and the trailing blank line)
+ * @len: length of @str up to (but not including) the terminating blank line.
+ * @dest: #SoupMessageHeaders to store the header values in
+ *
+ * Parses the headers of an HTTP request or response in @str and
+ * stores the results in @dest. Beware that @dest may be modified even
+ * on failure.
+ *
+ * This is a low-level method; normally you would use
+ * soup_headers_parse_request() or soup_headers_parse_response().
+ *
+ * Return value: success or failure
+ *
+ * Since: 2.26
+ **/
+gboolean
+soup_headers_parse (const char *str, int len, SoupMessageHeaders *dest)
+{
+ const char *headers_start;
+ char *headers_copy, *name, *name_end, *value, *value_end;
+ char *eol, *sol;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (str != NULL, FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+
+ /* Technically, the grammar does allow NUL bytes in the
+ * headers, but this is probably a bug, and if it's not, we
+ * can't deal with them anyway.
+ */
+ if (memchr (str, '\0', len))
+ return FALSE;
+
+ /* As per RFC 2616 section 19.3, we treat '\n' as the
+ * line terminator, and '\r', if it appears, merely as
+ * ignorable trailing whitespace.
+ */
+
+ /* Skip over the Request-Line / Status-Line */
+ headers_start = memchr (str, '\n', len);
+ if (!headers_start)
+ return FALSE;
+
+ /* We work on a copy of the headers, which we can write '\0's
+ * into, so that we don't have to individually g_strndup and
+ * then g_free each header name and value.
+ */
+ headers_copy = g_strndup (headers_start, len - (headers_start - str));
+ value_end = headers_copy;
+
+ while (*(value_end + 1)) {
+ name = value_end + 1;
+ name_end = strchr (name, ':');
+ if (!name_end || name + strcspn (name, " \t\r\n") < name_end) {
+ /* Bad header; just ignore this line. Note
+ * that if it has continuation lines, we'll
+ * end up ignoring them too since they'll
+ * start with spaces.
+ */
+ value_end = strchr (name, '\n');
+ if (!value_end)
+ goto done;
+ continue;
+ }
+
+ /* Find the end of the value; ie, an end-of-line that
+ * isn't followed by a continuation line.
+ */
+ value = name_end + 1;
+ value_end = strchr (name, '\n');
+ if (!value_end)
+ goto done;
+ while (*(value_end + 1) == ' ' || *(value_end + 1) == '\t') {
+ value_end = strchr (value_end + 1, '\n');
+ if (!value_end)
+ goto done;
+ }
+
+ *name_end = '\0';
+ *value_end = '\0';
+
+ /* Skip leading whitespace */
+ while (value < value_end &&
+ (*value == ' ' || *value == '\t' ||
+ *value == '\r' || *value == '\n'))
+ value++;
+
+ /* Collapse continuation lines */
+ while ((eol = strchr (value, '\n'))) {
+ /* find start of next line */
+ sol = eol + 1;
+ while (*sol == ' ' || *sol == '\t')
+ sol++;
+
+ /* back up over trailing whitespace on current line */
+ while (eol[-1] == ' ' || eol[-1] == '\t' || eol[-1] == '\r')
+ eol--;
+
+ /* Delete all but one SP */
+ *eol = ' ';
+ g_memmove (eol + 1, sol, strlen (sol) + 1);
+ }
+
+ /* clip trailing whitespace */
+ eol = strchr (value, '\0');
+ while (eol > value &&
+ (eol[-1] == ' ' || eol[-1] == '\t' || eol[-1] == '\r'))
+ eol--;
+ *eol = '\0';
+
+ soup_message_headers_append (dest, name, value);
+ }
+ success = TRUE;
+
+done:
+ g_free (headers_copy);
+ return success;
+}
+
+/**
+ * soup_headers_parse_request:
+ * @str: the header string (including the trailing blank line)
+ * @len: length of @str up to (but not including) the terminating blank line.
+ * @req_headers: #SoupMessageHeaders to store the header values in
+ * @req_method: (out) (allow-none): if non-%NULL, will be filled in with the
+ * request method
+ * @req_path: (out) (allow-none): if non-%NULL, will be filled in with the
+ * request path
+ * @ver: (out) (allow-none): if non-%NULL, will be filled in with the HTTP
+ * version
+ *
+ * Parses the headers of an HTTP request in @str and stores the
+ * results in @req_method, @req_path, @ver, and @req_headers.
+ *
+ * Beware that @req_headers may be modified even on failure.
+ *
+ * Return value: %SOUP_STATUS_OK if the headers could be parsed, or an
+ * HTTP error to be returned to the client if they could not be.
+ **/
+guint
+soup_headers_parse_request (const char *str,
+ int len,
+ SoupMessageHeaders *req_headers,
+ char **req_method,
+ char **req_path,
+ SoupHTTPVersion *ver)
+{
+ const char *method, *method_end, *path, *path_end;
+ const char *version, *version_end, *headers;
+ unsigned long major_version, minor_version;
+ char *p;
+
+ g_return_val_if_fail (str && *str, SOUP_STATUS_MALFORMED);
+
+ /* RFC 2616 4.1 "servers SHOULD ignore any empty line(s)
+ * received where a Request-Line is expected."
+ */
+ while ((*str == '\r' || *str == '\n') && len > 0) {
+ str++;
+ len--;
+ }
+ if (!len)
+ return SOUP_STATUS_BAD_REQUEST;
+
+ /* RFC 2616 19.3 "[servers] SHOULD accept any amount of SP or
+ * HT characters between [Request-Line] fields"
+ */
+
+ method = method_end = str;
+ while (method_end < str + len && *method_end != ' ' && *method_end != '\t')
+ method_end++;
+ if (method_end >= str + len)
+ return SOUP_STATUS_BAD_REQUEST;
+
+ path = method_end;
+ while (path < str + len && (*path == ' ' || *path == '\t'))
+ path++;
+ if (path >= str + len)
+ return SOUP_STATUS_BAD_REQUEST;
+
+ path_end = path;
+ while (path_end < str + len && *path_end != ' ' && *path_end != '\t')
+ path_end++;
+ if (path_end >= str + len)
+ return SOUP_STATUS_BAD_REQUEST;
+
+ version = path_end;
+ while (version < str + len && (*version == ' ' || *version == '\t'))
+ version++;
+ if (version + 8 >= str + len)
+ return SOUP_STATUS_BAD_REQUEST;
+
+ if (strncmp (version, "HTTP/", 5) != 0 ||
+ !g_ascii_isdigit (version[5]))
+ return SOUP_STATUS_BAD_REQUEST;
+ major_version = strtoul (version + 5, &p, 10);
+ if (*p != '.' || !g_ascii_isdigit (p[1]))
+ return SOUP_STATUS_BAD_REQUEST;
+ minor_version = strtoul (p + 1, &p, 10);
+ version_end = p;
+ if (major_version != 1)
+ return SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED;
+ if (minor_version > 1)
+ return SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED;
+
+ headers = version_end;
+ while (headers < str + len && (*headers == '\r' || *headers == ' '))
+ headers++;
+ if (headers >= str + len || *headers != '\n')
+ return SOUP_STATUS_BAD_REQUEST;
+
+ if (!soup_headers_parse (str, len, req_headers))
+ return SOUP_STATUS_BAD_REQUEST;
+
+ if (soup_message_headers_get_expectations (req_headers) &
+ SOUP_EXPECTATION_UNRECOGNIZED)
+ return SOUP_STATUS_EXPECTATION_FAILED;
+ /* RFC 2616 14.10 */
+ if (minor_version == 0)
+ soup_message_headers_clean_connection_headers (req_headers);
+
+ if (req_method)
+ *req_method = g_strndup (method, method_end - method);
+ if (req_path)
+ *req_path = g_strndup (path, path_end - path);
+ if (ver)
+ *ver = (minor_version == 0) ? SOUP_HTTP_1_0 : SOUP_HTTP_1_1;
+
+ return SOUP_STATUS_OK;
+}
+
+/**
+ * soup_headers_parse_status_line:
+ * @status_line: an HTTP Status-Line
+ * @ver: (out) (allow-none): if non-%NULL, will be filled in with the HTTP
+ * version
+ * @status_code: (out) (allow-none): if non-%NULL, will be filled in with
+ * the status code
+ * @reason_phrase: (out) (allow-none): if non-%NULL, will be filled in with
+ * the reason phrase
+ *
+ * Parses the HTTP Status-Line string in @status_line into @ver,
+ * @status_code, and @reason_phrase. @status_line must be terminated by
+ * either "\0" or "\r\n".
+ *
+ * Return value: %TRUE if @status_line was parsed successfully.
+ **/
+gboolean
+soup_headers_parse_status_line (const char *status_line,
+ SoupHTTPVersion *ver,
+ guint *status_code,
+ char **reason_phrase)
+{
+ unsigned long major_version, minor_version, code;
+ const char *code_start, *code_end, *phrase_start, *phrase_end;
+ char *p;
+
+ g_return_val_if_fail (status_line != NULL, FALSE);
+
+ if (strncmp (status_line, "HTTP/", 5) == 0 &&
+ g_ascii_isdigit (status_line[5])) {
+ major_version = strtoul (status_line + 5, &p, 10);
+ if (*p != '.' || !g_ascii_isdigit (p[1]))
+ return FALSE;
+ minor_version = strtoul (p + 1, &p, 10);
+ if (major_version != 1)
+ return FALSE;
+ if (minor_version > 1)
+ return FALSE;
+ if (ver)
+ *ver = (minor_version == 0) ? SOUP_HTTP_1_0 : SOUP_HTTP_1_1;
+ } else if (!strncmp (status_line, "ICY", 3)) {
+ /* Shoutcast not-quite-HTTP format */
+ if (ver)
+ *ver = SOUP_HTTP_1_0;
+ p = (char *)status_line + 3;
+ } else
+ return FALSE;
+
+ code_start = p;
+ while (*code_start == ' ' || *code_start == '\t')
+ code_start++;
+ code_end = code_start;
+ while (*code_end >= '0' && *code_end <= '9')
+ code_end++;
+ if (code_end != code_start + 3)
+ return FALSE;
+ code = atoi (code_start);
+ if (code < 100 || code > 599)
+ return FALSE;
+ if (status_code)
+ *status_code = code;
+
+ phrase_start = code_end;
+ while (*phrase_start == ' ' || *phrase_start == '\t')
+ phrase_start++;
+ phrase_end = phrase_start + strcspn (phrase_start, "\n");
+ while (phrase_end > phrase_start &&
+ (phrase_end[-1] == '\r' || phrase_end[-1] == ' ' || phrase_end[-1] == '\t'))
+ phrase_end--;
+ if (reason_phrase)
+ *reason_phrase = g_strndup (phrase_start, phrase_end - phrase_start);
+
+ return TRUE;
+}
+
+/**
+ * soup_headers_parse_response:
+ * @str: the header string (including the trailing blank line)
+ * @len: length of @str up to (but not including) the terminating blank line.
+ * @headers: #SoupMessageheaders to store the header values in
+ * @ver: (out) (allow-none): if non-%NULL, will be filled in with the HTTP
+ * version
+ * @status_code: (out) (allow-none): if non-%NULL, will be filled in with
+ * the status code
+ * @reason_phrase: (out) (allow-none): if non-%NULL, will be filled in with
+ * the reason phrase
+ *
+ * Parses the headers of an HTTP response in @str and stores the
+ * results in @ver, @status_code, @reason_phrase, and @headers.
+ *
+ * Beware that @headers may be modified even on failure.
+ *
+ * Return value: success or failure.
+ **/
+gboolean
+soup_headers_parse_response (const char *str,
+ int len,
+ SoupMessageHeaders *headers,
+ SoupHTTPVersion *ver,
+ guint *status_code,
+ char **reason_phrase)
+{
+ SoupHTTPVersion version;
+
+ g_return_val_if_fail (str && *str, FALSE);
+
+ /* Workaround for broken servers that send extra line breaks
+ * after a response, which we then see prepended to the next
+ * response on that connection.
+ */
+ while ((*str == '\r' || *str == '\n') && len > 0) {
+ str++;
+ len--;
+ }
+ if (!len)
+ return FALSE;
+
+ if (!soup_headers_parse (str, len, headers))
+ return FALSE;
+
+ if (!soup_headers_parse_status_line (str,
+ &version,
+ status_code,
+ reason_phrase))
+ return FALSE;
+ if (ver)
+ *ver = version;
+
+ /* RFC 2616 14.10 */
+ if (version == SOUP_HTTP_1_0)
+ soup_message_headers_clean_connection_headers (headers);
+
+ return TRUE;
+}
+
+
+/*
+ * Parsing of specific HTTP header types
+ */
+
+static const char *
+skip_lws (const char *s)
+{
+ while (g_ascii_isspace (*s))
+ s++;
+ return s;
+}
+
+static const char *
+unskip_lws (const char *s, const char *start)
+{
+ while (s > start && g_ascii_isspace (*(s - 1)))
+ s--;
+ return s;
+}
+
+static const char *
+skip_delims (const char *s, char delim)
+{
+ /* The grammar allows for multiple delimiters */
+ while (g_ascii_isspace (*s) || *s == delim)
+ s++;
+ return s;
+}
+
+static const char *
+skip_item (const char *s, char delim)
+{
+ gboolean quoted = FALSE;
+ const char *start = s;
+
+ /* A list item ends at the last non-whitespace character
+ * before a delimiter which is not inside a quoted-string. Or
+ * at the end of the string.
+ */
+
+ while (*s) {
+ if (*s == '"')
+ quoted = !quoted;
+ else if (quoted) {
+ if (*s == '\\' && *(s + 1))
+ s++;
+ } else {
+ if (*s == delim)
+ break;
+ }
+ s++;
+ }
+
+ return unskip_lws (s, start);
+}
+
+static GSList *
+parse_list (const char *header, char delim)
+{
+ GSList *list = NULL;
+ const char *end;
+
+ header = skip_delims (header, delim);
+ while (*header) {
+ end = skip_item (header, delim);
+ list = g_slist_prepend (list, g_strndup (header, end - header));
+ header = skip_delims (end, delim);
+ }
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * soup_header_parse_list:
+ * @header: a header value
+ *
+ * Parses a header whose content is described by RFC2616 as
+ * "#something", where "something" does not itself contain commas,
+ * except as part of quoted-strings.
+ *
+ * Return value: (transfer full) (element-type utf8): a #GSList of
+ * list elements, as allocated strings
+ **/
+GSList *
+soup_header_parse_list (const char *header)
+{
+ g_return_val_if_fail (header != NULL, NULL);
+
+ return parse_list (header, ',');
+}
+
+typedef struct {
+ char *item;
+ double qval;
+} QualityItem;
+
+static int
+sort_by_qval (const void *a, const void *b)
+{
+ QualityItem *qia = (QualityItem *)a;
+ QualityItem *qib = (QualityItem *)b;
+
+ if (qia->qval == qib->qval)
+ return 0;
+ else if (qia->qval < qib->qval)
+ return 1;
+ else
+ return -1;
+}
+
+/**
+ * soup_header_parse_quality_list:
+ * @header: a header value
+ * @unacceptable: (out) (allow-none) (transfer full) (element-type utf8): on
+ * return, will contain a list of unacceptable values
+ *
+ * Parses a header whose content is a list of items with optional
+ * "qvalue"s (eg, Accept, Accept-Charset, Accept-Encoding,
+ * Accept-Language, TE).
+ *
+ * If @unacceptable is not %NULL, then on return, it will contain the
+ * items with qvalue 0. Either way, those items will be removed from
+ * the main list.
+ *
+ * Return value: (transfer full) (element-type utf8): a #GSList of
+ * acceptable values (as allocated strings), highest-qvalue first.
+ **/
+GSList *
+soup_header_parse_quality_list (const char *header, GSList **unacceptable)
+{
+ GSList *unsorted;
+ QualityItem *array;
+ GSList *sorted, *iter;
+ char *item, *semi;
+ const char *param, *equal, *value;
+ double qval;
+ int n;
+
+ g_return_val_if_fail (header != NULL, NULL);
+
+ if (unacceptable)
+ *unacceptable = NULL;
+
+ unsorted = soup_header_parse_list (header);
+ array = g_new0 (QualityItem, g_slist_length (unsorted));
+ for (iter = unsorted, n = 0; iter; iter = iter->next) {
+ item = iter->data;
+ qval = 1.0;
+ for (semi = strchr (item, ';'); semi; semi = strchr (semi + 1, ';')) {
+ param = skip_lws (semi + 1);
+ if (*param != 'q')
+ continue;
+ equal = skip_lws (param + 1);
+ if (!equal || *equal != '=')
+ continue;
+ value = skip_lws (equal + 1);
+ if (!value)
+ continue;
+
+ if (value[0] != '0' && value[0] != '1')
+ continue;
+ qval = (double)(value[0] - '0');
+ if (value[0] == '0' && value[1] == '.') {
+ if (g_ascii_isdigit (value[2])) {
+ qval += (double)(value[2] - '0') / 10;
+ if (g_ascii_isdigit (value[3])) {
+ qval += (double)(value[3] - '0') / 100;
+ if (g_ascii_isdigit (value[4]))
+ qval += (double)(value[4] - '0') / 1000;
+ }
+ }
+ }
+
+ *semi = '\0';
+ break;
+ }
+
+ if (qval == 0.0) {
+ if (unacceptable) {
+ *unacceptable = g_slist_prepend (*unacceptable,
+ item);
+ }
+ } else {
+ array[n].item = item;
+ array[n].qval = qval;
+ n++;
+ }
+ }
+ g_slist_free (unsorted);
+
+ qsort (array, n, sizeof (QualityItem), sort_by_qval);
+ sorted = NULL;
+ while (n--)
+ sorted = g_slist_prepend (sorted, array[n].item);
+ g_free (array);
+
+ return sorted;
+}
+
+/**
+ * soup_header_free_list: (skip)
+ * @list: a #GSList returned from soup_header_parse_list() or
+ * soup_header_parse_quality_list()
+ *
+ * Frees @list.
+ **/
+void
+soup_header_free_list (GSList *list)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next)
+ g_free (l->data);
+ g_slist_free (list);
+}
+
+/**
+ * soup_header_contains:
+ * @header: An HTTP header suitable for parsing with
+ * soup_header_parse_list()
+ * @token: a token
+ *
+ * Parses @header to see if it contains the token @token (matched
+ * case-insensitively). Note that this can't be used with lists
+ * that have qvalues.
+ *
+ * Return value: whether or not @header contains @token
+ **/
+gboolean
+soup_header_contains (const char *header, const char *token)
+{
+ const char *end;
+ guint len = strlen (token);
+
+ g_return_val_if_fail (header != NULL, FALSE);
+ g_return_val_if_fail (token != NULL, FALSE);
+
+ header = skip_delims (header, ',');
+ while (*header) {
+ end = skip_item (header, ',');
+ if (end - header == len &&
+ !g_ascii_strncasecmp (header, token, len))
+ return TRUE;
+ header = skip_delims (end, ',');
+ }
+
+ return FALSE;
+}
+
+static void
+decode_quoted_string (char *quoted_string)
+{
+ char *src, *dst;
+
+ src = quoted_string + 1;
+ dst = quoted_string;
+ while (*src && *src != '"') {
+ if (*src == '\\' && *(src + 1))
+ src++;
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+}
+
+static gboolean
+decode_rfc5987 (char *encoded_string)
+{
+ char *q, *decoded;
+ gboolean iso_8859_1 = FALSE;
+
+ q = strchr (encoded_string, '\'');
+ if (!q)
+ return FALSE;
+ if (g_ascii_strncasecmp (encoded_string, "UTF-8",
+ q - encoded_string) == 0)
+ ;
+ else if (g_ascii_strncasecmp (encoded_string, "iso-8859-1",
+ q - encoded_string) == 0)
+ iso_8859_1 = TRUE;
+ else
+ return FALSE;
+
+ q = strchr (q + 1, '\'');
+ if (!q)
+ return FALSE;
+
+ decoded = soup_uri_decode (q + 1);
+ if (iso_8859_1) {
+ char *utf8 = g_convert_with_fallback (decoded, -1, "UTF-8",
+ "iso-8859-1", "_",
+ NULL, NULL, NULL);
+ g_free (decoded);
+ if (!utf8)
+ return FALSE;
+ decoded = utf8;
+ }
+
+ /* If encoded_string was UTF-8, then each 3-character %-escape
+ * will be converted to a single byte, and so decoded is
+ * shorter than encoded_string. If encoded_string was
+ * iso-8859-1, then each 3-character %-escape will be
+ * converted into at most 2 bytes in UTF-8, and so it's still
+ * shorter.
+ */
+ strcpy (encoded_string, decoded);
+ g_free (decoded);
+ return TRUE;
+}
+
+static GHashTable *
+parse_param_list (const char *header, char delim)
+{
+ GHashTable *params;
+ GSList *list, *iter;
+ char *item, *eq, *name_end, *value;
+ gboolean override;
+
+ list = parse_list (header, delim);
+ if (!list)
+ return NULL;
+
+ params = g_hash_table_new_full (soup_str_case_hash,
+ soup_str_case_equal,
+ g_free, NULL);
+
+ for (iter = list; iter; iter = iter->next) {
+ item = iter->data;
+ override = FALSE;
+
+ eq = strchr (item, '=');
+ if (eq) {
+ name_end = (char *)unskip_lws (eq, item);
+ if (name_end == item) {
+ /* That's no good... */
+ g_free (item);
+ continue;
+ }
+
+ *name_end = '\0';
+
+ value = (char *)skip_lws (eq + 1);
+
+ if (name_end[-1] == '*' && name_end > item + 1) {
+ name_end[-1] = '\0';
+ if (!decode_rfc5987 (value)) {
+ g_free (item);
+ continue;
+ }
+ override = TRUE;
+ } else if (*value == '"')
+ decode_quoted_string (value);
+ } else
+ value = NULL;
+
+ if (override || !g_hash_table_lookup (params, item))
+ g_hash_table_replace (params, item, value);
+ else
+ g_free (item);
+ }
+
+ g_slist_free (list);
+ return params;
+}
+
+/**
+ * soup_header_parse_param_list:
+ * @header: a header value
+ *
+ * Parses a header which is a comma-delimited list of something like:
+ * <literal>token [ "=" ( token | quoted-string ) ]</literal>.
+ *
+ * Tokens that don't have an associated value will still be added to
+ * the resulting hash table, but with a %NULL value.
+ *
+ * This also handles RFC5987 encoding (which in HTTP is mostly used
+ * for giving UTF8-encoded filenames in the Content-Disposition
+ * header).
+ *
+ * Return value: (element-type utf8 utf8) (transfer full): a
+ * #GHashTable of list elements, which can be freed with
+ * soup_header_free_param_list().
+ **/
+GHashTable *
+soup_header_parse_param_list (const char *header)
+{
+ g_return_val_if_fail (header != NULL, NULL);
+
+ return parse_param_list (header, ',');
+}
+
+/**
+ * soup_header_parse_semi_param_list:
+ * @header: a header value
+ *
+ * Parses a header which is a semicolon-delimited list of something
+ * like: <literal>token [ "=" ( token | quoted-string ) ]</literal>.
+ *
+ * Tokens that don't have an associated value will still be added to
+ * the resulting hash table, but with a %NULL value.
+ *
+ * This also handles RFC5987 encoding (which in HTTP is mostly used
+ * for giving UTF8-encoded filenames in the Content-Disposition
+ * header).
+ *
+ * Return value: (element-type utf8 utf8) (transfer full): a
+ * #GHashTable of list elements, which can be freed with
+ * soup_header_free_param_list().
+ *
+ * Since: 2.24
+ **/
+GHashTable *
+soup_header_parse_semi_param_list (const char *header)
+{
+ g_return_val_if_fail (header != NULL, NULL);
+
+ return parse_param_list (header, ';');
+}
+
+/**
+ * soup_header_free_param_list:
+ * @param_list: (element-type utf8 utf8): a #GHashTable returned from soup_header_parse_param_list()
+ * or soup_header_parse_semi_param_list()
+ *
+ * Frees @param_list.
+ **/
+void
+soup_header_free_param_list (GHashTable *param_list)
+{
+ g_return_if_fail (param_list != NULL);
+
+ g_hash_table_destroy (param_list);
+}
+
+static void
+append_param_rfc5987 (GString *string,
+ const char *name,
+ const char *value)
+{
+ char *encoded;
+
+ g_string_append (string, name);
+ g_string_append (string, "*=UTF-8''");
+ encoded = soup_uri_encode (value, " *'%()<>@,;:\\\"/[]?=");
+ g_string_append (string, encoded);
+ g_free (encoded);
+}
+
+static void
+append_param_quoted (GString *string,
+ const char *name,
+ const char *value)
+{
+ int len;
+
+ g_string_append (string, name);
+ g_string_append (string, "=\"");
+ while (*value) {
+ while (*value == '\\' || *value == '"') {
+ g_string_append_c (string, '\\');
+ g_string_append_c (string, *value++);
+ }
+ len = strcspn (value, "\\\"");
+ g_string_append_len (string, value, len);
+ value += len;
+ }
+ g_string_append_c (string, '"');
+}
+
+static void
+append_param_internal (GString *string,
+ const char *name,
+ const char *value,
+ gboolean allow_token)
+{
+ const char *v;
+ gboolean use_token = allow_token;
+
+ for (v = value; *v; v++) {
+ if (*v & 0x80) {
+ if (g_utf8_validate (value, -1, NULL)) {
+ append_param_rfc5987 (string, name, value);
+ return;
+ } else {
+ use_token = FALSE;
+ break;
+ }
+ } else if (!soup_char_is_token (*v))
+ use_token = FALSE;
+ }
+
+ if (use_token) {
+ g_string_append (string, name);
+ g_string_append_c (string, '=');
+ g_string_append (string, value);
+ } else
+ append_param_quoted (string, name, value);
+}
+
+/**
+ * soup_header_g_string_append_param_quoted:
+ * @string: a #GString being used to construct an HTTP header value
+ * @name: a parameter name
+ * @value: a parameter value
+ *
+ * Appends something like <literal>@name="@value"</literal> to
+ * @string, taking care to escape any quotes or backslashes in @value.
+ *
+ * If @value is (non-ASCII) UTF-8, this will instead use RFC 5987
+ * encoding, just like soup_header_g_string_append_param().
+ *
+ * Since: 2.30
+ **/
+void
+soup_header_g_string_append_param_quoted (GString *string,
+ const char *name,
+ const char *value)
+{
+ g_return_if_fail (string != NULL);
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (value != NULL);
+
+ append_param_internal (string, name, value, FALSE);
+}
+
+/**
+ * soup_header_g_string_append_param:
+ * @string: a #GString being used to construct an HTTP header value
+ * @name: a parameter name
+ * @value: a parameter value, or %NULL
+ *
+ * Appends something like <literal>@name=@value</literal> to @string,
+ * taking care to quote @value if needed, and if so, to escape any
+ * quotes or backslashes in @value.
+ *
+ * Alternatively, if @value is a non-ASCII UTF-8 string, it will be
+ * appended using RFC5987 syntax. Although in theory this is supposed
+ * to work anywhere in HTTP that uses this style of parameter, in
+ * reality, it can only be used portably with the Content-Disposition
+ * "filename" parameter.
+ *
+ * If @value is %NULL, this will just append @name to @string.
+ *
+ * Since: 2.26
+ **/
+void
+soup_header_g_string_append_param (GString *string,
+ const char *name,
+ const char *value)
+{
+ g_return_if_fail (string != NULL);
+ g_return_if_fail (name != NULL);
+
+ if (!value) {
+ g_string_append (string, name);
+ return;
+ }
+
+ append_param_internal (string, name, value, TRUE);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_HEADERS_H
+#define SOUP_HEADERS_H 1
+
+#include <glib.h>
+#include <libsoup/soup-message.h>
+
+G_BEGIN_DECLS
+
+/* HTTP Header Parsing */
+
+gboolean soup_headers_parse (const char *str,
+ int len,
+ SoupMessageHeaders *dest);
+
+guint soup_headers_parse_request (const char *str,
+ int len,
+ SoupMessageHeaders *req_headers,
+ char **req_method,
+ char **req_path,
+ SoupHTTPVersion *ver);
+
+gboolean soup_headers_parse_status_line (const char *status_line,
+ SoupHTTPVersion *ver,
+ guint *status_code,
+ char **reason_phrase);
+
+gboolean soup_headers_parse_response (const char *str,
+ int len,
+ SoupMessageHeaders *headers,
+ SoupHTTPVersion *ver,
+ guint *status_code,
+ char **reason_phrase);
+
+/* Individual header parsing */
+
+GSList *soup_header_parse_list (const char *header);
+GSList *soup_header_parse_quality_list (const char *header,
+ GSList **unacceptable);
+void soup_header_free_list (GSList *list);
+
+gboolean soup_header_contains (const char *header,
+ const char *token);
+
+GHashTable *soup_header_parse_param_list (const char *header);
+GHashTable *soup_header_parse_semi_param_list (const char *header);
+void soup_header_free_param_list (GHashTable *param_list);
+
+void soup_header_g_string_append_param (GString *string,
+ const char *name,
+ const char *value);
+void soup_header_g_string_append_param_quoted (GString *string,
+ const char *name,
+ const char *value);
+
+G_END_DECLS
+
+#endif /*SOUP_HEADERS_H*/
--- /dev/null
+/* soup-input-stream.c, based on gsocketinputstream.c
+ *
+ * Copyright (C) 2006-2007, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "soup-http-input-stream.h"
+#include "soup-session.h"
+
+static void soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface);
+
+G_DEFINE_TYPE_WITH_CODE (SoupHTTPInputStream, soup_http_input_stream, G_TYPE_INPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+ soup_http_input_stream_seekable_iface_init))
+
+typedef void (*SoupHTTPInputStreamCallback)(GInputStream *);
+
+typedef struct {
+ SoupSession *session;
+ GMainContext *async_context;
+ SoupMessage *msg;
+ gboolean got_headers, finished;
+ goffset offset;
+
+ GCancellable *cancellable;
+ GSource *cancel_watch;
+ SoupHTTPInputStreamCallback got_headers_cb;
+ SoupHTTPInputStreamCallback got_chunk_cb;
+ SoupHTTPInputStreamCallback finished_cb;
+ SoupHTTPInputStreamCallback cancelled_cb;
+
+ guchar *leftover_buffer;
+ gsize leftover_bufsize, leftover_offset;
+
+ guchar *caller_buffer;
+ gsize caller_bufsize, caller_nread;
+ GAsyncReadyCallback outstanding_callback;
+ GSimpleAsyncResult *result;
+} SoupHTTPInputStreamPrivate;
+#define SOUP_HTTP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStreamPrivate))
+
+
+static gssize soup_http_input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean soup_http_input_stream_close (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+static void soup_http_input_stream_read_async (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data);
+static gssize soup_http_input_stream_read_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error);
+static void soup_http_input_stream_close_async (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data);
+static gboolean soup_http_input_stream_close_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error);
+
+static goffset soup_http_input_stream_tell (GSeekable *seekable);
+
+static gboolean soup_http_input_stream_can_seek (GSeekable *seekable);
+static gboolean soup_http_input_stream_seek (GSeekable *seekable,
+ goffset offset,
+ GSeekType type,
+ GCancellable *cancellable,
+ GError **error);
+
+static gboolean soup_http_input_stream_can_truncate (GSeekable *seekable);
+static gboolean soup_http_input_stream_truncate (GSeekable *seekable,
+ goffset offset,
+ GCancellable *cancellable,
+ GError **error);
+
+static void soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream);
+static void soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer stream);
+static void soup_http_input_stream_finished (SoupMessage *msg, gpointer stream);
+
+static void
+soup_http_input_stream_finalize (GObject *object)
+{
+ SoupHTTPInputStream *stream = SOUP_HTTP_INPUT_STREAM (object);
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_object_unref (priv->session);
+
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_http_input_stream_got_headers), stream);
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_http_input_stream_got_chunk), stream);
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_http_input_stream_finished), stream);
+ g_object_unref (priv->msg);
+ g_free (priv->leftover_buffer);
+
+ if (G_OBJECT_CLASS (soup_http_input_stream_parent_class)->finalize)
+ (*G_OBJECT_CLASS (soup_http_input_stream_parent_class)->finalize)(object);
+}
+
+static void
+soup_http_input_stream_class_init (SoupHTTPInputStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (SoupHTTPInputStreamPrivate));
+
+ gobject_class->finalize = soup_http_input_stream_finalize;
+
+ stream_class->read_fn = soup_http_input_stream_read;
+ stream_class->close_fn = soup_http_input_stream_close;
+ stream_class->read_async = soup_http_input_stream_read_async;
+ stream_class->read_finish = soup_http_input_stream_read_finish;
+ stream_class->close_async = soup_http_input_stream_close_async;
+ stream_class->close_finish = soup_http_input_stream_close_finish;
+}
+
+static void
+soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface)
+{
+ seekable_iface->tell = soup_http_input_stream_tell;
+ seekable_iface->can_seek = soup_http_input_stream_can_seek;
+ seekable_iface->seek = soup_http_input_stream_seek;
+ seekable_iface->can_truncate = soup_http_input_stream_can_truncate;
+ seekable_iface->truncate_fn = soup_http_input_stream_truncate;
+}
+
+static void
+soup_http_input_stream_init (SoupHTTPInputStream *stream)
+{
+ ;
+}
+
+static void
+soup_http_input_stream_queue_message (SoupHTTPInputStream *stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->got_headers = priv->finished = FALSE;
+
+ /* Add an extra ref since soup_session_queue_message steals one */
+ g_object_ref (priv->msg);
+ soup_session_queue_message (priv->session, priv->msg, NULL, NULL);
+}
+
+/**
+ * soup_http_input_stream_new:
+ * @session: the #SoupSession to use
+ * @msg: the #SoupMessage whose response will be streamed
+ *
+ * Prepares to send @msg over @session, and returns a #GInputStream
+ * that can be used to read the response.
+ *
+ * @msg may not be sent until the first read call; if you need to look
+ * at the status code or response headers before reading the body, you
+ * can use soup_http_input_stream_send() or soup_http_input_stream_send_async()
+ * to force the message to be sent and the response headers read.
+ *
+ * If @msg gets a non-2xx result, the first read (or send) will return
+ * an error with type %SOUP_HTTP_INPUT_STREAM_HTTP_ERROR.
+ *
+ * Internally, #SoupHTTPInputStream is implemented using asynchronous I/O,
+ * so if you are using the synchronous API (eg,
+ * g_input_stream_read()), you should create a new #GMainContext and
+ * set it as the %SOUP_SESSION_ASYNC_CONTEXT property on @session. (If
+ * you don't, then synchronous #GInputStream calls will cause the main
+ * loop to be run recursively.) The async #GInputStream API works fine
+ * with %SOUP_SESSION_ASYNC_CONTEXT either set or unset.
+ *
+ * Returns: a new #GInputStream.
+ **/
+SoupHTTPInputStream *
+soup_http_input_stream_new (SoupSession *session, SoupMessage *msg)
+{
+ SoupHTTPInputStream *stream;
+ SoupHTTPInputStreamPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ stream = g_object_new (SOUP_TYPE_HTTP_INPUT_STREAM, NULL);
+ priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->session = g_object_ref (session);
+ priv->async_context = soup_session_get_async_context (session);
+ priv->msg = g_object_ref (msg);
+
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (soup_http_input_stream_got_headers), stream);
+ g_signal_connect (msg, "got_chunk",
+ G_CALLBACK (soup_http_input_stream_got_chunk), stream);
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (soup_http_input_stream_finished), stream);
+
+ soup_http_input_stream_queue_message (stream);
+ return stream;
+}
+
+static void
+soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ /* If the status is unsuccessful, we just ignore the signal and let
+ * libsoup keep going (eventually either it will requeue the request
+ * (after handling authentication/redirection), or else the
+ * "finished" handler will run).
+ */
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ return;
+
+ priv->got_headers = TRUE;
+ if (!priv->caller_buffer) {
+ /* Not ready to read the body yet */
+ soup_session_pause_message (priv->session, msg);
+ }
+
+ if (priv->got_headers_cb)
+ priv->got_headers_cb (stream);
+}
+
+static void
+soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer,
+ gpointer stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ const gchar *chunk = chunk_buffer->data;
+ gsize chunk_size = chunk_buffer->length;
+
+ /* We only pay attention to the chunk if it's part of a successful
+ * response.
+ */
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ return;
+
+ /* Sanity check */
+ if (priv->caller_bufsize == 0 || priv->leftover_bufsize != 0)
+ g_warning ("soup_http_input_stream_got_chunk called again before previous chunk was processed");
+
+ /* Copy what we can into priv->caller_buffer */
+ if (priv->caller_bufsize > priv->caller_nread) {
+ gsize nread = MIN (chunk_size, priv->caller_bufsize - priv->caller_nread);
+
+ memcpy (priv->caller_buffer + priv->caller_nread, chunk, nread);
+ priv->caller_nread += nread;
+ priv->offset += nread;
+ chunk += nread;
+ chunk_size -= nread;
+ }
+
+ if (chunk_size > 0) {
+ /* Copy the rest into priv->leftover_buffer. If
+ * there's already some data there, realloc and
+ * append. Otherwise just copy.
+ */
+ if (priv->leftover_bufsize) {
+ priv->leftover_buffer = g_realloc (priv->leftover_buffer,
+ priv->leftover_bufsize + chunk_size);
+ memcpy (priv->leftover_buffer + priv->leftover_bufsize,
+ chunk, chunk_size);
+ priv->leftover_bufsize += chunk_size;
+ } else {
+ priv->leftover_bufsize = chunk_size;
+ priv->leftover_buffer = g_memdup (chunk, chunk_size);
+ priv->leftover_offset = 0;
+ }
+ }
+
+ soup_session_pause_message (priv->session, msg);
+ if (priv->got_chunk_cb)
+ priv->got_chunk_cb (stream);
+}
+
+static void
+soup_http_input_stream_finished (SoupMessage *msg, gpointer stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->finished = TRUE;
+
+ if (priv->finished_cb)
+ priv->finished_cb (stream);
+}
+
+static gboolean
+soup_http_input_stream_cancelled (GIOChannel *chan, GIOCondition condition,
+ gpointer stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->cancel_watch = NULL;
+
+ soup_session_pause_message (priv->session, priv->msg);
+ if (priv->cancelled_cb)
+ priv->cancelled_cb (stream);
+
+ return FALSE;
+}
+
+static void
+soup_http_input_stream_prepare_for_io (GInputStream *stream,
+ GCancellable *cancellable,
+ guchar *buffer,
+ gsize count)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ int cancel_fd;
+
+ priv->cancellable = cancellable;
+ cancel_fd = g_cancellable_get_fd (cancellable);
+ if (cancel_fd != -1) {
+ GIOChannel *chan = g_io_channel_unix_new (cancel_fd);
+ priv->cancel_watch = soup_add_io_watch (priv->async_context, chan,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ soup_http_input_stream_cancelled,
+ stream);
+ g_io_channel_unref (chan);
+ }
+
+ priv->caller_buffer = buffer;
+ priv->caller_bufsize = count;
+ priv->caller_nread = 0;
+
+ if (priv->got_headers)
+ soup_session_unpause_message (priv->session, priv->msg);
+}
+
+static void
+soup_http_input_stream_done_io (GInputStream *stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (priv->cancel_watch) {
+ g_source_destroy (priv->cancel_watch);
+ priv->cancel_watch = NULL;
+ g_cancellable_release_fd (priv->cancellable);
+ }
+ priv->cancellable = NULL;
+
+ priv->caller_buffer = NULL;
+ priv->caller_bufsize = 0;
+}
+
+static gboolean
+set_error_if_http_failed (SoupMessage *msg, GError **error)
+{
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ g_set_error_literal (error, SOUP_HTTP_ERROR,
+ msg->status_code, msg->reason_phrase);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gsize
+read_from_leftover (SoupHTTPInputStreamPrivate *priv,
+ gpointer buffer, gsize bufsize)
+{
+ gsize nread;
+
+ if (priv->leftover_bufsize - priv->leftover_offset <= bufsize) {
+ nread = priv->leftover_bufsize - priv->leftover_offset;
+ memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
+
+ g_free (priv->leftover_buffer);
+ priv->leftover_buffer = NULL;
+ priv->leftover_bufsize = priv->leftover_offset = 0;
+ } else {
+ nread = bufsize;
+ memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
+ priv->leftover_offset += nread;
+ }
+
+ priv->offset += nread;
+ return nread;
+}
+
+/* This does the work of soup_http_input_stream_send(), assuming that the
+ * GInputStream pending flag has already been set. It is also used by
+ * soup_http_input_stream_send_async() in some circumstances.
+ */
+static gboolean
+soup_http_input_stream_send_internal (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
+ while (!priv->finished && !priv->got_headers &&
+ !g_cancellable_is_cancelled (cancellable))
+ g_main_context_iteration (priv->async_context, TRUE);
+ soup_http_input_stream_done_io (stream);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+ else if (set_error_if_http_failed (priv->msg, error))
+ return FALSE;
+ return TRUE;
+}
+
+static void
+send_sync_finished (GInputStream *stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ GError *error = NULL;
+
+ if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
+ set_error_if_http_failed (priv->msg, &error);
+
+ priv->got_headers_cb = NULL;
+ priv->finished_cb = NULL;
+
+ /* Wake up the main context iteration */
+ soup_add_completion (priv->async_context, NULL, NULL);
+}
+
+/**
+ * soup_http_input_stream_send:
+ * @httpstream: a #SoupHTTPInputStream
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Synchronously sends the HTTP request associated with @stream, and
+ * reads the response headers. Call this after soup_http_input_stream_new()
+ * and before the first g_input_stream_read() if you want to check the
+ * HTTP status code before you start reading.
+ *
+ * Return value: %TRUE if msg has a successful (2xx) status, %FALSE if
+ * not.
+ **/
+gboolean
+soup_http_input_stream_send (SoupHTTPInputStream *httpstream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
+ GInputStream *istream = (GInputStream *)httpstream;
+ gboolean result;
+
+ g_return_val_if_fail (SOUP_IS_HTTP_INPUT_STREAM (httpstream), FALSE);
+
+ if (!g_input_stream_set_pending (istream, error))
+ return FALSE;
+
+ priv->got_headers_cb = send_sync_finished;
+ priv->finished_cb = send_sync_finished;
+
+ result = soup_http_input_stream_send_internal (istream, cancellable, error);
+ g_input_stream_clear_pending (istream);
+
+ return result;
+}
+
+static gssize
+soup_http_input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ /* If there is data leftover from a previous read, return it. */
+ if (priv->leftover_bufsize)
+ return read_from_leftover (priv, buffer, count);
+
+ if (priv->finished)
+ return 0;
+
+ /* No leftover data, accept one chunk from the network */
+ soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
+ while (!priv->finished && priv->caller_nread == 0 &&
+ !g_cancellable_is_cancelled (cancellable))
+ g_main_context_iteration (priv->async_context, TRUE);
+ soup_http_input_stream_done_io (stream);
+
+ if (priv->caller_nread > 0)
+ return priv->caller_nread;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return -1;
+ else if (set_error_if_http_failed (priv->msg, error))
+ return -1;
+ else
+ return 0;
+}
+
+static gboolean
+soup_http_input_stream_close (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (!priv->finished)
+ soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
+
+ return TRUE;
+}
+
+static void
+wrapper_callback (GObject *source_object, GAsyncResult *res,
+ gpointer user_data)
+{
+ GInputStream *stream = G_INPUT_STREAM (source_object);
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_input_stream_clear_pending (stream);
+ if (priv->outstanding_callback)
+ (*priv->outstanding_callback)(source_object, res, user_data);
+ priv->outstanding_callback = NULL;
+ g_object_unref (stream);
+}
+
+static void
+send_async_finished (GInputStream *stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+ GError *error = NULL;
+
+ if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error))
+ set_error_if_http_failed (priv->msg, &error);
+
+ priv->got_headers_cb = NULL;
+ priv->finished_cb = NULL;
+ soup_http_input_stream_done_io (stream);
+
+ result = priv->result;
+ priv->result = NULL;
+
+ g_simple_async_result_set_op_res_gboolean (result, error == NULL);
+ if (error) {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ }
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+}
+
+static void
+soup_http_input_stream_send_async_internal (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_return_if_fail (priv->async_context == g_main_context_get_thread_default ());
+
+ g_object_ref (stream);
+ priv->outstanding_callback = callback;
+
+ priv->got_headers_cb = send_async_finished;
+ priv->finished_cb = send_async_finished;
+
+ soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
+ priv->result = g_simple_async_result_new (G_OBJECT (stream),
+ wrapper_callback, user_data,
+ soup_http_input_stream_send_async);
+}
+
+/**
+ * soup_http_input_stream_send_async:
+ * @httpstream: a #SoupHTTPInputStream
+ * @io_priority: the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously sends the HTTP request associated with @stream, and
+ * reads the response headers. Call this after soup_http_input_stream_new()
+ * and before the first g_input_stream_read_async() if you want to
+ * check the HTTP status code before you start reading.
+ **/
+void
+soup_http_input_stream_send_async (SoupHTTPInputStream *httpstream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GInputStream *istream = (GInputStream *)httpstream;
+ GError *error = NULL;
+
+ g_return_if_fail (SOUP_IS_HTTP_INPUT_STREAM (httpstream));
+
+ if (!g_input_stream_set_pending (istream, &error)) {
+ g_simple_async_report_gerror_in_idle (G_OBJECT (httpstream),
+ callback,
+ user_data,
+ error);
+ g_error_free (error);
+ return;
+ }
+ soup_http_input_stream_send_async_internal (istream, io_priority, cancellable,
+ callback, user_data);
+}
+
+/**
+ * soup_http_input_stream_send_finish:
+ * @httpstream: a #SoupHTTPInputStream
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to
+ * ignore.
+ *
+ * Finishes a soup_http_input_stream_send_async() operation.
+ *
+ * Return value: %TRUE if the message was sent successfully and
+ * received a successful status code, %FALSE if not.
+ **/
+gboolean
+soup_http_input_stream_send_finish (SoupHTTPInputStream *httpstream,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == soup_http_input_stream_send_async, FALSE);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+static void
+read_async_done (GInputStream *stream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+ GError *error = NULL;
+
+ result = priv->result;
+ priv->result = NULL;
+
+ if (g_cancellable_set_error_if_cancelled (priv->cancellable, &error) ||
+ set_error_if_http_failed (priv->msg, &error)) {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ } else
+ g_simple_async_result_set_op_res_gssize (result, priv->caller_nread);
+
+ priv->got_chunk_cb = NULL;
+ priv->finished_cb = NULL;
+ priv->cancelled_cb = NULL;
+ soup_http_input_stream_done_io (stream);
+
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+}
+
+static void
+soup_http_input_stream_read_async (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+
+ g_return_if_fail (priv->async_context == g_main_context_get_thread_default ());
+
+ result = g_simple_async_result_new (G_OBJECT (stream),
+ callback, user_data,
+ soup_http_input_stream_read_async);
+
+ if (priv->leftover_bufsize) {
+ gsize nread = read_from_leftover (priv, buffer, count);
+ g_simple_async_result_set_op_res_gssize (result, nread);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
+ if (priv->finished) {
+ g_simple_async_result_set_op_res_gssize (result, 0);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
+ priv->result = result;
+
+ priv->got_chunk_cb = read_async_done;
+ priv->finished_cb = read_async_done;
+ priv->cancelled_cb = read_async_done;
+ soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count);
+}
+
+static gssize
+soup_http_input_stream_read_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), -1);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == soup_http_input_stream_read_async, -1);
+
+ return g_simple_async_result_get_op_res_gssize (simple);
+}
+
+static void
+soup_http_input_stream_close_async (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ gboolean success;
+ GError *error = NULL;
+
+ result = g_simple_async_result_new (G_OBJECT (stream),
+ callback, user_data,
+ soup_http_input_stream_close_async);
+ success = soup_http_input_stream_close (stream, cancellable, &error);
+ g_simple_async_result_set_op_res_gboolean (result, success);
+ if (error) {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
+static gboolean
+soup_http_input_stream_close_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ /* Failures handled in generic close_finish code */
+ return TRUE;
+}
+
+static goffset
+soup_http_input_stream_tell (GSeekable *seekable)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
+
+ return priv->offset;
+}
+
+static gboolean
+soup_http_input_stream_can_seek (GSeekable *seekable)
+{
+ return TRUE;
+}
+
+extern void soup_message_io_cleanup (SoupMessage *msg);
+
+static gboolean
+soup_http_input_stream_seek (GSeekable *seekable,
+ goffset offset,
+ GSeekType type,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GInputStream *stream = G_INPUT_STREAM (seekable);
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable);
+ char *range;
+
+ if (type == G_SEEK_END) {
+ /* FIXME: we could send "bytes=-offset", but unless we
+ * know the Content-Length, we wouldn't be able to
+ * answer a tell() properly. We could find the
+ * Content-Length by doing a HEAD...
+ */
+
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "G_SEEK_END not currently supported");
+ return FALSE;
+ }
+
+ if (!g_input_stream_set_pending (stream, error))
+ return FALSE;
+
+ soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED);
+ soup_message_io_cleanup (priv->msg);
+
+ switch (type) {
+ case G_SEEK_CUR:
+ offset += priv->offset;
+ /* fall through */
+
+ case G_SEEK_SET:
+ range = g_strdup_printf ("bytes=%" G_GUINT64_FORMAT "-", (guint64)offset);
+ priv->offset = offset;
+ break;
+
+ case G_SEEK_END:
+ range = NULL; /* keep compilers happy */
+ g_return_val_if_reached (FALSE);
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ soup_message_headers_remove (priv->msg->request_headers, "Range");
+ soup_message_headers_append (priv->msg->request_headers, "Range", range);
+ g_free (range);
+
+ soup_http_input_stream_queue_message (SOUP_HTTP_INPUT_STREAM (stream));
+
+ g_input_stream_clear_pending (stream);
+ return TRUE;
+}
+
+static gboolean
+soup_http_input_stream_can_truncate (GSeekable *seekable)
+{
+ return FALSE;
+}
+
+static gboolean
+soup_http_input_stream_truncate (GSeekable *seekable,
+ goffset offset,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Truncate not allowed on input stream");
+ return FALSE;
+}
+
+SoupMessage *
+soup_http_input_stream_get_message (SoupHTTPInputStream *httpstream)
+{
+ SoupHTTPInputStreamPrivate *priv = SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream);
+ return priv->msg ? g_object_ref (priv->msg) : NULL;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, 2007, 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SOUP_HTTP_INPUT_STREAM_H__
+#define __SOUP_HTTP_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_HTTP_INPUT_STREAM (soup_http_input_stream_get_type ())
+#define SOUP_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStream))
+#define SOUP_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStreamClass))
+#define SOUP_IS_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SOUP_TYPE_HTTP_INPUT_STREAM))
+#define SOUP_IS_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SOUP_TYPE_HTTP_INPUT_STREAM))
+#define SOUP_HTTP_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStreamClass))
+
+typedef struct SoupHTTPInputStream SoupHTTPInputStream;
+typedef struct SoupHTTPInputStreamClass SoupHTTPInputStreamClass;
+
+struct SoupHTTPInputStream {
+ GInputStream parent;
+};
+
+struct SoupHTTPInputStreamClass {
+ GInputStreamClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_g_reserved1)(void);
+ void (*_g_reserved2)(void);
+ void (*_g_reserved3)(void);
+ void (*_g_reserved4)(void);
+ void (*_g_reserved5)(void);
+};
+
+GType soup_http_input_stream_get_type (void) G_GNUC_CONST;
+
+SoupHTTPInputStream *soup_http_input_stream_new (SoupSession *session,
+ SoupMessage *msg);
+
+gboolean soup_http_input_stream_send (SoupHTTPInputStream *httpstream,
+ GCancellable *cancellable,
+ GError **error);
+
+void soup_http_input_stream_send_async (SoupHTTPInputStream *httpstream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean soup_http_input_stream_send_finish (SoupHTTPInputStream *httpstream,
+ GAsyncResult *result,
+ GError **error);
+
+SoupMessage *soup_http_input_stream_get_message (SoupHTTPInputStream *httpstream);
+
+G_END_DECLS
+
+#endif /* __SOUP_HTTP_INPUT_STREAM_H__ */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-logger.c
+ *
+ * Copyright (C) 2001-2004 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "soup-logger.h"
+#include "soup-message.h"
+#include "soup-session.h"
+#include "soup-session-feature.h"
+#include "soup-socket.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-logger
+ * @short_description: Debug logging support
+ *
+ * #SoupLogger watches a #SoupSession and logs the HTTP traffic that
+ * it generates, for debugging purposes. Many applications use an
+ * environment variable to determine whether or not to use
+ * #SoupLogger, and to determine the amount of debugging output.
+ *
+ * To use #SoupLogger, first create a logger with soup_logger_new(),
+ * optionally configure it with soup_logger_set_request_filter(),
+ * soup_logger_set_response_filter(), and soup_logger_set_printer(),
+ * and then attach it to a session (or multiple sessions) with
+ * soup_session_add_feature().
+ *
+ * By default, the debugging output is sent to %stdout, and looks
+ * something like:
+ *
+ * <informalexample><screen>
+ * > POST /unauth HTTP/1.1
+ * > Soup-Debug-Timestamp: 1200171744
+ * > Soup-Debug: SoupSessionAsync 1 (0x612190), SoupMessage 1 (0x617000), SoupSocket 1 (0x612220)
+ * > Host: localhost
+ * > Content-Type: text/plain
+ * > Connection: close
+ * >
+ * > This is a test.
+ *
+ * < HTTP/1.1 201 Created
+ * < Soup-Debug-Timestamp: 1200171744
+ * < Soup-Debug: SoupMessage 1 (0x617000)
+ * < Date: Sun, 12 Jan 2008 21:02:24 GMT
+ * < Content-Length: 0
+ * </screen></informalexample>
+ *
+ * The <literal>Soup-Debug-Timestamp</literal> line gives the time (as
+ * a #time_t) when the request was sent, or the response fully
+ * received.
+ *
+ * The <literal>Soup-Debug</literal> line gives further debugging
+ * information about the #SoupSession, #SoupMessage, and #SoupSocket
+ * involved; the hex numbers are the addresses of the objects in
+ * question (which may be useful if you are running in a debugger).
+ * The decimal IDs are simply counters that uniquely identify objects
+ * across the lifetime of the #SoupLogger. In particular, this can be
+ * used to identify when multiple messages are sent across the same
+ * connection.
+ *
+ * Currently, the request half of the message is logged just before
+ * the first byte of the request gets written to the network (from the
+ * #SoupSession::request_started signal), which means that if you have
+ * not made the complete request body available at that point, it will
+ * not be logged. The response is logged just after the last byte of
+ * the response body is read from the network (from the
+ * #SoupMessage::got_body or #SoupMessage::got_informational signal),
+ * which means that the #SoupMessage::got_headers signal, and anything
+ * triggered off it (such as #SoupSession::authenticate) will be
+ * emitted <emphasis>before</emphasis> the response headers are
+ * actually logged.
+ **/
+
+static void soup_logger_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+static void request_queued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg);
+static void request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket);
+static void request_unqueued (SoupSessionFeature *feature,
+ SoupSession *session, SoupMessage *msg);
+
+G_DEFINE_TYPE_WITH_CODE (SoupLogger, soup_logger, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_logger_session_feature_init))
+
+typedef struct {
+ /* We use a mutex so that if requests are being run in
+ * multiple threads, we don't mix up the output.
+ */
+ GMutex *lock;
+
+ GQuark tag;
+ GHashTable *ids;
+
+ SoupLoggerLogLevel level;
+ int max_body_size;
+
+ SoupLoggerFilter request_filter;
+ gpointer request_filter_data;
+ GDestroyNotify request_filter_dnotify;
+
+ SoupLoggerFilter response_filter;
+ gpointer response_filter_data;
+ GDestroyNotify response_filter_dnotify;
+
+ SoupLoggerPrinter printer;
+ gpointer printer_data;
+ GDestroyNotify printer_dnotify;
+} SoupLoggerPrivate;
+#define SOUP_LOGGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_LOGGER, SoupLoggerPrivate))
+
+static void
+soup_logger_init (SoupLogger *logger)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ priv->lock = g_mutex_new ();
+ priv->tag = g_quark_from_static_string (g_strdup_printf ("SoupLogger-%p", logger));
+ priv->ids = g_hash_table_new (NULL, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->ids);
+
+ if (priv->request_filter_dnotify)
+ priv->request_filter_dnotify (priv->request_filter_data);
+ if (priv->response_filter_dnotify)
+ priv->response_filter_dnotify (priv->response_filter_data);
+ if (priv->printer_dnotify)
+ priv->printer_dnotify (priv->printer_data);
+
+ g_mutex_free (priv->lock);
+
+ G_OBJECT_CLASS (soup_logger_parent_class)->finalize (object);
+}
+
+static void
+soup_logger_class_init (SoupLoggerClass *logger_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (logger_class);
+
+ g_type_class_add_private (logger_class, sizeof (SoupLoggerPrivate));
+
+ object_class->finalize = finalize;
+}
+
+static void
+soup_logger_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ feature_interface->request_queued = request_queued;
+ feature_interface->request_started = request_started;
+ feature_interface->request_unqueued = request_unqueued;
+}
+
+/**
+ * SoupLoggerLogLevel:
+ * @SOUP_LOGGER_LOG_NONE: No logging
+ * @SOUP_LOGGER_LOG_MINIMAL: Log the Request-Line or Status-Line and
+ * the Soup-Debug pseudo-headers
+ * @SOUP_LOGGER_LOG_HEADERS: Log the full request/response headers
+ * @SOUP_LOGGER_LOG_BODY: Log the full headers and request/response
+ * bodies.
+ *
+ * Describes the level of logging output to provide.
+ **/
+
+/**
+ * soup_logger_new:
+ * @level: the debug level
+ * @max_body_size: the maximum body size to output, or -1
+ *
+ * Creates a new #SoupLogger with the given debug level. If @level is
+ * %SOUP_LOGGER_LOG_BODY, @max_body_size gives the maximum number of
+ * bytes of the body that will be logged. (-1 means "no limit".)
+ *
+ * If you need finer control over what message parts are and aren't
+ * logged, use soup_logger_set_request_filter() and
+ * soup_logger_set_response_filter().
+ *
+ * Returns: a new #SoupLogger
+ **/
+SoupLogger *
+soup_logger_new (SoupLoggerLogLevel level, int max_body_size)
+{
+ SoupLogger *logger;
+ SoupLoggerPrivate *priv;
+
+ logger = g_object_new (SOUP_TYPE_LOGGER, NULL);
+
+ priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ priv->level = level;
+ priv->max_body_size = max_body_size;
+
+ return logger;
+}
+
+/**
+ * SoupLoggerFilter:
+ * @logger: the #SoupLogger
+ * @msg: the message being logged
+ * @user_data: the data passed to soup_logger_set_request_filter()
+ * or soup_logger_set_response_filter()
+ *
+ * The prototype for a logging filter. The filter callback will be
+ * invoked for each request or response, and should analyze it and
+ * return a #SoupLoggerLogLevel value indicating how much of the
+ * message to log. Eg, it might choose between %SOUP_LOGGER_LOG_BODY
+ * and %SOUP_LOGGER_LOG_HEADERS depending on the Content-Type.
+ *
+ * Return value: a #SoupLoggerLogLevel value indicating how much of
+ * the message to log
+ **/
+
+/**
+ * soup_logger_set_request_filter:
+ * @logger: a #SoupLogger
+ * @request_filter: the callback for request debugging
+ * @filter_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @filter_data
+ *
+ * Sets up a filter to determine the log level for a given request.
+ * For each HTTP request @logger will invoke @request_filter to
+ * determine how much (if any) of that request to log. (If you do not
+ * set a request filter, @logger will just always log requests at the
+ * level passed to soup_logger_new().)
+ **/
+void
+soup_logger_set_request_filter (SoupLogger *logger,
+ SoupLoggerFilter request_filter,
+ gpointer filter_data,
+ GDestroyNotify destroy)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ priv->request_filter = request_filter;
+ priv->request_filter_data = filter_data;
+ priv->request_filter_dnotify = destroy;
+}
+
+/**
+ * soup_logger_set_response_filter:
+ * @logger: a #SoupLogger
+ * @response_filter: the callback for response debugging
+ * @filter_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @filter_data
+ *
+ * Sets up a filter to determine the log level for a given response.
+ * For each HTTP response @logger will invoke @response_filter to
+ * determine how much (if any) of that response to log. (If you do not
+ * set a response filter, @logger will just always log responses at
+ * the level passed to soup_logger_new().)
+ **/
+void
+soup_logger_set_response_filter (SoupLogger *logger,
+ SoupLoggerFilter response_filter,
+ gpointer filter_data,
+ GDestroyNotify destroy)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ priv->response_filter = response_filter;
+ priv->response_filter_data = filter_data;
+ priv->response_filter_dnotify = destroy;
+}
+
+/**
+ * SoupLoggerPrinter:
+ * @logger: the #SoupLogger
+ * @level: the level of the information being printed.
+ * @direction: a single-character prefix to @data
+ * @data: data to print
+ * @user_data: the data passed to soup_logger_set_printer()
+ *
+ * The prototype for a custom printing callback.
+ *
+ * @level indicates what kind of information is being printed. Eg, it
+ * will be %SOUP_LOGGER_LOG_HEADERS if @data is header data.
+ *
+ * @direction is either '<', '>', or ' ', and @data is the single line
+ * to print; the printer is expected to add a terminating newline.
+ *
+ * To get the effect of the default printer, you would do:
+ *
+ * <informalexample><programlisting>
+ * printf ("%c %s\n", direction, data);
+ * </programlisting></informalexample>
+ **/
+
+/**
+ * soup_logger_set_printer:
+ * @logger: a #SoupLogger
+ * @printer: the callback for printing logging output
+ * @printer_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @printer_data
+ *
+ * Sets up an alternate log printing routine, if you don't want
+ * the log to go to %stdout.
+ **/
+void
+soup_logger_set_printer (SoupLogger *logger,
+ SoupLoggerPrinter printer,
+ gpointer printer_data,
+ GDestroyNotify destroy)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ priv->printer = printer;
+ priv->printer_data = printer_data;
+ priv->printer_dnotify = destroy;
+}
+
+static guint
+soup_logger_get_id (SoupLogger *logger, gpointer object)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ return GPOINTER_TO_UINT (g_object_get_qdata (object, priv->tag));
+}
+
+static guint
+soup_logger_set_id (SoupLogger *logger, gpointer object)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ gpointer klass = G_OBJECT_GET_CLASS (object);
+ gpointer id;
+
+ id = g_hash_table_lookup (priv->ids, klass);
+ id = (char *)id + 1;
+ g_hash_table_insert (priv->ids, klass, id);
+
+ g_object_set_qdata (object, priv->tag, id);
+ return GPOINTER_TO_UINT (id);
+}
+
+/**
+ * soup_logger_attach:
+ * @logger: a #SoupLogger
+ * @session: a #SoupSession
+ *
+ * Sets @logger to watch @session and print debug information for
+ * its messages.
+ *
+ * (The session will take a reference on @logger, which will be
+ * removed when you call soup_logger_detach(), or when the session is
+ * destroyed.)
+ *
+ * Deprecated: Use soup_session_add_feature() instead.
+ **/
+void
+soup_logger_attach (SoupLogger *logger,
+ SoupSession *session)
+{
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
+}
+
+/**
+ * soup_logger_detach:
+ * @logger: a #SoupLogger
+ * @session: a #SoupSession
+ *
+ * Stops @logger from watching @session.
+ *
+ * Deprecated: Use soup_session_remove_feature() instead.
+ **/
+void
+soup_logger_detach (SoupLogger *logger,
+ SoupSession *session)
+{
+ soup_session_remove_feature (session, SOUP_SESSION_FEATURE (logger));
+}
+
+static void
+soup_logger_print (SoupLogger *logger, SoupLoggerLogLevel level,
+ char direction, const char *format, ...)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ va_list args;
+ char *data, *line, *end;
+
+ va_start (args, format);
+ data = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ if (level == SOUP_LOGGER_LOG_BODY && priv->max_body_size > 0) {
+ if (strlen (data) > priv->max_body_size + 6)
+ strcpy (data + priv->max_body_size, "\n[...]");
+ }
+
+ line = data;
+ do {
+ end = strchr (line, '\n');
+ if (end)
+ *end = '\0';
+ if (priv->printer) {
+ priv->printer (logger, level, direction,
+ line, priv->printer_data);
+ } else
+ printf ("%c %s\n", direction, line);
+
+ line = end + 1;
+ } while (end && *line);
+
+ g_free (data);
+}
+
+static void
+soup_logger_print_basic_auth (SoupLogger *logger, const char *value)
+{
+ char *decoded, *p;
+ gsize len;
+
+ decoded = (char *)g_base64_decode (value + 6, &len);
+ if (!decoded)
+ decoded = g_strdup (value);
+ p = strchr (decoded, ':');
+ if (p) {
+ while (++p < decoded + len)
+ *p = '*';
+ }
+ soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '>',
+ "Authorization: Basic [%.*s]", len, decoded);
+ g_free (decoded);
+}
+
+static void
+print_request (SoupLogger *logger, SoupMessage *msg,
+ SoupSession *session, SoupSocket *socket,
+ gboolean restarted)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ SoupLoggerLogLevel log_level;
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+ SoupURI *uri;
+
+ if (priv->request_filter) {
+ log_level = priv->request_filter (logger, msg,
+ priv->request_filter_data);
+ } else
+ log_level = priv->level;
+
+ if (log_level == SOUP_LOGGER_LOG_NONE)
+ return;
+
+ uri = soup_message_get_uri (msg);
+ if (msg->method == SOUP_METHOD_CONNECT) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
+ "CONNECT %s:%u HTTP/1.%d",
+ uri->host, uri->port,
+ soup_message_get_http_version (msg));
+ } else {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
+ "%s %s%s%s HTTP/1.%d",
+ msg->method, uri->path,
+ uri->query ? "?" : "",
+ uri->query ? uri->query : "",
+ soup_message_get_http_version (msg));
+ }
+
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
+ "Soup-Debug-Timestamp: %lu",
+ (unsigned long)time (0));
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
+ "Soup-Debug: %s %u (%p), %s %u (%p), %s %u (%p)%s",
+ g_type_name_from_instance ((GTypeInstance *)session),
+ soup_logger_get_id (logger, session), session,
+ g_type_name_from_instance ((GTypeInstance *)msg),
+ soup_logger_get_id (logger, msg), msg,
+ g_type_name_from_instance ((GTypeInstance *)socket),
+ soup_logger_get_id (logger, socket), socket,
+ restarted ? ", restarted" : "");
+
+ if (log_level == SOUP_LOGGER_LOG_MINIMAL)
+ return;
+
+ if (!soup_message_headers_get_one (msg->request_headers, "Host")) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '>',
+ "Host: %s%c%u", uri->host,
+ soup_uri_uses_default_port (uri) ? '\0' : ':',
+ uri->port);
+ }
+ soup_message_headers_iter_init (&iter, msg->request_headers);
+ while (soup_message_headers_iter_next (&iter, &name, &value)) {
+ if (!g_ascii_strcasecmp (name, "Authorization") &&
+ !g_ascii_strncasecmp (value, "Basic ", 6))
+ soup_logger_print_basic_auth (logger, value);
+ else {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '>',
+ "%s: %s", name, value);
+ }
+ }
+ if (log_level == SOUP_LOGGER_LOG_HEADERS)
+ return;
+
+ if (msg->request_body->length &&
+ soup_message_body_get_accumulate (msg->request_body)) {
+ SoupBuffer *request;
+
+ request = soup_message_body_flatten (msg->request_body);
+ g_return_if_fail (request != NULL);
+ soup_buffer_free (request);
+
+ if (soup_message_headers_get_expectations (msg->request_headers) != SOUP_EXPECTATION_CONTINUE) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '>',
+ "\n%s", msg->request_body->data);
+ }
+ }
+}
+
+static void
+print_response (SoupLogger *logger, SoupMessage *msg)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ SoupLoggerLogLevel log_level;
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+
+ if (priv->response_filter) {
+ log_level = priv->response_filter (logger, msg,
+ priv->response_filter_data);
+ } else
+ log_level = priv->level;
+
+ if (log_level == SOUP_LOGGER_LOG_NONE)
+ return;
+
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '<',
+ "HTTP/1.%d %u %s\n",
+ soup_message_get_http_version (msg),
+ msg->status_code, msg->reason_phrase);
+
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '<',
+ "Soup-Debug-Timestamp: %lu",
+ (unsigned long)time (0));
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '<',
+ "Soup-Debug: %s %u (%p)",
+ g_type_name_from_instance ((GTypeInstance *)msg),
+ soup_logger_get_id (logger, msg), msg);
+
+ if (log_level == SOUP_LOGGER_LOG_MINIMAL)
+ return;
+
+ soup_message_headers_iter_init (&iter, msg->response_headers);
+ while (soup_message_headers_iter_next (&iter, &name, &value)) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '<',
+ "%s: %s", name, value);
+ }
+ if (log_level == SOUP_LOGGER_LOG_HEADERS)
+ return;
+
+ if (msg->response_body->data) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '<',
+ "\n%s", msg->response_body->data);
+ }
+}
+
+static void
+got_informational (SoupMessage *msg, gpointer user_data)
+{
+ SoupLogger *logger = user_data;
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ g_mutex_lock (priv->lock);
+
+ print_response (logger, msg);
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "");
+
+ if (msg->status_code == SOUP_STATUS_CONTINUE && msg->request_body->data) {
+ SoupLoggerLogLevel log_level;
+
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
+ "[Now sending request body...]");
+
+ if (priv->request_filter) {
+ log_level = priv->request_filter (logger, msg,
+ priv->request_filter_data);
+ } else
+ log_level = priv->level;
+
+ if (log_level == SOUP_LOGGER_LOG_BODY) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '>',
+ "%s", msg->request_body->data);
+ }
+
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "");
+ }
+
+ g_mutex_unlock (priv->lock);
+}
+
+static void
+got_body (SoupMessage *msg, gpointer user_data)
+{
+ SoupLogger *logger = user_data;
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ g_mutex_lock (priv->lock);
+
+ print_response (logger, msg);
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "");
+
+ g_mutex_unlock (priv->lock);
+}
+
+static void
+request_queued (SoupSessionFeature *logger, SoupSession *session,
+ SoupMessage *msg)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ g_signal_connect (msg, "got-informational",
+ G_CALLBACK (got_informational),
+ logger);
+ g_signal_connect (msg, "got-body",
+ G_CALLBACK (got_body),
+ logger);
+}
+
+static void
+request_started (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg, SoupSocket *socket)
+{
+ SoupLogger *logger = SOUP_LOGGER (feature);
+ gboolean restarted;
+ guint msg_id;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (SOUP_IS_SOCKET (socket));
+
+ msg_id = soup_logger_get_id (logger, msg);
+ if (msg_id)
+ restarted = TRUE;
+ else {
+ soup_logger_set_id (logger, msg);
+ restarted = FALSE;
+ }
+
+ if (!soup_logger_get_id (logger, session))
+ soup_logger_set_id (logger, session);
+
+ if (!soup_logger_get_id (logger, socket))
+ soup_logger_set_id (logger, socket);
+
+ print_request (logger, msg, session, socket, restarted);
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "");
+}
+
+static void
+request_unqueued (SoupSessionFeature *logger, SoupSession *session,
+ SoupMessage *msg)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ g_signal_handlers_disconnect_by_func (msg, got_informational, logger);
+ g_signal_handlers_disconnect_by_func (msg, got_body, logger);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_LOGGER_H
+#define SOUP_LOGGER_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_LOGGER (soup_logger_get_type ())
+#define SOUP_LOGGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_LOGGER, SoupLogger))
+#define SOUP_LOGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_LOGGER, SoupLoggerClass))
+#define SOUP_IS_LOGGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_LOGGER))
+#define SOUP_IS_LOGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_LOGGER))
+#define SOUP_LOGGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_LOGGER, SoupLoggerClass))
+
+typedef enum {
+ SOUP_LOGGER_LOG_NONE,
+ SOUP_LOGGER_LOG_MINIMAL,
+ SOUP_LOGGER_LOG_HEADERS,
+ SOUP_LOGGER_LOG_BODY
+} SoupLoggerLogLevel;
+
+typedef struct {
+ GObject parent;
+
+} SoupLogger;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupLoggerClass;
+
+typedef SoupLoggerLogLevel (*SoupLoggerFilter) (SoupLogger *logger,
+ SoupMessage *msg,
+ gpointer user_data);
+
+typedef void (*SoupLoggerPrinter) (SoupLogger *logger,
+ SoupLoggerLogLevel level,
+ char direction,
+ const char *data,
+ gpointer user_data);
+
+GType soup_logger_get_type (void);
+
+SoupLogger *soup_logger_new (SoupLoggerLogLevel level,
+ int max_body_size);
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+/* Use soup_session_add/remove_feature */
+void soup_logger_attach (SoupLogger *logger,
+ SoupSession *session);
+void soup_logger_detach (SoupLogger *logger,
+ SoupSession *session);
+#endif
+
+void soup_logger_set_request_filter (SoupLogger *logger,
+ SoupLoggerFilter request_filter,
+ gpointer filter_data,
+ GDestroyNotify destroy);
+void soup_logger_set_response_filter (SoupLogger *logger,
+ SoupLoggerFilter response_filter,
+ gpointer filter_data,
+ GDestroyNotify destroy);
+
+void soup_logger_set_printer (SoupLogger *logger,
+ SoupLoggerPrinter printer,
+ gpointer printer_data,
+ GDestroyNotify destroy);
+
+G_END_DECLS
+
+#endif /* SOUP_LOGGER_H */
--- /dev/null
+NONE:BOXED
+NONE:INT
+NONE:NONE
+NONE:OBJECT
+NONE:OBJECT,OBJECT
+NONE:OBJECT,POINTER
+NONE:BOXED,BOXED
+NONE:OBJECT,OBJECT,BOOLEAN
+NONE:STRING,BOXED
+NONE:STRING,STRING
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-body.c: SoupMessage request/response bodies
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#include <string.h>
+
+#include "soup-message-body.h"
+
+/**
+ * SECTION:soup-message-body
+ * @short_description: HTTP message body
+ * @see_also: #SoupMessage
+ *
+ * #SoupMessageBody represents the request or response body of a
+ * #SoupMessage.
+ *
+ * In addition to #SoupMessageBody, libsoup also defines a "smaller"
+ * data buffer type, #SoupBuffer, which is primarily used as a
+ * component of #SoupMessageBody. In particular, when using chunked
+ * encoding to transmit or receive a message, each chunk is
+ * represented as a #SoupBuffer.
+ **/
+
+/**
+ * SoupMemoryUse:
+ * @SOUP_MEMORY_STATIC: The memory is statically allocated and
+ * constant; libsoup can use the passed-in buffer directly and not
+ * need to worry about it being modified or freed.
+ * @SOUP_MEMORY_TAKE: The caller has allocated the memory for the
+ * #SoupBuffer's use; libsoup will assume ownership of it and free it
+ * (with g_free()) when it is done with it.
+ * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller; the
+ * #SoupBuffer will copy it into new memory, leaving the caller free
+ * to reuse the original memory.
+ * @SOUP_MEMORY_TEMPORARY: The passed-in data belongs to the caller,
+ * but will remain valid for the lifetime of the #SoupBuffer. The
+ * difference between this and @SOUP_MEMORY_STATIC is that if you copy
+ * a @SOUP_MEMORY_TEMPORARY buffer, it will make a copy of the memory
+ * as well, rather than reusing the original memory.
+ *
+ * Describes how #SoupBuffer should use the data passed in by the
+ * caller.
+ *
+ * See also soup_buffer_new_with_owner(), which allows to you create a
+ * buffer containing data which is owned by another object.
+ **/
+
+/* Internal SoupMemoryUse values */
+enum {
+ SOUP_MEMORY_SUBBUFFER = SOUP_MEMORY_TEMPORARY + 1,
+ SOUP_MEMORY_OWNED
+};
+
+/**
+ * SoupBuffer:
+ * @data: (type gpointer): the data
+ * @length: length of @data
+ *
+ * A data buffer, generally used to represent a chunk of a
+ * #SoupMessageBody.
+ *
+ * @data is a #char because that's generally convenient; in some
+ * situations you may need to cast it to #guchar or another type.
+ **/
+
+typedef struct {
+ SoupBuffer buffer;
+ SoupMemoryUse use;
+ guint refcount;
+
+ gpointer owner;
+ GDestroyNotify owner_dnotify;
+} SoupBufferPrivate;
+
+/**
+ * soup_buffer_new:
+ * @use: how @data is to be used by the buffer
+ * @data: data
+ * @length: length of @data
+ *
+ * Creates a new #SoupBuffer containing @length bytes from @data.
+ *
+ * Return value: the new #SoupBuffer.
+ **/
+SoupBuffer *
+soup_buffer_new (SoupMemoryUse use, gconstpointer data, gsize length)
+{
+ SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate);
+
+ if (use == SOUP_MEMORY_COPY) {
+ data = g_memdup (data, length);
+ use = SOUP_MEMORY_TAKE;
+ }
+
+ priv->buffer.data = data;
+ priv->buffer.length = length;
+ priv->use = use;
+ priv->refcount = 1;
+
+ if (use == SOUP_MEMORY_TAKE) {
+ priv->owner = (gpointer)data;
+ priv->owner_dnotify = g_free;
+ }
+
+ return (SoupBuffer *)priv;
+}
+
+/**
+ * soup_buffer_new_take:
+ * @data: (array length=length) (transfer full): data
+ * @length: length of @data
+ *
+ * Creates a new #SoupBuffer containing @length bytes from @data.
+ *
+ * This function is exactly equivalent to soup_buffer_new() with
+ * %SOUP_MEMORY_TAKE as first argument; it exists mainly for
+ * convenience and simplifying language bindings.
+ *
+ * Return value: the new #SoupBuffer.
+ *
+ * Since: 2.32
+ * Rename to: soup_buffer_new
+ **/
+SoupBuffer *
+soup_buffer_new_take (guchar *data, gsize length)
+{
+ return soup_buffer_new (SOUP_MEMORY_TAKE, data, length);
+}
+
+/**
+ * soup_buffer_new_subbuffer:
+ * @parent: the parent #SoupBuffer
+ * @offset: offset within @parent to start at
+ * @length: number of bytes to copy from @parent
+ *
+ * Creates a new #SoupBuffer containing @length bytes "copied" from
+ * @parent starting at @offset. (Normally this will not actually copy
+ * any data, but will instead simply reference the same data as
+ * @parent does.)
+ *
+ * Return value: the new #SoupBuffer.
+ **/
+SoupBuffer *
+soup_buffer_new_subbuffer (SoupBuffer *parent, gsize offset, gsize length)
+{
+ SoupBufferPrivate *priv;
+
+ /* Normally this is just a ref, but if @parent is TEMPORARY,
+ * it will do an actual copy.
+ */
+ parent = soup_buffer_copy (parent);
+
+ priv = g_slice_new0 (SoupBufferPrivate);
+ priv->buffer.data = parent->data + offset;
+ priv->buffer.length = length;
+ priv->use = SOUP_MEMORY_SUBBUFFER;
+ priv->owner = parent;
+ priv->owner_dnotify = (GDestroyNotify)soup_buffer_free;
+ priv->refcount = 1;
+
+ return (SoupBuffer *)priv;
+}
+
+/**
+ * soup_buffer_new_with_owner:
+ * @data: data
+ * @length: length of @data
+ * @owner: pointer to an object that owns @data
+ * @owner_dnotify: (allow-none): a function to free/unref @owner when
+ * the buffer is freed
+ *
+ * Creates a new #SoupBuffer containing @length bytes from @data. When
+ * the #SoupBuffer is freed, it will call @owner_dnotify, passing
+ * @owner to it. You must ensure that @data will remain valid until
+ * @owner_dnotify is called.
+ *
+ * For example, you could use this to create a buffer containing data
+ * returned from libxml without needing to do an extra copy:
+ *
+ * <informalexample><programlisting>
+ * xmlDocDumpMemory (doc, &xmlbody, &len);
+ * return soup_buffer_new_with_owner (xmlbody, len, xmlbody,
+ * (GDestroyNotify)xmlFree);
+ * </programlisting></informalexample>
+ *
+ * In this example, @data and @owner are the same, but in other cases
+ * they would be different (eg, @owner would be a object, and @data
+ * would be a pointer to one of the object's fields).
+ *
+ * Return value: the new #SoupBuffer.
+ **/
+SoupBuffer *
+soup_buffer_new_with_owner (gconstpointer data, gsize length,
+ gpointer owner, GDestroyNotify owner_dnotify)
+{
+ SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate);
+
+ priv->buffer.data = data;
+ priv->buffer.length = length;
+ priv->use = SOUP_MEMORY_OWNED;
+ priv->owner = owner;
+ priv->owner_dnotify = owner_dnotify;
+ priv->refcount = 1;
+
+ return (SoupBuffer *)priv;
+}
+
+/**
+ * soup_buffer_get_owner:
+ * @buffer: a #SoupBuffer created with soup_buffer_new_with_owner()
+ *
+ * Gets the "owner" object for a buffer created with
+ * soup_buffer_new_with_owner().
+ *
+ * Return value: (transfer none): the owner pointer
+ **/
+gpointer
+soup_buffer_get_owner (SoupBuffer *buffer)
+{
+ SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
+
+ g_return_val_if_fail ((int)priv->use == (int)SOUP_MEMORY_OWNED, NULL);
+ return priv->owner;
+}
+
+/**
+ * soup_buffer_get_data:
+ * @buffer: a #SoupBuffer
+ * @data: (out) (array length=length) (transfer none): the pointer
+ * to the buffer data is stored here
+ * @length: (out): the length of the buffer data is stored here
+ *
+ * This function exists for use by language bindings, because it's not
+ * currently possible to get the right effect by annotating the fields
+ * of #SoupBuffer.
+ *
+ * Since: 2.32
+ */
+void
+soup_buffer_get_data (SoupBuffer *buffer,
+ const guint8 **data,
+ gsize *length)
+{
+ *data = (const guint8 *)buffer->data;
+ *length = buffer->length;
+}
+
+/**
+ * soup_buffer_copy:
+ * @buffer: a #SoupBuffer
+ *
+ * Makes a copy of @buffer. In reality, #SoupBuffer is a refcounted
+ * type, and calling soup_buffer_copy() will normally just increment
+ * the refcount on @buffer and return it. However, if @buffer was
+ * created with #SOUP_MEMORY_TEMPORARY memory, then soup_buffer_copy()
+ * will actually return a copy of it, so that the data in the copy
+ * will remain valid after the temporary buffer is freed.
+ *
+ * Return value: the new (or newly-reffed) buffer
+ **/
+SoupBuffer *
+soup_buffer_copy (SoupBuffer *buffer)
+{
+ SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
+
+ /* For non-TEMPORARY buffers, this is just a ref */
+ if (priv->use != SOUP_MEMORY_TEMPORARY) {
+ priv->refcount++;
+ return buffer;
+ }
+
+ /* For TEMPORARY buffers, we need to do a real copy the first
+ * time, and then after that, we just keep returning the copy.
+ * We store the copy in priv->owner, which is technically
+ * backwards, but it saves us from having to keep an extra
+ * pointer in SoupBufferPrivate.
+ */
+
+ if (!priv->owner) {
+ priv->owner = soup_buffer_new (SOUP_MEMORY_COPY,
+ buffer->data,
+ buffer->length);
+ priv->owner_dnotify = (GDestroyNotify)soup_buffer_free;
+ }
+ return soup_buffer_copy (priv->owner);
+}
+
+/**
+ * soup_buffer_free:
+ * @buffer: a #SoupBuffer
+ *
+ * Frees @buffer. (In reality, as described in the documentation for
+ * soup_buffer_copy(), this is actually an "unref" operation, and may
+ * or may not actually free @buffer.)
+ **/
+void
+soup_buffer_free (SoupBuffer *buffer)
+{
+ SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
+
+ if (!--priv->refcount) {
+ if (priv->owner_dnotify)
+ priv->owner_dnotify (priv->owner);
+ g_slice_free (SoupBufferPrivate, priv);
+ }
+}
+
+GType
+soup_buffer_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupBuffer"),
+ (GBoxedCopyFunc) soup_buffer_copy,
+ (GBoxedFreeFunc) soup_buffer_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
+
+
+/**
+ * SoupMessageBody:
+ * @data: the data
+ * @length: length of @data
+ *
+ * A #SoupMessage request or response body.
+ *
+ * Note that while @length always reflects the full length of the
+ * message body, @data is normally %NULL, and will only be filled in
+ * after soup_message_body_flatten() is called. For client-side
+ * messages, this automatically happens for the response body after it
+ * has been fully read, unless you set the
+ * %SOUP_MESSAGE_OVERWRITE_CHUNKS flags. Likewise, for server-side
+ * messages, the request body is automatically filled in after being
+ * read.
+ *
+ * As an added bonus, when @data is filled in, it is always terminated
+ * with a '\0' byte (which is not reflected in @length).
+ **/
+
+typedef struct {
+ SoupMessageBody body;
+ GSList *chunks, *last;
+ SoupBuffer *flattened;
+ gboolean accumulate;
+ goffset base_offset;
+ int ref_count;
+} SoupMessageBodyPrivate;
+
+/**
+ * soup_message_body_new:
+ *
+ * Creates a new #SoupMessageBody. #SoupMessage uses this internally; you
+ * will not normally need to call it yourself.
+ *
+ * Return value: a new #SoupMessageBody.
+ **/
+SoupMessageBody *
+soup_message_body_new (void)
+{
+ SoupMessageBodyPrivate *priv;
+
+ priv = g_slice_new0 (SoupMessageBodyPrivate);
+ priv->accumulate = TRUE;
+ priv->ref_count = 1;
+
+ return (SoupMessageBody *)priv;
+}
+
+/**
+ * soup_message_body_set_accumulate:
+ * @body: a #SoupMessageBody
+ * @accumulate: whether or not to accumulate body chunks in @body
+ *
+ * Sets or clears the accumulate flag on @body. (The default value is
+ * %TRUE.) If set to %FALSE, @body's %data field will not be filled in
+ * after the body is fully sent/received, and the chunks that make up
+ * @body may be discarded when they are no longer needed.
+ *
+ * In particular, if you set this flag to %FALSE on an "incoming"
+ * message body (that is, the %response_body of a client-side message,
+ * or %request_body of a server-side message), this will cause each
+ * chunk of the body to be discarded after its corresponding
+ * #SoupMessage::got_chunk signal is emitted. (This is equivalent to
+ * setting the deprecated %SOUP_MESSAGE_OVERWRITE_CHUNKS flag on the
+ * message.)
+ *
+ * If you set this flag to %FALSE on the %response_body of a
+ * server-side message, it will cause each chunk of the body to be
+ * discarded after its corresponding #SoupMessage::wrote_chunk signal
+ * is emitted.
+ *
+ * If you set the flag to %FALSE on the %request_body of a client-side
+ * message, it will block the accumulation of chunks into @body's
+ * %data field, but it will not normally cause the chunks to be
+ * discarded after being written like in the server-side
+ * %response_body case, because the request body needs to be kept
+ * around in case the request needs to be sent a second time due to
+ * redirection or authentication. However, if you set the
+ * %SOUP_MESSAGE_CAN_REBUILD flag on the message, then the chunks will
+ * be discarded, and you will be responsible for recreating the
+ * request body after the #SoupMessage::restarted signal is emitted.
+ *
+ * Since: 2.4.1
+ **/
+void
+soup_message_body_set_accumulate (SoupMessageBody *body,
+ gboolean accumulate)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+ priv->accumulate = accumulate;
+}
+
+/**
+ * soup_message_body_get_accumulate:
+ * @body: a #SoupMessageBody
+ *
+ * Gets the accumulate flag on @body; see
+ * soup_message_body_set_accumulate() for details.
+ *
+ * Return value: the accumulate flag for @body.
+ *
+ * Since: 2.4.1
+ **/
+gboolean
+soup_message_body_get_accumulate (SoupMessageBody *body)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+ return priv->accumulate;
+}
+
+static void
+append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+ if (priv->last) {
+ priv->last = g_slist_append (priv->last, buffer);
+ priv->last = priv->last->next;
+ } else
+ priv->chunks = priv->last = g_slist_append (NULL, buffer);
+
+ if (priv->flattened) {
+ soup_buffer_free (priv->flattened);
+ priv->flattened = NULL;
+ body->data = NULL;
+ }
+ body->length += buffer->length;
+}
+
+/**
+ * soup_message_body_append:
+ * @body: a #SoupMessageBody
+ * @use: how to use @data
+ * @data: (array length=length) (element-type guint8): data to append
+ * @length: length of @data
+ *
+ * Appends @length bytes from @data to @body according to @use.
+ **/
+void
+soup_message_body_append (SoupMessageBody *body, SoupMemoryUse use,
+ gconstpointer data, gsize length)
+{
+ if (length > 0)
+ append_buffer (body, soup_buffer_new (use, data, length));
+ else if (use == SOUP_MEMORY_TAKE)
+ g_free ((gpointer)data);
+}
+
+/**
+ * soup_message_body_append_take:
+ * @body: a #SoupMessageBody
+ * @data: (array length=length) (transfer full): data to append
+ * @length: length of @data
+ *
+ * Appends @length bytes from @data to @body.
+ *
+ * This function is exactly equivalent to soup_message_body_apppend()
+ * with %SOUP_MEMORY_TAKE as second argument; it exists mainly for
+ * convenience and simplifying language bindings.
+ *
+ * Since: 2.32
+ * Rename to: soup_message_body_append
+ **/
+void
+soup_message_body_append_take (SoupMessageBody *body,
+ guchar *data, gsize length)
+{
+ soup_message_body_append(body, SOUP_MEMORY_TAKE, data, length);
+}
+
+/**
+ * soup_message_body_append_buffer:
+ * @body: a #SoupMessageBody
+ * @buffer: a #SoupBuffer
+ *
+ * Appends the data from @buffer to @body. (#SoupMessageBody uses
+ * #SoupBuffers internally, so this is normally a constant-time
+ * operation that doesn't actually require copying the data in
+ * @buffer.)
+ **/
+void
+soup_message_body_append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
+{
+ g_return_if_fail (buffer->length > 0);
+ append_buffer (body, soup_buffer_copy (buffer));
+}
+
+/**
+ * soup_message_body_truncate:
+ * @body: a #SoupMessageBody
+ *
+ * Deletes all of the data in @body.
+ **/
+void
+soup_message_body_truncate (SoupMessageBody *body)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+ GSList *iter;
+
+ for (iter = priv->chunks; iter; iter = iter->next)
+ soup_buffer_free (iter->data);
+ g_slist_free (priv->chunks);
+ priv->chunks = priv->last = NULL;
+ priv->base_offset = 0;
+
+ if (priv->flattened) {
+ soup_buffer_free (priv->flattened);
+ priv->flattened = NULL;
+ body->data = NULL;
+ }
+ body->length = 0;
+}
+
+/**
+ * soup_message_body_complete:
+ * @body: a #SoupMessageBody
+ *
+ * Tags @body as being complete; Call this when using chunked encoding
+ * after you have appended the last chunk.
+ **/
+void
+soup_message_body_complete (SoupMessageBody *body)
+{
+ append_buffer (body, soup_buffer_new (SOUP_MEMORY_STATIC, NULL, 0));
+}
+
+/**
+ * soup_message_body_flatten:
+ * @body: a #SoupMessageBody
+ *
+ * Fills in @body's data field with a buffer containing all of the
+ * data in @body (plus an additional '\0' byte not counted by @body's
+ * length field).
+ *
+ * Return value: a #SoupBuffer containing the same data as @body.
+ * (You must free this buffer if you do not want it.)
+ **/
+SoupBuffer *
+soup_message_body_flatten (SoupMessageBody *body)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+ char *buf, *ptr;
+ GSList *iter;
+ SoupBuffer *chunk;
+
+ g_return_val_if_fail (priv->accumulate == TRUE, NULL);
+
+ if (!priv->flattened) {
+#if GLIB_SIZEOF_SIZE_T < 8
+ g_return_val_if_fail (body->length < G_MAXSIZE, NULL);
+#endif
+
+ buf = ptr = g_malloc (body->length + 1);
+ for (iter = priv->chunks; iter; iter = iter->next) {
+ chunk = iter->data;
+ memcpy (ptr, chunk->data, chunk->length);
+ ptr += chunk->length;
+ }
+ *ptr = '\0';
+
+ priv->flattened = soup_buffer_new (SOUP_MEMORY_TAKE,
+ buf, body->length);
+ body->data = priv->flattened->data;
+ }
+
+ return soup_buffer_copy (priv->flattened);
+}
+
+/**
+ * soup_message_body_get_chunk:
+ * @body: a #SoupMessageBody
+ * @offset: an offset
+ *
+ * Gets a #SoupBuffer containing data from @body starting at @offset.
+ * The size of the returned chunk is unspecified. You can iterate
+ * through the entire body by first calling
+ * soup_message_body_get_chunk() with an offset of 0, and then on each
+ * successive call, increment the offset by the length of the
+ * previously-returned chunk.
+ *
+ * If @offset is greater than or equal to the total length of @body,
+ * then the return value depends on whether or not
+ * soup_message_body_complete() has been called or not; if it has,
+ * then soup_message_body_get_chunk() will return a 0-length chunk
+ * (indicating the end of @body). If it has not, then
+ * soup_message_body_get_chunk() will return %NULL (indicating that
+ * @body may still potentially have more data, but that data is not
+ * currently available).
+ *
+ * Return value: a #SoupBuffer, or %NULL.
+ **/
+SoupBuffer *
+soup_message_body_get_chunk (SoupMessageBody *body, goffset offset)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+ GSList *iter;
+ SoupBuffer *chunk = NULL;
+
+ offset -= priv->base_offset;
+ for (iter = priv->chunks; iter; iter = iter->next) {
+ chunk = iter->data;
+
+ if (offset < chunk->length || offset == 0)
+ break;
+
+ offset -= chunk->length;
+ }
+
+ if (!iter)
+ return NULL;
+
+ if (offset == 0)
+ return soup_buffer_copy (chunk);
+ else {
+ return soup_buffer_new_subbuffer (chunk, offset,
+ chunk->length - offset);
+ }
+}
+
+/**
+ * soup_message_body_got_chunk:
+ * @body: a #SoupMessageBody
+ * @chunk: a #SoupBuffer received from the network
+ *
+ * Handles the #SoupMessageBody part of receiving a chunk of data from
+ * the network. Normally this means appending @chunk to @body, exactly
+ * as with soup_message_body_append_buffer(), but if you have set
+ * @body's accumulate flag to %FALSE, then that will not happen.
+ *
+ * This is a low-level method which you should not normally need to
+ * use.
+ *
+ * Since: 2.4.1
+ **/
+void
+soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+ if (!priv->accumulate)
+ return;
+
+ soup_message_body_append_buffer (body, chunk);
+}
+
+/**
+ * soup_message_body_wrote_chunk:
+ * @body: a #SoupMessageBody
+ * @chunk: a #SoupBuffer returned from soup_message_body_get_chunk()
+ *
+ * Handles the #SoupMessageBody part of writing a chunk of data to the
+ * network. Normally this is a no-op, but if you have set @body's
+ * accumulate flag to %FALSE, then this will cause @chunk to be
+ * discarded to free up memory.
+ *
+ * This is a low-level method which you should not need to use, and
+ * there are further restrictions on its proper use which are not
+ * documented here.
+ *
+ * Since: 2.4.1
+ **/
+void
+soup_message_body_wrote_chunk (SoupMessageBody *body, SoupBuffer *chunk)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+ SoupBuffer *chunk2;
+
+ if (priv->accumulate)
+ return;
+
+ chunk2 = priv->chunks->data;
+ g_return_if_fail (chunk->length == chunk2->length);
+ g_return_if_fail (chunk == chunk2 || ((SoupBufferPrivate *)chunk2)->use == SOUP_MEMORY_TEMPORARY);
+
+ priv->chunks = g_slist_remove (priv->chunks, chunk2);
+ if (!priv->chunks)
+ priv->last = NULL;
+
+ priv->base_offset += chunk2->length;
+ soup_buffer_free (chunk2);
+}
+
+static SoupMessageBody *
+soup_message_body_copy (SoupMessageBody *body)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+ priv->ref_count++;
+ return body;
+}
+
+/**
+ * soup_message_body_free:
+ * @body: a #SoupMessageBody
+ *
+ * Frees @body. You will not normally need to use this, as
+ * #SoupMessage frees its associated message bodies automatically.
+ **/
+void
+soup_message_body_free (SoupMessageBody *body)
+{
+ SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+ if (--priv->ref_count == 0) {
+ soup_message_body_truncate (body);
+ g_slice_free (SoupMessageBodyPrivate, priv);
+ }
+}
+
+GType
+soup_message_body_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupMessageBody"),
+ (GBoxedCopyFunc) soup_message_body_copy,
+ (GBoxedFreeFunc) soup_message_body_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_BODY_H
+#define SOUP_MESSAGE_BODY_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ SOUP_MEMORY_STATIC,
+ SOUP_MEMORY_TAKE,
+ SOUP_MEMORY_COPY,
+ SOUP_MEMORY_TEMPORARY
+} SoupMemoryUse;
+
+typedef struct {
+ const char *data;
+ gsize length;
+} SoupBuffer;
+
+GType soup_buffer_get_type (void);
+#define SOUP_TYPE_BUFFER (soup_buffer_get_type ())
+
+SoupBuffer *soup_buffer_new (SoupMemoryUse use,
+ gconstpointer data,
+ gsize length);
+SoupBuffer *soup_buffer_new_take (guchar *data,
+ gsize length);
+SoupBuffer *soup_buffer_new_subbuffer (SoupBuffer *parent,
+ gsize offset,
+ gsize length);
+
+SoupBuffer *soup_buffer_new_with_owner (gconstpointer data,
+ gsize length,
+ gpointer owner,
+ GDestroyNotify owner_dnotify);
+gpointer soup_buffer_get_owner (SoupBuffer *buffer);
+void soup_buffer_get_data (SoupBuffer *buffer,
+ const guint8 **data,
+ gsize *length);
+SoupBuffer *soup_buffer_copy (SoupBuffer *buffer);
+void soup_buffer_free (SoupBuffer *buffer);
+
+typedef struct {
+ const char *data;
+ goffset length;
+} SoupMessageBody;
+
+GType soup_message_body_get_type (void);
+#define SOUP_TYPE_MESSAGE_BODY (soup_message_body_get_type ())
+
+SoupMessageBody *soup_message_body_new (void);
+
+void soup_message_body_set_accumulate(SoupMessageBody *body,
+ gboolean accumulate);
+gboolean soup_message_body_get_accumulate(SoupMessageBody *body);
+
+void soup_message_body_append (SoupMessageBody *body,
+ SoupMemoryUse use,
+ gconstpointer data,
+ gsize length);
+void soup_message_body_append_take (SoupMessageBody *body,
+ guchar *data,
+ gsize length);
+void soup_message_body_append_buffer (SoupMessageBody *body,
+ SoupBuffer *buffer);
+void soup_message_body_truncate (SoupMessageBody *body);
+void soup_message_body_complete (SoupMessageBody *body);
+
+SoupBuffer *soup_message_body_flatten (SoupMessageBody *body);
+
+SoupBuffer *soup_message_body_get_chunk (SoupMessageBody *body,
+ goffset offset);
+
+void soup_message_body_got_chunk (SoupMessageBody *body,
+ SoupBuffer *chunk);
+void soup_message_body_wrote_chunk (SoupMessageBody *body,
+ SoupBuffer *chunk);
+
+void soup_message_body_free (SoupMessageBody *body);
+
+G_END_DECLS
+
+#endif /* SOUP_MESSAGE_BODY_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-client-io.c: client-side request/response
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-connection.h"
+#include "soup-message-private.h"
+#include "soup-auth.h"
+#include "soup-connection.h"
+#include "soup-headers.h"
+#include "soup-message-queue.h"
+#include "soup-uri.h"
+
+static guint
+parse_response_headers (SoupMessage *req,
+ char *headers, guint headers_len,
+ SoupEncoding *encoding,
+ gpointer user_data)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+ SoupHTTPVersion version;
+
+ g_free(req->reason_phrase);
+ req->reason_phrase = NULL;
+ if (!soup_headers_parse_response (headers, headers_len,
+ req->response_headers,
+ &version,
+ &req->status_code,
+ &req->reason_phrase))
+ return SOUP_STATUS_MALFORMED;
+
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
+
+ if (version < priv->http_version) {
+ priv->http_version = version;
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
+ }
+
+ if ((req->method == SOUP_METHOD_HEAD ||
+ req->status_code == SOUP_STATUS_NO_CONTENT ||
+ req->status_code == SOUP_STATUS_NOT_MODIFIED ||
+ SOUP_STATUS_IS_INFORMATIONAL (req->status_code)) ||
+ (req->method == SOUP_METHOD_CONNECT &&
+ SOUP_STATUS_IS_SUCCESSFUL (req->status_code)))
+ *encoding = SOUP_ENCODING_NONE;
+ else
+ *encoding = soup_message_headers_get_encoding (req->response_headers);
+
+ if (*encoding == SOUP_ENCODING_UNRECOGNIZED)
+ return SOUP_STATUS_MALFORMED;
+
+ return SOUP_STATUS_OK;
+}
+
+static void
+get_request_headers (SoupMessage *req, GString *header,
+ SoupEncoding *encoding, gpointer user_data)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+ SoupMessageQueueItem *item = user_data;
+ SoupURI *uri = soup_message_get_uri (req);
+ char *uri_host;
+ char *uri_string;
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+
+ if (strchr (uri->host, ':'))
+ uri_host = g_strdup_printf ("[%s]", uri->host);
+ else if (g_hostname_is_non_ascii (uri->host))
+ uri_host = g_hostname_to_ascii (uri->host);
+ else
+ uri_host = uri->host;
+
+ if (req->method == SOUP_METHOD_CONNECT) {
+ /* CONNECT URI is hostname:port for tunnel destination */
+ uri_string = g_strdup_printf ("%s:%d", uri_host, uri->port);
+ } else {
+ gboolean proxy = soup_connection_is_via_proxy (item->conn);
+
+ /* Proxy expects full URI to destination. Otherwise
+ * just the path.
+ */
+ uri_string = soup_uri_to_string (uri, !proxy);
+
+ if (proxy && uri->fragment) {
+ /* Strip fragment */
+ char *fragment = strchr (uri_string, '#');
+ if (fragment)
+ *fragment = '\0';
+ }
+ }
+
+ if (priv->http_version == SOUP_HTTP_1_0) {
+ g_string_append_printf (header, "%s %s HTTP/1.0\r\n",
+ req->method, uri_string);
+ } else {
+ g_string_append_printf (header, "%s %s HTTP/1.1\r\n",
+ req->method, uri_string);
+ if (!soup_message_headers_get_one (req->request_headers, "Host")) {
+ if (soup_uri_uses_default_port (uri)) {
+ g_string_append_printf (header, "Host: %s\r\n",
+ uri_host);
+ } else {
+ g_string_append_printf (header, "Host: %s:%d\r\n",
+ uri_host, uri->port);
+ }
+ }
+ }
+ g_free (uri_string);
+ if (uri_host != uri->host)
+ g_free (uri_host);
+
+ *encoding = soup_message_headers_get_encoding (req->request_headers);
+ if ((*encoding == SOUP_ENCODING_CONTENT_LENGTH ||
+ *encoding == SOUP_ENCODING_NONE) &&
+ (req->request_body->length > 0 ||
+ soup_message_headers_get_one (req->request_headers, "Content-Type")) &&
+ !soup_message_headers_get_content_length (req->request_headers)) {
+ *encoding = SOUP_ENCODING_CONTENT_LENGTH;
+ soup_message_headers_set_content_length (req->request_headers,
+ req->request_body->length);
+ }
+
+ soup_message_headers_iter_init (&iter, req->request_headers);
+ while (soup_message_headers_iter_next (&iter, &name, &value))
+ g_string_append_printf (header, "%s: %s\r\n", name, value);
+ g_string_append (header, "\r\n");
+}
+
+void
+soup_message_send_request (SoupMessageQueueItem *item,
+ SoupMessageCompletionFn completion_cb,
+ gpointer user_data)
+{
+ soup_message_cleanup_response (item->msg);
+ soup_message_io_client (item,
+ get_request_headers,
+ parse_response_headers,
+ item,
+ completion_cb, user_data);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-headers.c: HTTP message header arrays
+ *
+ * Copyright (C) 2007, 2008 Red Hat, Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "soup-message-headers.h"
+#include "soup-headers.h"
+#include "soup-misc.h"
+
+/**
+ * SECTION:soup-message-headers
+ * @short_description: HTTP message headers
+ * @see_also: #SoupMessage
+ *
+ * #SoupMessageHeaders represents the HTTP message headers associated
+ * with a request or response.
+ **/
+
+/**
+ * SoupMessageHeadersType:
+ * @SOUP_MESSAGE_HEADERS_REQUEST: request headers
+ * @SOUP_MESSAGE_HEADERS_RESPONSE: response headers
+ * @SOUP_MESSAGE_HEADERS_MULTIPART: multipart body part headers
+ *
+ * Value passed to soup_message_headers_new() to set certain default
+ * behaviors.
+ **/
+
+typedef void (*SoupHeaderSetter) (SoupMessageHeaders *, const char *);
+static const char *intern_header_name (const char *name, SoupHeaderSetter *setter);
+static void clear_special_headers (SoupMessageHeaders *hdrs);
+
+typedef struct {
+ const char *name;
+ char *value;
+} SoupHeader;
+
+struct SoupMessageHeaders {
+ GArray *array;
+ GHashTable *concat;
+ SoupMessageHeadersType type;
+
+ SoupEncoding encoding;
+ goffset content_length;
+ SoupExpectation expectations;
+ char *content_type;
+
+ int ref_count;
+};
+
+/**
+ * soup_message_headers_new:
+ * @type: the type of headers
+ *
+ * Creates a #SoupMessageHeaders. (#SoupMessage does this
+ * automatically for its own headers. You would only need to use this
+ * method if you are manually parsing or generating message headers.)
+ *
+ * Return value: a new #SoupMessageHeaders
+ **/
+SoupMessageHeaders *
+soup_message_headers_new (SoupMessageHeadersType type)
+{
+ SoupMessageHeaders *hdrs;
+
+ hdrs = g_slice_new0 (SoupMessageHeaders);
+ /* FIXME: is "5" a good default? */
+ hdrs->array = g_array_sized_new (TRUE, FALSE, sizeof (SoupHeader), 5);
+ hdrs->type = type;
+ hdrs->encoding = -1;
+ hdrs->ref_count = 1;
+
+ return hdrs;
+}
+
+static SoupMessageHeaders *
+soup_message_headers_copy (SoupMessageHeaders *hdrs)
+{
+ hdrs->ref_count++;
+ return hdrs;
+}
+
+/**
+ * soup_message_headers_free:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Frees @hdrs.
+ **/
+void
+soup_message_headers_free (SoupMessageHeaders *hdrs)
+{
+ if (--hdrs->ref_count == 0) {
+ soup_message_headers_clear (hdrs);
+ g_array_free (hdrs->array, TRUE);
+ if (hdrs->concat)
+ g_hash_table_destroy (hdrs->concat);
+ g_slice_free (SoupMessageHeaders, hdrs);
+ }
+}
+
+GType
+soup_message_headers_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupMessageHeaders"),
+ (GBoxedCopyFunc) soup_message_headers_copy,
+ (GBoxedFreeFunc) soup_message_headers_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
+
+/**
+ * soup_message_headers_clear:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Clears @hdrs.
+ **/
+void
+soup_message_headers_clear (SoupMessageHeaders *hdrs)
+{
+ SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
+ int i;
+
+ for (i = 0; i < hdrs->array->len; i++)
+ g_free (hdr_array[i].value);
+ g_array_set_size (hdrs->array, 0);
+
+ if (hdrs->concat)
+ g_hash_table_remove_all (hdrs->concat);
+
+ clear_special_headers (hdrs);
+}
+
+/**
+ * soup_message_headers_clean_connection_headers:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Removes all the headers listed in the Connection header.
+ *
+ * Since: 2.36
+ */
+void
+soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs)
+{
+ /* RFC 2616 14.10 */
+ const char *connection;
+ GSList *tokens, *t;
+
+ connection = soup_message_headers_get_list (hdrs, "Connection");
+ if (!connection)
+ return;
+
+ tokens = soup_header_parse_list (connection);
+ for (t = tokens; t; t = t->next)
+ soup_message_headers_remove (hdrs, t->data);
+ soup_header_free_list (tokens);
+}
+
+/**
+ * soup_message_headers_append:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: the header name to add
+ * @value: the new value of @name
+ *
+ * Appends a new header with name @name and value @value to @hdrs. (If
+ * there is an existing header with name @name, then this creates a
+ * second one, which is only allowed for list-valued headers; see also
+ * soup_message_headers_replace().)
+ *
+ * The caller is expected to make sure that @name and @value are
+ * syntactically correct.
+ **/
+void
+soup_message_headers_append (SoupMessageHeaders *hdrs,
+ const char *name, const char *value)
+{
+ SoupHeader header;
+ SoupHeaderSetter setter;
+
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (value != NULL);
+
+ /* Setting a syntactically invalid header name or value is
+ * considered to be a programming error. However, it can also
+ * be a security hole, so we want to fail here even if
+ * compiled with G_DISABLE_CHECKS.
+ */
+#ifndef G_DISABLE_CHECKS
+ g_return_if_fail (*name && strpbrk (name, " \t\r\n:") == NULL);
+ g_return_if_fail (strpbrk (value, "\r\n") == NULL);
+#else
+ if (*name && strpbrk (name, " \t\r\n:")) {
+ g_warning ("soup_message_headers_append: Ignoring bad name '%s'", name);
+ return;
+ }
+ if (strpbrk (value, "\r\n")) {
+ g_warning ("soup_message_headers_append: Ignoring bad value '%s'", value);
+ return;
+ }
+#endif
+
+ header.name = intern_header_name (name, &setter);
+ header.value = g_strdup (value);
+ g_array_append_val (hdrs->array, header);
+ if (hdrs->concat)
+ g_hash_table_remove (hdrs->concat, header.name);
+ if (setter)
+ setter (hdrs, header.value);
+}
+
+/**
+ * soup_message_headers_replace:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: the header name to replace
+ * @value: the new value of @name
+ *
+ * Replaces the value of the header @name in @hdrs with @value. (See
+ * also soup_message_headers_append().)
+ *
+ * The caller is expected to make sure that @name and @value are
+ * syntactically correct.
+ **/
+void
+soup_message_headers_replace (SoupMessageHeaders *hdrs,
+ const char *name, const char *value)
+{
+ soup_message_headers_remove (hdrs, name);
+ soup_message_headers_append (hdrs, name, value);
+}
+
+static int
+find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
+{
+ int i;
+
+ for (i = 0; hdr_array[i].name; i++) {
+ if (hdr_array[i].name == interned_name) {
+ if (nth-- == 0)
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int
+find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name, int nth)
+{
+ int i;
+
+ for (i = length; i >= 0; i--) {
+ if (hdr_array[i].name == interned_name) {
+ if (nth-- == 0)
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * soup_message_headers_remove:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: the header name to remove
+ *
+ * Removes @name from @hdrs. If there are multiple values for @name,
+ * they are all removed.
+ **/
+void
+soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
+{
+ SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
+ SoupHeaderSetter setter;
+ int index;
+
+ g_return_if_fail (name != NULL);
+
+ name = intern_header_name (name, &setter);
+ while ((index = find_header (hdr_array, name, 0)) != -1) {
+ g_free (hdr_array[index].value);
+ g_array_remove_index (hdrs->array, index);
+ }
+ if (hdrs->concat)
+ g_hash_table_remove (hdrs->concat, name);
+ if (setter)
+ setter (hdrs, NULL);
+}
+
+/**
+ * soup_message_headers_get_one:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: header name
+ *
+ * Gets the value of header @name in @hdrs. Use this for headers whose
+ * values are <emphasis>not</emphasis> comma-delimited lists, and
+ * which therefore can only appear at most once in the headers. For
+ * list-valued headers, use soup_message_headers_get_list().
+ *
+ * If @hdrs does erroneously contain multiple copies of the header, it
+ * is not defined which one will be returned. (Ideally, it will return
+ * whichever one makes libsoup most compatible with other HTTP
+ * implementations.)
+ *
+ * Return value: the header's value or %NULL if not found.
+ *
+ * Since: 2.26.1
+ **/
+const char *
+soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
+{
+ SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
+ guint hdr_length = hdrs->array->len;
+ int index;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ name = intern_header_name (name, NULL);
+
+ index = find_last_header (hdr_array, hdr_length, name, 0);
+
+ return (index == -1) ? NULL : hdr_array[index].value;
+}
+
+/**
+ * soup_message_headers_get_list:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: header name
+ *
+ * Gets the value of header @name in @hdrs. Use this for headers whose
+ * values are comma-delimited lists, and which are therefore allowed
+ * to appear multiple times in the headers. For non-list-valued
+ * headers, use soup_message_headers_get_one().
+ *
+ * If @name appears multiple times in @hdrs,
+ * soup_message_headers_get_list() will concatenate all of the values
+ * together, separated by commas. This is sometimes awkward to parse
+ * (eg, WWW-Authenticate, Set-Cookie), but you have to be able to deal
+ * with it anyway, because the HTTP spec explicitly states that this
+ * transformation is allowed, and so an upstream proxy could do the
+ * same thing.
+ *
+ * Return value: the header's value or %NULL if not found.
+ *
+ * Since: 2.26.1
+ **/
+const char *
+soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name)
+{
+ SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
+ GString *concat;
+ char *value;
+ int index, i;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ name = intern_header_name (name, NULL);
+ if (hdrs->concat) {
+ value = g_hash_table_lookup (hdrs->concat, name);
+ if (value)
+ return value;
+ }
+
+ index = find_header (hdr_array, name, 0);
+ if (index == -1)
+ return NULL;
+ else if (find_header (hdr_array, name, 1) == -1)
+ return hdr_array[index].value;
+
+ concat = g_string_new (NULL);
+ for (i = 0; (index = find_header (hdr_array, name, i)) != -1; i++) {
+ if (i != 0)
+ g_string_append (concat, ", ");
+ g_string_append (concat, hdr_array[index].value);
+ }
+ value = g_string_free (concat, FALSE);
+
+ if (!hdrs->concat)
+ hdrs->concat = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+ g_hash_table_insert (hdrs->concat, (gpointer)name, value);
+ return value;
+}
+
+/**
+ * soup_message_headers_get:
+ * @hdrs: a #SoupMessageHeaders
+ * @name: header name
+ *
+ * Gets the value of header @name in @hdrs.
+ *
+ * This method was supposed to work correctly for both single-valued
+ * and list-valued headers, but because some HTTP clients/servers
+ * mistakenly send multiple copies of headers that are supposed to be
+ * single-valued, it sometimes returns incorrect results. To fix this,
+ * the methods soup_message_headers_get_one() and
+ * soup_message_headers_get_list() were introduced, so callers can
+ * explicitly state which behavior they are expecting.
+ *
+ * Return value: as with soup_message_headers_get_list().
+ *
+ * Deprecated: Use soup_message_headers_get_one() or
+ * soup_message_headers_get_list() instead.
+ **/
+const char *
+soup_message_headers_get (SoupMessageHeaders *hdrs, const char *name)
+{
+ return soup_message_headers_get_list (hdrs, name);
+}
+
+/**
+ * SoupMessageHeadersIter:
+ *
+ * An opaque type used to iterate over a %SoupMessageHeaders
+ * structure.
+ *
+ * After intializing the iterator with
+ * soup_message_headers_iter_init(), call
+ * soup_message_headers_iter_next() to fetch data from it.
+ *
+ * You may not modify the headers while iterating over them.
+ **/
+
+typedef struct {
+ SoupMessageHeaders *hdrs;
+ int index;
+} SoupMessageHeadersIterReal;
+
+/**
+ * soup_message_headers_iter_init:
+ * @iter: (out) (transfer none): a pointer to a %SoupMessageHeadersIter
+ * structure
+ * @hdrs: a %SoupMessageHeaders
+ *
+ * Initializes @iter for iterating @hdrs.
+ **/
+void
+soup_message_headers_iter_init (SoupMessageHeadersIter *iter,
+ SoupMessageHeaders *hdrs)
+{
+ SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
+
+ real->hdrs = hdrs;
+ real->index = 0;
+}
+
+/**
+ * soup_message_headers_iter_next:
+ * @iter: (inout) (transfer none): a %SoupMessageHeadersIter
+ * @name: (out) (transfer none): pointer to a variable to return
+ * the header name in
+ * @value: (out) (transfer none): pointer to a variable to return
+ * the header value in
+ *
+ * Yields the next name/value pair in the %SoupMessageHeaders being
+ * iterated by @iter. If @iter has already yielded the last header,
+ * then soup_message_headers_iter_next() will return %FALSE and @name
+ * and @value will be unchanged.
+ *
+ * Return value: %TRUE if another name and value were returned, %FALSE
+ * if the end of the headers has been reached.
+ **/
+gboolean
+soup_message_headers_iter_next (SoupMessageHeadersIter *iter,
+ const char **name, const char **value)
+{
+ SoupMessageHeadersIterReal *real = (SoupMessageHeadersIterReal *)iter;
+ SoupHeader *hdr_array = (SoupHeader *)real->hdrs->array->data;
+
+ if (real->index >= real->hdrs->array->len)
+ return FALSE;
+
+ *name = hdr_array[real->index].name;
+ *value = hdr_array[real->index].value;
+ real->index++;
+ return TRUE;
+}
+
+/**
+ * SoupMessageHeadersForeachFunc:
+ * @name: the header name
+ * @value: the header value
+ * @user_data: the data passed to soup_message_headers_foreach()
+ *
+ * The callback passed to soup_message_headers_foreach().
+ **/
+
+/**
+ * soup_message_headers_foreach:
+ * @hdrs: a #SoupMessageHeaders
+ * @func: (scope call): callback function to run for each header
+ * @user_data: data to pass to @func
+ *
+ * Calls @func once for each header value in @hdrs.
+ *
+ * Beware that unlike soup_message_headers_get(), this processes the
+ * headers in exactly the way they were added, rather than
+ * concatenating multiple same-named headers into a single value.
+ * (This is intentional; it ensures that if you call
+ * soup_message_headers_append() multiple times with the same name,
+ * then the I/O code will output multiple copies of the header when
+ * sending the message to the remote implementation, which may be
+ * required for interoperability in some cases.)
+ *
+ * You may not modify the headers from @func.
+ **/
+void
+soup_message_headers_foreach (SoupMessageHeaders *hdrs,
+ SoupMessageHeadersForeachFunc func,
+ gpointer user_data)
+{
+ SoupHeader *hdr_array = (SoupHeader *)hdrs->array->data;
+ int i;
+
+ for (i = 0; i < hdrs->array->len; i++)
+ func (hdr_array[i].name, hdr_array[i].value, user_data);
+}
+
+
+static GStaticMutex header_pool_mutex = G_STATIC_MUTEX_INIT;
+static GHashTable *header_pool, *header_setters;
+
+static void transfer_encoding_setter (SoupMessageHeaders *, const char *);
+static void content_length_setter (SoupMessageHeaders *, const char *);
+static void expectation_setter (SoupMessageHeaders *, const char *);
+static void content_type_setter (SoupMessageHeaders *, const char *);
+
+static char *
+intern_header_locked (const char *name)
+{
+ char *interned;
+
+ interned = g_hash_table_lookup (header_pool, name);
+ if (!interned) {
+ char *dup = g_strdup (name);
+ g_hash_table_insert (header_pool, dup, dup);
+ interned = dup;
+ }
+ return interned;
+}
+
+static const char *
+intern_header_name (const char *name, SoupHeaderSetter *setter)
+{
+ const char *interned;
+
+ g_static_mutex_lock (&header_pool_mutex);
+
+ if (!header_pool) {
+ header_pool = g_hash_table_new (soup_str_case_hash, soup_str_case_equal);
+ header_setters = g_hash_table_new (NULL, NULL);
+ g_hash_table_insert (header_setters,
+ intern_header_locked ("Transfer-Encoding"),
+ transfer_encoding_setter);
+ g_hash_table_insert (header_setters,
+ intern_header_locked ("Content-Length"),
+ content_length_setter);
+ g_hash_table_insert (header_setters,
+ intern_header_locked ("Expect"),
+ expectation_setter);
+ g_hash_table_insert (header_setters,
+ intern_header_locked ("Content-Type"),
+ content_type_setter);
+ }
+
+ interned = intern_header_locked (name);
+ if (setter)
+ *setter = g_hash_table_lookup (header_setters, interned);
+
+ g_static_mutex_unlock (&header_pool_mutex);
+ return interned;
+}
+
+static void
+clear_special_headers (SoupMessageHeaders *hdrs)
+{
+ SoupHeaderSetter setter;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ /* Make sure header_setters has been initialized */
+ intern_header_name ("", NULL);
+
+ g_hash_table_iter_init (&iter, header_setters);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ setter = value;
+ setter (hdrs, NULL);
+ }
+}
+
+/* Specific headers */
+
+static void
+transfer_encoding_setter (SoupMessageHeaders *hdrs, const char *value)
+{
+ if (value) {
+ if (g_ascii_strcasecmp (value, "chunked") == 0)
+ hdrs->encoding = SOUP_ENCODING_CHUNKED;
+ else
+ hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
+ } else
+ hdrs->encoding = -1;
+}
+
+static void
+content_length_setter (SoupMessageHeaders *hdrs, const char *value)
+{
+ /* Transfer-Encoding trumps Content-Length */
+ if (hdrs->encoding == SOUP_ENCODING_CHUNKED)
+ return;
+
+ if (value) {
+ char *end;
+
+ hdrs->content_length = g_ascii_strtoull (value, &end, 10);
+ if (*end)
+ hdrs->encoding = SOUP_ENCODING_UNRECOGNIZED;
+ else
+ hdrs->encoding = SOUP_ENCODING_CONTENT_LENGTH;
+ } else
+ hdrs->encoding = -1;
+}
+
+/**
+ * SoupEncoding:
+ * @SOUP_ENCODING_UNRECOGNIZED: unknown / error
+ * @SOUP_ENCODING_NONE: no body is present (which is not the same as a
+ * 0-length body, and only occurs in certain places)
+ * @SOUP_ENCODING_CONTENT_LENGTH: Content-Length encoding
+ * @SOUP_ENCODING_EOF: Response body ends when the connection is closed
+ * @SOUP_ENCODING_CHUNKED: chunked encoding (currently only supported
+ * for response)
+ * @SOUP_ENCODING_BYTERANGES: multipart/byteranges (Reserved for future
+ * use: NOT CURRENTLY IMPLEMENTED)
+ *
+ * How a message body is encoded for transport
+ **/
+
+/**
+ * soup_message_headers_get_encoding:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Gets the message body encoding that @hdrs declare. This may not
+ * always correspond to the encoding used on the wire; eg, a HEAD
+ * response may declare a Content-Length or Transfer-Encoding, but
+ * it will never actually include a body.
+ *
+ * Return value: the encoding declared by @hdrs.
+ **/
+SoupEncoding
+soup_message_headers_get_encoding (SoupMessageHeaders *hdrs)
+{
+ const char *header;
+
+ if (hdrs->encoding != -1)
+ return hdrs->encoding;
+
+ /* If Transfer-Encoding was set, hdrs->encoding would already
+ * be set. So we don't need to check that possibility.
+ */
+ header = soup_message_headers_get_one (hdrs, "Content-Length");
+ if (header) {
+ content_length_setter (hdrs, header);
+ if (hdrs->encoding != -1)
+ return hdrs->encoding;
+ }
+
+ /* Per RFC 2616 4.4, a response body that doesn't indicate its
+ * encoding otherwise is terminated by connection close, and a
+ * request that doesn't indicate otherwise has no body. Note
+ * that SoupMessage calls soup_message_headers_set_encoding()
+ * to override the response body default for our own
+ * server-side messages.
+ */
+ hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
+ SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
+ return hdrs->encoding;
+}
+
+/**
+ * soup_message_headers_set_encoding:
+ * @hdrs: a #SoupMessageHeaders
+ * @encoding: a #SoupEncoding
+ *
+ * Sets the message body encoding that @hdrs will declare. In particular,
+ * you should use this if you are going to send a request or response in
+ * chunked encoding.
+ **/
+void
+soup_message_headers_set_encoding (SoupMessageHeaders *hdrs,
+ SoupEncoding encoding)
+{
+ if (encoding == hdrs->encoding)
+ return;
+
+ switch (encoding) {
+ case SOUP_ENCODING_NONE:
+ case SOUP_ENCODING_EOF:
+ soup_message_headers_remove (hdrs, "Transfer-Encoding");
+ soup_message_headers_remove (hdrs, "Content-Length");
+ break;
+
+ case SOUP_ENCODING_CONTENT_LENGTH:
+ soup_message_headers_remove (hdrs, "Transfer-Encoding");
+ break;
+
+ case SOUP_ENCODING_CHUNKED:
+ soup_message_headers_remove (hdrs, "Content-Length");
+ soup_message_headers_replace (hdrs, "Transfer-Encoding", "chunked");
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ hdrs->encoding = encoding;
+}
+
+/**
+ * soup_message_headers_get_content_length:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Gets the message body length that @hdrs declare. This will only
+ * be non-0 if soup_message_headers_get_encoding() returns
+ * %SOUP_ENCODING_CONTENT_LENGTH.
+ *
+ * Return value: the message body length declared by @hdrs.
+ **/
+goffset
+soup_message_headers_get_content_length (SoupMessageHeaders *hdrs)
+{
+ return (hdrs->encoding == SOUP_ENCODING_CONTENT_LENGTH) ?
+ hdrs->content_length : 0;
+}
+
+/**
+ * soup_message_headers_set_content_length:
+ * @hdrs: a #SoupMessageHeaders
+ * @content_length: the message body length
+ *
+ * Sets the message body length that @hdrs will declare, and sets
+ * @hdrs's encoding to %SOUP_ENCODING_CONTENT_LENGTH.
+ *
+ * You do not normally need to call this; if @hdrs is set to use
+ * Content-Length encoding, libsoup will automatically set its
+ * Content-Length header for you immediately before sending the
+ * headers. One situation in which this method is useful is when
+ * generating the response to a HEAD request; Calling
+ * soup_message_headers_set_content_length() allows you to put the
+ * correct content length into the response without needing to waste
+ * memory by filling in a response body which won't actually be sent.
+ **/
+void
+soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
+ goffset content_length)
+{
+ char length[128];
+
+ snprintf (length, sizeof (length), "%" G_GUINT64_FORMAT,
+ content_length);
+ soup_message_headers_remove (hdrs, "Transfer-Encoding");
+ soup_message_headers_replace (hdrs, "Content-Length", length);
+}
+
+static void
+expectation_setter (SoupMessageHeaders *hdrs, const char *value)
+{
+ if (value) {
+ if (!g_ascii_strcasecmp (value, "100-continue"))
+ hdrs->expectations = SOUP_EXPECTATION_CONTINUE;
+ else
+ hdrs->expectations = SOUP_EXPECTATION_UNRECOGNIZED;
+ } else
+ hdrs->expectations = 0;
+}
+
+/**
+ * SoupExpectation:
+ * @SOUP_EXPECTATION_CONTINUE: "100-continue"
+ * @SOUP_EXPECTATION_UNRECOGNIZED: any unrecognized expectation
+ *
+ * Represents the parsed value of the "Expect" header.
+ **/
+
+/**
+ * soup_message_headers_get_expectations:
+ * @hdrs: a #SoupMessageHeaders
+ *
+ * Gets the expectations declared by @hdrs's "Expect" header.
+ * Currently this will either be %SOUP_EXPECTATION_CONTINUE or
+ * %SOUP_EXPECTATION_UNRECOGNIZED.
+ *
+ * Return value: the contents of @hdrs's "Expect" header
+ **/
+SoupExpectation
+soup_message_headers_get_expectations (SoupMessageHeaders *hdrs)
+{
+ return hdrs->expectations;
+}
+
+/**
+ * soup_message_headers_set_expectations:
+ * @hdrs: a #SoupMessageHeaders
+ * @expectations: the expectations to set
+ *
+ * Sets @hdrs's "Expect" header according to @expectations.
+ *
+ * Currently %SOUP_EXPECTATION_CONTINUE is the only known expectation
+ * value. You should set this value on a request if you are sending a
+ * large message body (eg, via POST or PUT), and want to give the
+ * server a chance to reject the request after seeing just the headers
+ * (eg, because it will require authentication before allowing you to
+ * post, or because you're POSTing to a URL that doesn't exist). This
+ * saves you from having to transmit the large request body when the
+ * server is just going to ignore it anyway.
+ **/
+void
+soup_message_headers_set_expectations (SoupMessageHeaders *hdrs,
+ SoupExpectation expectations)
+{
+ g_return_if_fail ((expectations & ~SOUP_EXPECTATION_CONTINUE) == 0);
+
+ if (expectations & SOUP_EXPECTATION_CONTINUE)
+ soup_message_headers_replace (hdrs, "Expect", "100-continue");
+ else
+ soup_message_headers_remove (hdrs, "Expect");
+}
+
+/**
+ * SoupRange:
+ * @start: the start of the range
+ * @end: the end of the range
+ *
+ * Represents a byte range as used in the Range header.
+ *
+ * If @end is non-negative, then @start and @end represent the bounds
+ * of of the range, counting from %0. (Eg, the first 500 bytes would be
+ * represented as @start = %0 and @end = %499.)
+ *
+ * If @end is %-1 and @start is non-negative, then this represents a
+ * range starting at @start and ending with the last byte of the
+ * requested resource body. (Eg, all but the first 500 bytes would be
+ * @start = %500, and @end = %-1.)
+ *
+ * If @end is %-1 and @start is negative, then it represents a "suffix
+ * range", referring to the last -@start bytes of the resource body.
+ * (Eg, the last 500 bytes would be @start = %-500 and @end = %-1.)
+ *
+ * Since: 2.26
+ **/
+
+static int
+sort_ranges (gconstpointer a, gconstpointer b)
+{
+ SoupRange *ra = (SoupRange *)a;
+ SoupRange *rb = (SoupRange *)b;
+
+ return ra->start - rb->start;
+}
+
+/**
+ * soup_message_headers_get_ranges:
+ * @hdrs: a #SoupMessageHeaders
+ * @total_length: the total_length of the response body
+ * @ranges: (out): return location for an array of #SoupRange
+ * @length: the length of the returned array
+ *
+ * Parses @hdrs's Range header and returns an array of the requested
+ * byte ranges. The returned array must be freed with
+ * soup_message_headers_free_ranges().
+ *
+ * If @total_length is non-0, its value will be used to adjust the
+ * returned ranges to have explicit start and end values, and the
+ * returned ranges will be sorted and non-overlapping. If
+ * @total_length is 0, then some ranges may have an end value of -1,
+ * as described under #SoupRange, and some of the ranges may be
+ * redundant.
+ *
+ * Return value: %TRUE if @hdrs contained a "Range" header containing
+ * byte ranges which could be parsed, %FALSE otherwise (in which case
+ * @range and @length will not be set).
+ *
+ * Since: 2.26
+ **/
+gboolean
+soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
+ goffset total_length,
+ SoupRange **ranges,
+ int *length)
+{
+ const char *range = soup_message_headers_get_one (hdrs, "Range");
+ GSList *range_list, *r;
+ GArray *array;
+ char *spec, *end;
+ int i;
+
+ if (!range || strncmp (range, "bytes", 5) != 0)
+ return FALSE;
+
+ range += 5;
+ while (g_ascii_isspace (*range))
+ range++;
+ if (*range++ != '=')
+ return FALSE;
+ while (g_ascii_isspace (*range))
+ range++;
+
+ range_list = soup_header_parse_list (range);
+ if (!range_list)
+ return FALSE;
+
+ array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
+ for (r = range_list; r; r = r->next) {
+ SoupRange cur;
+
+ spec = r->data;
+ if (*spec == '-') {
+ cur.start = g_ascii_strtoll (spec, &end, 10) + total_length;
+ cur.end = total_length - 1;
+ } else {
+ cur.start = g_ascii_strtoull (spec, &end, 10);
+ if (*end == '-')
+ end++;
+ if (*end)
+ cur.end = g_ascii_strtoull (end, &end, 10);
+ else
+ cur.end = total_length - 1;
+ }
+ if (*end) {
+ g_array_free (array, TRUE);
+ soup_header_free_list (range_list);
+ return FALSE;
+ }
+
+ g_array_append_val (array, cur);
+ }
+
+ soup_header_free_list (range_list);
+
+ if (total_length) {
+ g_array_sort (array, sort_ranges);
+ for (i = 1; i < array->len; i++) {
+ SoupRange *cur = &((SoupRange *)array->data)[i];
+ SoupRange *prev = &((SoupRange *)array->data)[i - 1];
+
+ if (cur->start <= prev->end) {
+ prev->end = MAX (prev->end, cur->end);
+ g_array_remove_index (array, i);
+ }
+ }
+ }
+
+ *ranges = (SoupRange *)array->data;
+ *length = array->len;
+
+ g_array_free (array, FALSE);
+ return TRUE;
+}
+
+/**
+ * soup_message_headers_free_ranges:
+ * @hdrs: a #SoupMessageHeaders
+ * @ranges: an array of #SoupRange
+ *
+ * Frees the array of ranges returned from soup_message_headers_get_ranges().
+ *
+ * Since: 2.26
+ **/
+void
+soup_message_headers_free_ranges (SoupMessageHeaders *hdrs,
+ SoupRange *ranges)
+{
+ g_free (ranges);
+}
+
+/**
+ * soup_message_headers_set_ranges:
+ * @hdrs: a #SoupMessageHeaders
+ * @ranges: an array of #SoupRange
+ * @length: the length of @range
+ *
+ * Sets @hdrs's Range header to request the indicated ranges. (If you
+ * only want to request a single range, you can use
+ * soup_message_headers_set_range().)
+ *
+ * Since: 2.26
+ **/
+void
+soup_message_headers_set_ranges (SoupMessageHeaders *hdrs,
+ SoupRange *ranges,
+ int length)
+{
+ GString *header;
+ int i;
+
+ header = g_string_new ("bytes=");
+ for (i = 0; i < length; i++) {
+ if (i > 0)
+ g_string_append_c (header, ',');
+ if (ranges[i].end >= 0) {
+ g_string_append_printf (header, "%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT,
+ ranges[i].start, ranges[i].end);
+ } else if (ranges[i].start >= 0) {
+ g_string_append_printf (header,"%" G_GINT64_FORMAT "-",
+ ranges[i].start);
+ } else {
+ g_string_append_printf (header, "%" G_GINT64_FORMAT,
+ ranges[i].start);
+ }
+ }
+
+ soup_message_headers_replace (hdrs, "Range", header->str);
+ g_string_free (header, TRUE);
+}
+
+/**
+ * soup_message_headers_set_range:
+ * @hdrs: a #SoupMessageHeaders
+ * @start: the start of the range to request
+ * @end: the end of the range to request
+ *
+ * Sets @hdrs's Range header to request the indicated range.
+ * @start and @end are interpreted as in a #SoupRange.
+ *
+ * If you need to request multiple ranges, use
+ * soup_message_headers_set_ranges().
+ *
+ * Since: 2.26
+ **/
+void
+soup_message_headers_set_range (SoupMessageHeaders *hdrs,
+ goffset start,
+ goffset end)
+{
+ SoupRange range;
+
+ range.start = start;
+ range.end = end;
+ soup_message_headers_set_ranges (hdrs, &range, 1);
+}
+
+/**
+ * soup_message_headers_get_content_range:
+ * @hdrs: a #SoupMessageHeaders
+ * @start: return value for the start of the range
+ * @end: return value for the end of the range
+ * @total_length: return value for the total length of the resource,
+ * or %NULL if you don't care.
+ *
+ * Parses @hdrs's Content-Range header and returns it in @start,
+ * @end, and @total_length. If the total length field in the header
+ * was specified as "*", then @total_length will be set to -1.
+ *
+ * Return value: %TRUE if @hdrs contained a "Content-Range" header
+ * containing a byte range which could be parsed, %FALSE otherwise.
+ *
+ * Since: 2.26
+ **/
+gboolean
+soup_message_headers_get_content_range (SoupMessageHeaders *hdrs,
+ goffset *start,
+ goffset *end,
+ goffset *total_length)
+{
+ const char *header = soup_message_headers_get_one (hdrs, "Content-Range");
+ goffset length;
+ char *p;
+
+ if (!header || strncmp (header, "bytes ", 6) != 0)
+ return FALSE;
+
+ header += 6;
+ while (g_ascii_isspace (*header))
+ header++;
+ if (!g_ascii_isdigit (*header))
+ return FALSE;
+
+ *start = g_ascii_strtoull (header, &p, 10);
+ if (*p != '-')
+ return FALSE;
+ *end = g_ascii_strtoull (p + 1, &p, 10);
+ if (*p != '/')
+ return FALSE;
+ p++;
+ if (*p == '*') {
+ length = -1;
+ p++;
+ } else
+ length = g_ascii_strtoull (p, &p, 10);
+
+ if (total_length)
+ *total_length = length;
+ return *p == '\0';
+}
+
+/**
+ * soup_message_headers_set_content_range:
+ * @hdrs: a #SoupMessageHeaders
+ * @start: the start of the range
+ * @end: the end of the range
+ * @total_length: the total length of the resource, or -1 if unknown
+ *
+ * Sets @hdrs's Content-Range header according to the given values.
+ * (Note that @total_length is the total length of the entire resource
+ * that this is a range of, not simply @end - @start + 1.)
+ *
+ * Since: 2.26
+ **/
+void
+soup_message_headers_set_content_range (SoupMessageHeaders *hdrs,
+ goffset start,
+ goffset end,
+ goffset total_length)
+{
+ char *header;
+
+ if (total_length >= 0) {
+ header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
+ G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
+ start, end, total_length);
+ } else {
+ header = g_strdup_printf ("bytes %" G_GINT64_FORMAT "-%"
+ G_GINT64_FORMAT "/*", start, end);
+ }
+ soup_message_headers_replace (hdrs, "Content-Range", header);
+ g_free (header);
+}
+
+static gboolean
+parse_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
+ char **foo, GHashTable **params)
+{
+ const char *header;
+ char *semi;
+
+ header = soup_message_headers_get_one (hdrs, header_name);
+ if (!header)
+ return FALSE;
+
+ if (foo) {
+ *foo = g_strdup (header);
+ semi = strchr (*foo, ';');
+ if (semi) {
+ char *p = semi;
+
+ *semi++ = '\0';
+ while (p - 1 > *foo && g_ascii_isspace(p[-1]))
+ *(--p) = '\0';
+ }
+ } else {
+ semi = strchr (header, ';');
+ if (semi)
+ semi++;
+ }
+
+ if (!params)
+ return TRUE;
+
+ if (!semi) {
+ *params = soup_header_parse_semi_param_list ("");
+ return TRUE;
+ }
+
+ *params = soup_header_parse_semi_param_list (semi);
+ return TRUE;
+}
+
+static void
+set_content_foo (SoupMessageHeaders *hdrs, const char *header_name,
+ const char *foo, GHashTable *params)
+{
+ GString *str;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ str = g_string_new (foo);
+ if (params) {
+ g_hash_table_iter_init (&iter, params);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ g_string_append (str, "; ");
+ soup_header_g_string_append_param (str, key, value);
+ }
+ }
+
+ soup_message_headers_replace (hdrs, header_name, str->str);
+ g_string_free (str, TRUE);
+}
+
+static void
+content_type_setter (SoupMessageHeaders *hdrs, const char *value)
+{
+ g_free (hdrs->content_type);
+ if (value) {
+ char *content_type, *p;
+
+ parse_content_foo (hdrs, "Content-Type", &content_type, NULL);
+ p = strpbrk (content_type, " /");
+ if (!p || *p != '/' || strpbrk (p + 1, " /")) {
+ g_free (content_type);
+ hdrs->content_type = NULL;
+ } else
+ hdrs->content_type = content_type;
+ } else
+ hdrs->content_type = NULL;
+}
+
+/**
+ * soup_message_headers_get_content_type:
+ * @hdrs: a #SoupMessageHeaders
+ * @params: (out) (allow-none) (transfer full): return location for
+ * the Content-Type parameters (eg, "charset"), or %NULL
+ *
+ * Looks up the "Content-Type" header in @hdrs, parses it, and returns
+ * its value in *@content_type and *@params. @params can be %NULL if you
+ * are only interested in the content type itself.
+ *
+ * Return value: a string with the value of the "Content-Type" header
+ * or NULL if @hdrs does not contain that header or it cannot be
+ * parsed (in which case *@params will be unchanged).
+ *
+ * Since: 2.26
+ **/
+const char *
+soup_message_headers_get_content_type (SoupMessageHeaders *hdrs,
+ GHashTable **params)
+{
+ if (!hdrs->content_type)
+ return NULL;
+
+ if (params)
+ parse_content_foo (hdrs, "Content-Type", NULL, params);
+ return hdrs->content_type;
+}
+
+/**
+ * soup_message_headers_set_content_type:
+ * @hdrs: a #SoupMessageHeaders
+ * @content_type: the MIME type
+ * @params: (allow-none) (element-type utf8 utf8): additional
+ * parameters, or %NULL
+ *
+ * Sets the "Content-Type" header in @hdrs to @content_type,
+ * optionally with additional parameters specified in @params.
+ *
+ * Since: 2.26
+ **/
+void
+soup_message_headers_set_content_type (SoupMessageHeaders *hdrs,
+ const char *content_type,
+ GHashTable *params)
+{
+ set_content_foo (hdrs, "Content-Type", content_type, params);
+}
+
+/**
+ * soup_message_headers_get_content_disposition:
+ * @hdrs: a #SoupMessageHeaders
+ * @disposition: (out) (transfer full): return location for the
+ * disposition-type, or %NULL
+ * @params: (out) (transfer full) (element-type utf8 utf8): return
+ * location for the Content-Disposition parameters, or %NULL
+ *
+ * Looks up the "Content-Disposition" header in @hdrs, parses it, and
+ * returns its value in *@disposition and *@params. @params can be
+ * %NULL if you are only interested in the disposition-type.
+ *
+ * In HTTP, the most common use of this header is to set a
+ * disposition-type of "attachment", to suggest to the browser that a
+ * response should be saved to disk rather than displayed in the
+ * browser. If @params contains a "filename" parameter, this is a
+ * suggestion of a filename to use. (If the parameter value in the
+ * header contains an absolute or relative path, libsoup will truncate
+ * it down to just the final path component, so you do not need to
+ * test this yourself.)
+ *
+ * Content-Disposition is also used in "multipart/form-data", however
+ * this is handled automatically by #SoupMultipart and the associated
+ * form methods.
+ *
+ * Return value: %TRUE if @hdrs contains a "Content-Disposition"
+ * header, %FALSE if not (in which case *@disposition and *@params
+ * will be unchanged).
+ *
+ * Since: 2.26
+ **/
+gboolean
+soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs,
+ char **disposition,
+ GHashTable **params)
+{
+ gpointer orig_key, orig_value;
+
+ if (!parse_content_foo (hdrs, "Content-Disposition",
+ disposition, params))
+ return FALSE;
+
+ /* If there is a filename parameter, make sure it contains
+ * only a single path component
+ */
+ if (params && g_hash_table_lookup_extended (*params, "filename",
+ &orig_key, &orig_value)) {
+ char *filename = strrchr (orig_value, '/');
+
+ if (filename)
+ g_hash_table_insert (*params, orig_key, filename + 1);
+ }
+ return TRUE;
+}
+
+/**
+ * soup_message_headers_set_content_disposition:
+ * @hdrs: a #SoupMessageHeaders
+ * @disposition: the disposition-type
+ * @params: (allow-none) (element-type utf8 utf8): additional
+ * parameters, or %NULL
+ *
+ * Sets the "Content-Disposition" header in @hdrs to @disposition,
+ * optionally with additional parameters specified in @params.
+ *
+ * See soup_message_headers_get_content_disposition() for a discussion
+ * of how Content-Disposition is used in HTTP.
+ *
+ * Since: 2.26
+ **/
+void
+soup_message_headers_set_content_disposition (SoupMessageHeaders *hdrs,
+ const char *disposition,
+ GHashTable *params)
+{
+ set_content_foo (hdrs, "Content-Disposition", disposition, params);
+}
+
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005 Novell, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_HEADERS_H
+#define SOUP_MESSAGE_HEADERS_H 1
+
+#include <libsoup/soup-types.h>
+
+typedef struct SoupMessageHeaders SoupMessageHeaders;
+GType soup_message_headers_get_type (void);
+#define SOUP_TYPE_MESSAGE_HEADERS (soup_message_headers_get_type ())
+
+typedef enum {
+ SOUP_MESSAGE_HEADERS_REQUEST,
+ SOUP_MESSAGE_HEADERS_RESPONSE,
+ SOUP_MESSAGE_HEADERS_MULTIPART
+} SoupMessageHeadersType;
+
+SoupMessageHeaders *soup_message_headers_new (SoupMessageHeadersType type);
+
+void soup_message_headers_free (SoupMessageHeaders *hdrs);
+
+void soup_message_headers_append (SoupMessageHeaders *hdrs,
+ const char *name,
+ const char *value);
+void soup_message_headers_replace (SoupMessageHeaders *hdrs,
+ const char *name,
+ const char *value);
+
+void soup_message_headers_remove (SoupMessageHeaders *hdrs,
+ const char *name);
+void soup_message_headers_clear (SoupMessageHeaders *hdrs);
+
+void soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs);
+
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+const char *soup_message_headers_get (SoupMessageHeaders *hdrs,
+ const char *name);
+#endif
+const char *soup_message_headers_get_one (SoupMessageHeaders *hdrs,
+ const char *name);
+const char *soup_message_headers_get_list (SoupMessageHeaders *hdrs,
+ const char *name);
+
+typedef void (*SoupMessageHeadersForeachFunc)(const char *name,
+ const char *value,
+ gpointer user_data);
+
+void soup_message_headers_foreach (SoupMessageHeaders *hdrs,
+ SoupMessageHeadersForeachFunc func,
+ gpointer user_data);
+
+typedef struct {
+ /*< private >*/
+ gpointer dummy[3];
+} SoupMessageHeadersIter;
+
+void soup_message_headers_iter_init (SoupMessageHeadersIter *iter,
+ SoupMessageHeaders *hdrs);
+gboolean soup_message_headers_iter_next (SoupMessageHeadersIter *iter,
+ const char **name,
+ const char **value);
+
+/* Specific headers */
+
+typedef enum {
+ SOUP_ENCODING_UNRECOGNIZED,
+ SOUP_ENCODING_NONE,
+ SOUP_ENCODING_CONTENT_LENGTH,
+ SOUP_ENCODING_EOF,
+ SOUP_ENCODING_CHUNKED,
+ SOUP_ENCODING_BYTERANGES
+} SoupEncoding;
+
+SoupEncoding soup_message_headers_get_encoding (SoupMessageHeaders *hdrs);
+void soup_message_headers_set_encoding (SoupMessageHeaders *hdrs,
+ SoupEncoding encoding);
+
+goffset soup_message_headers_get_content_length (SoupMessageHeaders *hdrs);
+void soup_message_headers_set_content_length (SoupMessageHeaders *hdrs,
+ goffset content_length);
+
+typedef enum {
+ SOUP_EXPECTATION_UNRECOGNIZED = (1 << 0),
+ SOUP_EXPECTATION_CONTINUE = (1 << 1)
+} SoupExpectation;
+
+SoupExpectation soup_message_headers_get_expectations (SoupMessageHeaders *hdrs);
+void soup_message_headers_set_expectations (SoupMessageHeaders *hdrs,
+ SoupExpectation expectations);
+
+typedef struct {
+ goffset start;
+ goffset end;
+} SoupRange;
+
+gboolean soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
+ goffset total_length,
+ SoupRange **ranges,
+ int *length);
+void soup_message_headers_free_ranges (SoupMessageHeaders *hdrs,
+ SoupRange *ranges);
+void soup_message_headers_set_ranges (SoupMessageHeaders *hdrs,
+ SoupRange *ranges,
+ int length);
+void soup_message_headers_set_range (SoupMessageHeaders *hdrs,
+ goffset start,
+ goffset end);
+
+gboolean soup_message_headers_get_content_range (SoupMessageHeaders *hdrs,
+ goffset *start,
+ goffset *end,
+ goffset *total_length);
+void soup_message_headers_set_content_range (SoupMessageHeaders *hdrs,
+ goffset start,
+ goffset end,
+ goffset total_length);
+
+
+const char *soup_message_headers_get_content_type (SoupMessageHeaders *hdrs,
+ GHashTable **params);
+void soup_message_headers_set_content_type (SoupMessageHeaders *hdrs,
+ const char *content_type,
+ GHashTable *params);
+
+gboolean soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs,
+ char **disposition,
+ GHashTable **params);
+void soup_message_headers_set_content_disposition (SoupMessageHeaders *hdrs,
+ const char *disposition,
+ GHashTable *params);
+
+#endif /* SOUP_MESSAGE_HEADERS_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-io.c: HTTP message I/O
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-connection.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-message-queue.h"
+#include "soup-misc.h"
+#include "soup-socket.h"
+#include "soup-ssl.h"
+
+typedef enum {
+ SOUP_MESSAGE_IO_CLIENT,
+ SOUP_MESSAGE_IO_SERVER
+} SoupMessageIOMode;
+
+typedef enum {
+ SOUP_MESSAGE_IO_STATE_NOT_STARTED,
+ SOUP_MESSAGE_IO_STATE_HEADERS,
+ SOUP_MESSAGE_IO_STATE_BLOCKING,
+ SOUP_MESSAGE_IO_STATE_BODY,
+ SOUP_MESSAGE_IO_STATE_CHUNK_SIZE,
+ SOUP_MESSAGE_IO_STATE_CHUNK,
+ SOUP_MESSAGE_IO_STATE_CHUNK_END,
+ SOUP_MESSAGE_IO_STATE_TRAILERS,
+ SOUP_MESSAGE_IO_STATE_FINISHING,
+ SOUP_MESSAGE_IO_STATE_DONE
+} SoupMessageIOState;
+
+#define SOUP_MESSAGE_IO_STATE_ACTIVE(state) \
+ (state != SOUP_MESSAGE_IO_STATE_NOT_STARTED && \
+ state != SOUP_MESSAGE_IO_STATE_BLOCKING && \
+ state != SOUP_MESSAGE_IO_STATE_DONE)
+
+typedef struct {
+ SoupSocket *sock;
+ SoupMessageQueueItem *item;
+ SoupMessageIOMode mode;
+ GCancellable *cancellable;
+
+ SoupMessageIOState read_state;
+ SoupEncoding read_encoding;
+ GByteArray *read_meta_buf;
+ SoupMessageBody *read_body;
+ goffset read_length;
+ gboolean read_eof_ok;
+
+ gboolean need_content_sniffed, need_got_chunk;
+ SoupMessageBody *sniff_data;
+
+ SoupMessageIOState write_state;
+ SoupEncoding write_encoding;
+ GString *write_buf;
+ SoupMessageBody *write_body;
+ SoupBuffer *write_chunk;
+ goffset write_body_offset;
+ goffset write_length;
+ goffset written;
+
+ guint read_tag, write_tag, tls_signal_id;
+ GSource *unpause_source;
+
+ SoupMessageGetHeadersFn get_headers_cb;
+ SoupMessageParseHeadersFn parse_headers_cb;
+ gpointer header_data;
+ SoupMessageCompletionFn completion_cb;
+ gpointer completion_data;
+} SoupMessageIOData;
+
+
+/* Put these around callback invocation if there is code afterward
+ * that depends on the IO having not been cancelled.
+ */
+#define dummy_to_make_emacs_happy {
+#define SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK { gboolean cancelled; g_object_ref (msg);
+#define SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED cancelled = (priv->io_data != io); g_object_unref (msg); if (cancelled || (!io->read_tag && !io->write_tag)) return; }
+#define SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED(val) cancelled = (priv->io_data != io); g_object_unref (msg); if (cancelled || (!io->read_tag && !io->write_tag)) return val; }
+
+#define RESPONSE_BLOCK_SIZE 8192
+
+void
+soup_message_io_cleanup (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io;
+
+ soup_message_io_stop (msg);
+
+ io = priv->io_data;
+ if (!io)
+ return;
+ priv->io_data = NULL;
+
+ if (io->tls_signal_id)
+ g_signal_handler_disconnect (io->sock, io->tls_signal_id);
+ if (io->sock)
+ g_object_unref (io->sock);
+ if (io->item)
+ soup_message_queue_item_unref (io->item);
+
+ g_byte_array_free (io->read_meta_buf, TRUE);
+
+ g_string_free (io->write_buf, TRUE);
+ if (io->write_chunk)
+ soup_buffer_free (io->write_chunk);
+
+ if (io->sniff_data)
+ soup_message_body_free (io->sniff_data);
+
+ g_slice_free (SoupMessageIOData, io);
+}
+
+void
+soup_message_io_stop (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+
+ if (!io)
+ return;
+
+ if (io->read_tag) {
+ g_signal_handler_disconnect (io->sock, io->read_tag);
+ io->read_tag = 0;
+ }
+ if (io->write_tag) {
+ g_signal_handler_disconnect (io->sock, io->write_tag);
+ io->write_tag = 0;
+ }
+
+ if (io->unpause_source) {
+ g_source_destroy (io->unpause_source);
+ io->unpause_source = NULL;
+ }
+
+ if (io->read_state < SOUP_MESSAGE_IO_STATE_FINISHING)
+ soup_socket_disconnect (io->sock);
+ else if (io->item && io->item->conn)
+ soup_connection_set_state (io->item->conn, SOUP_CONNECTION_IDLE);
+}
+
+#define SOUP_MESSAGE_IO_EOL "\r\n"
+#define SOUP_MESSAGE_IO_EOL_LEN 2
+
+void
+soup_message_io_finished (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ SoupMessageCompletionFn completion_cb = io->completion_cb;
+ gpointer completion_data = io->completion_data;
+
+ g_object_ref (msg);
+ soup_message_io_cleanup (msg);
+ if (completion_cb)
+ completion_cb (msg, completion_data);
+ g_object_unref (msg);
+}
+
+static void io_read (SoupSocket *sock, SoupMessage *msg);
+
+static gboolean
+request_is_idempotent (SoupMessage *msg)
+{
+ /* FIXME */
+ return (msg->method == SOUP_METHOD_GET);
+}
+
+static void
+io_error (SoupSocket *sock, SoupMessage *msg, GError *error)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+
+ if (error && error->domain == G_TLS_ERROR) {
+ soup_message_set_status_full (msg,
+ SOUP_STATUS_SSL_FAILED,
+ error->message);
+ } else if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+ io->read_state <= SOUP_MESSAGE_IO_STATE_HEADERS &&
+ io->read_meta_buf->len == 0 &&
+ soup_connection_get_ever_used (io->item->conn) &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT) &&
+ request_is_idempotent (msg)) {
+ /* Connection got closed, but we can safely try again */
+ io->item->state = SOUP_MESSAGE_RESTARTING;
+ } else if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
+ soup_message_set_status (msg, SOUP_STATUS_IO_ERROR);
+
+ if (error)
+ g_error_free (error);
+
+ soup_message_io_finished (msg);
+}
+
+static gboolean
+io_handle_sniffing (SoupMessage *msg, gboolean done_reading)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ SoupBuffer *sniffed_buffer;
+ char *sniffed_mime_type;
+ GHashTable *params = NULL;
+
+ if (!priv->sniffer)
+ return TRUE;
+
+ if (!io->sniff_data) {
+ io->sniff_data = soup_message_body_new ();
+ io->need_content_sniffed = TRUE;
+ }
+
+ if (io->need_content_sniffed) {
+ if (io->sniff_data->length < priv->bytes_for_sniffing &&
+ !done_reading)
+ return TRUE;
+
+ io->need_content_sniffed = FALSE;
+ sniffed_buffer = soup_message_body_flatten (io->sniff_data);
+ sniffed_mime_type = soup_content_sniffer_sniff (priv->sniffer, msg, sniffed_buffer, ¶ms);
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_content_sniffed (msg, sniffed_mime_type, params);
+ g_free (sniffed_mime_type);
+ if (params)
+ g_hash_table_destroy (params);
+ if (sniffed_buffer)
+ soup_buffer_free (sniffed_buffer);
+ SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
+ }
+
+ if (io->need_got_chunk) {
+ io->need_got_chunk = FALSE;
+ sniffed_buffer = soup_message_body_flatten (io->sniff_data);
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_got_chunk (msg, sniffed_buffer);
+ soup_buffer_free (sniffed_buffer);
+ SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
+ }
+
+ return TRUE;
+}
+
+/* Reads data from io->sock into io->read_meta_buf. If @to_blank is
+ * %TRUE, it reads up until a blank line ("CRLF CRLF" or "LF LF").
+ * Otherwise, it reads up until a single CRLF or LF.
+ *
+ * This function is used to read metadata, and read_body_chunk() is
+ * used to read the message body contents.
+ *
+ * read_metadata, read_body_chunk, and write_data all use the same
+ * convention for return values: if they return %TRUE, it means
+ * they've completely finished the requested read/write, and the
+ * caller should move on to the next step. If they return %FALSE, it
+ * means that either (a) the socket returned SOUP_SOCKET_WOULD_BLOCK,
+ * so the caller should give up for now and wait for the socket to
+ * emit a signal, or (b) the socket returned an error, and io_error()
+ * was called to process it and cancel the I/O. So either way, if the
+ * function returns %FALSE, the caller should return immediately.
+ */
+static gboolean
+read_metadata (SoupMessage *msg, gboolean to_blank)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ SoupSocketIOStatus status;
+ guchar read_buf[RESPONSE_BLOCK_SIZE];
+ gsize nread;
+ gboolean got_lf;
+ GError *error = NULL;
+
+ while (1) {
+ status = soup_socket_read_until (io->sock, read_buf,
+ sizeof (read_buf),
+ "\n", 1, &nread, &got_lf,
+ io->cancellable, &error);
+ switch (status) {
+ case SOUP_SOCKET_OK:
+ g_byte_array_append (io->read_meta_buf, read_buf, nread);
+ break;
+
+ case SOUP_SOCKET_EOF:
+ /* More lame server handling... deal with
+ * servers that don't send the final chunk.
+ */
+ if (io->read_state == SOUP_MESSAGE_IO_STATE_CHUNK_SIZE &&
+ io->read_meta_buf->len == 0) {
+ g_byte_array_append (io->read_meta_buf,
+ (guchar *)"0\r\n", 3);
+ got_lf = TRUE;
+ break;
+ } else if (io->read_state == SOUP_MESSAGE_IO_STATE_TRAILERS &&
+ io->read_meta_buf->len == 0) {
+ g_byte_array_append (io->read_meta_buf,
+ (guchar *)"\r\n", 2);
+ got_lf = TRUE;
+ break;
+ }
+ /* else fall through */
+
+ case SOUP_SOCKET_ERROR:
+ io_error (io->sock, msg, error);
+ return FALSE;
+
+ case SOUP_SOCKET_WOULD_BLOCK:
+ return FALSE;
+ }
+
+ if (got_lf) {
+ if (!to_blank)
+ break;
+ if (nread == 1 &&
+ !strncmp ((char *)io->read_meta_buf->data +
+ io->read_meta_buf->len - 2,
+ "\n\n", 2))
+ break;
+ else if (nread == 2 &&
+ !strncmp ((char *)io->read_meta_buf->data +
+ io->read_meta_buf->len - 3,
+ "\n\r\n", 3))
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static SoupBuffer *
+content_decode_one (SoupBuffer *buf, GConverter *converter, GError **error)
+{
+ gsize outbuf_length, outbuf_used, outbuf_cur, input_used, input_cur;
+ char *outbuf;
+ GConverterResult result;
+
+ outbuf_length = MAX (buf->length * 2, 1024);
+ outbuf = g_malloc (outbuf_length);
+ outbuf_cur = input_cur = 0;
+
+ do {
+ result = g_converter_convert (
+ converter,
+ buf->data + input_cur, buf->length - input_cur,
+ outbuf + outbuf_cur, outbuf_length - outbuf_cur,
+ 0, &input_used, &outbuf_used, error);
+ input_cur += input_used;
+ outbuf_cur += outbuf_used;
+
+ if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NO_SPACE) ||
+ (!*error && outbuf_cur == outbuf_length)) {
+ g_clear_error (error);
+ outbuf_length *= 2;
+ outbuf = g_realloc (outbuf, outbuf_length);
+ } else if (*error) {
+ /* GZlibDecompressor can't ever return
+ * G_IO_ERROR_PARTIAL_INPUT unless we pass it
+ * input_length = 0, which we don't. Other
+ * converters might of course, so eventually
+ * this code needs to be rewritten to deal
+ * with that.
+ */
+ g_free (outbuf);
+ return NULL;
+ }
+ } while (input_cur < buf->length && result != G_CONVERTER_FINISHED);
+
+ if (outbuf_cur)
+ return soup_buffer_new (SOUP_MEMORY_TAKE, outbuf, outbuf_cur);
+ else {
+ g_free (outbuf);
+ return NULL;
+ }
+}
+
+static SoupBuffer *
+content_decode (SoupMessage *msg, SoupBuffer *buf)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ GConverter *decoder;
+ SoupBuffer *decoded;
+ GError *error = NULL;
+ GSList *d;
+
+ for (d = priv->decoders; d; d = d->next) {
+ decoder = d->data;
+
+ decoded = content_decode_one (buf, decoder, &error);
+ if (error) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED))
+ g_warning ("Content-Decoding error: %s\n", error->message);
+ g_error_free (error);
+
+ soup_message_set_flags (msg, priv->msg_flags & ~SOUP_MESSAGE_CONTENT_DECODED);
+ break;
+ }
+ if (buf)
+ soup_buffer_free (buf);
+
+ if (decoded)
+ buf = decoded;
+ else
+ return NULL;
+ }
+
+ return buf;
+}
+
+/* Reads as much message body data as is available on io->sock (but no
+ * further than the end of the current message body or chunk). On a
+ * successful read, emits "got_chunk" (possibly multiple times), and
+ * (unless told not to) appends the chunk to io->read_body.
+ *
+ * See the note at read_metadata() for an explanation of the return
+ * value.
+ */
+static gboolean
+read_body_chunk (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ SoupSocketIOStatus status;
+ guchar *stack_buf = NULL;
+ gsize len;
+ gboolean read_to_eof = (io->read_encoding == SOUP_ENCODING_EOF);
+ gsize nread;
+ GError *error = NULL;
+ SoupBuffer *buffer;
+
+ if (!io_handle_sniffing (msg, FALSE))
+ return FALSE;
+
+ while (read_to_eof || io->read_length > 0) {
+ if (priv->chunk_allocator) {
+ buffer = priv->chunk_allocator (msg, io->read_length, priv->chunk_allocator_data);
+ if (!buffer) {
+ soup_message_io_pause (msg);
+ return FALSE;
+ }
+ } else {
+ if (!stack_buf)
+ stack_buf = alloca (RESPONSE_BLOCK_SIZE);
+ buffer = soup_buffer_new (SOUP_MEMORY_TEMPORARY,
+ stack_buf,
+ RESPONSE_BLOCK_SIZE);
+ }
+
+ if (read_to_eof)
+ len = buffer->length;
+ else
+ len = MIN (buffer->length, io->read_length);
+
+ status = soup_socket_read (io->sock,
+ (guchar *)buffer->data, len,
+ &nread, io->cancellable, &error);
+
+ if (status == SOUP_SOCKET_OK && nread) {
+ buffer->length = nread;
+ io->read_length -= nread;
+
+ buffer = content_decode (msg, buffer);
+ if (!buffer)
+ continue;
+
+ soup_message_body_got_chunk (io->read_body, buffer);
+
+ if (io->need_content_sniffed) {
+ soup_message_body_append_buffer (io->sniff_data, buffer);
+ soup_buffer_free (buffer);
+ io->need_got_chunk = TRUE;
+ if (!io_handle_sniffing (msg, FALSE))
+ return FALSE;
+ continue;
+ }
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_got_chunk (msg, buffer);
+ soup_buffer_free (buffer);
+ SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
+ continue;
+ }
+
+ soup_buffer_free (buffer);
+ switch (status) {
+ case SOUP_SOCKET_OK:
+ break;
+
+ case SOUP_SOCKET_EOF:
+ if (io->read_eof_ok) {
+ io->read_length = 0;
+ return TRUE;
+ }
+ /* else fall through */
+
+ case SOUP_SOCKET_ERROR:
+ io_error (io->sock, msg, error);
+ return FALSE;
+
+ case SOUP_SOCKET_WOULD_BLOCK:
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Attempts to write @len bytes from @data. See the note at
+ * read_metadata() for an explanation of the return value.
+ */
+static gboolean
+write_data (SoupMessage *msg, const char *data, guint len, gboolean body)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ SoupSocketIOStatus status;
+ gsize nwrote;
+ GError *error = NULL;
+ SoupBuffer *chunk;
+ const char *start;
+
+ while (len > io->written) {
+ status = soup_socket_write (io->sock,
+ data + io->written,
+ len - io->written,
+ &nwrote,
+ io->cancellable, &error);
+ switch (status) {
+ case SOUP_SOCKET_EOF:
+ case SOUP_SOCKET_ERROR:
+ io_error (io->sock, msg, error);
+ return FALSE;
+
+ case SOUP_SOCKET_WOULD_BLOCK:
+ return FALSE;
+
+ case SOUP_SOCKET_OK:
+ start = data + io->written;
+ io->written += nwrote;
+
+ if (body) {
+ if (io->write_length)
+ io->write_length -= nwrote;
+
+ chunk = soup_buffer_new (SOUP_MEMORY_TEMPORARY,
+ start, nwrote);
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_wrote_body_data (msg, chunk);
+ soup_buffer_free (chunk);
+ SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
+ }
+ break;
+ }
+ }
+
+ io->written = 0;
+ return TRUE;
+}
+
+static inline SoupMessageIOState
+io_body_state (SoupEncoding encoding)
+{
+ if (encoding == SOUP_ENCODING_CHUNKED)
+ return SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
+ else
+ return SOUP_MESSAGE_IO_STATE_BODY;
+}
+
+/*
+ * There are two request/response formats: the basic request/response,
+ * possibly with one or more unsolicited informational responses (such
+ * as the WebDAV "102 Processing" response):
+ *
+ * Client Server
+ * W:HEADERS / R:NOT_STARTED -> R:HEADERS / W:NOT_STARTED
+ * W:BODY / R:NOT_STARTED -> R:BODY / W:NOT_STARTED
+ * [W:DONE / R:HEADERS (1xx) <- R:DONE / W:HEADERS (1xx) ...]
+ * W:DONE / R:HEADERS <- R:DONE / W:HEADERS
+ * W:DONE / R:BODY <- R:DONE / W:BODY
+ * W:DONE / R:DONE R:DONE / W:DONE
+ *
+ * and the "Expect: 100-continue" request/response, with the client
+ * blocking halfway through its request, and then either continuing or
+ * aborting, depending on the server response:
+ *
+ * Client Server
+ * W:HEADERS / R:NOT_STARTED -> R:HEADERS / W:NOT_STARTED
+ * W:BLOCKING / R:HEADERS <- R:BLOCKING / W:HEADERS
+ * [W:BODY / R:BLOCKING -> R:BODY / W:BLOCKING]
+ * [W:DONE / R:HEADERS <- R:DONE / W:HEADERS]
+ * W:DONE / R:BODY <- R:DONE / W:BODY
+ * W:DONE / R:DONE R:DONE / W:DONE
+ */
+
+static void
+io_write (SoupSocket *sock, SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+
+ write_more:
+ switch (io->write_state) {
+ case SOUP_MESSAGE_IO_STATE_NOT_STARTED:
+ return;
+
+
+ case SOUP_MESSAGE_IO_STATE_HEADERS:
+ if (!io->write_buf->len) {
+ io->get_headers_cb (msg, io->write_buf,
+ &io->write_encoding,
+ io->header_data);
+ if (!io->write_buf->len) {
+ soup_message_io_pause (msg);
+ return;
+ }
+ }
+
+ if (!write_data (msg, io->write_buf->str,
+ io->write_buf->len, FALSE))
+ return;
+
+ g_string_truncate (io->write_buf, 0);
+
+ if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
+ SoupMessageHeaders *hdrs =
+ (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
+ msg->request_headers : msg->response_headers;
+ io->write_length = soup_message_headers_get_content_length (hdrs);
+ }
+
+ if (io->mode == SOUP_MESSAGE_IO_SERVER &&
+ SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
+ if (msg->status_code == SOUP_STATUS_CONTINUE) {
+ /* Stop and wait for the body now */
+ io->write_state =
+ SOUP_MESSAGE_IO_STATE_BLOCKING;
+ io->read_state = io_body_state (io->read_encoding);
+ } else {
+ /* We just wrote a 1xx response
+ * header, so stay in STATE_HEADERS.
+ * (The caller will pause us from the
+ * wrote_informational callback if he
+ * is not ready to send the final
+ * response.)
+ */
+ }
+ } else if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+ soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) {
+ /* Need to wait for the Continue response */
+ io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
+ io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+ } else {
+ io->write_state = io_body_state (io->write_encoding);
+
+ /* If the client was waiting for a Continue
+ * but we sent something else, then they're
+ * now done writing.
+ */
+ if (io->mode == SOUP_MESSAGE_IO_SERVER &&
+ io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
+ io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+ }
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
+ soup_message_wrote_informational (msg);
+ soup_message_cleanup_response (msg);
+ } else
+ soup_message_wrote_headers (msg);
+ SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+ break;
+
+
+ case SOUP_MESSAGE_IO_STATE_BLOCKING:
+ io_read (sock, msg);
+
+ /* If io_read reached a point where we could write
+ * again, it would have recursively called io_write.
+ * So (a) we don't need to try to keep writing, and
+ * (b) we can't anyway, because msg may have been
+ * destroyed.
+ */
+ return;
+
+
+ case SOUP_MESSAGE_IO_STATE_BODY:
+ if (!io->write_length && io->write_encoding != SOUP_ENCODING_EOF) {
+ wrote_body:
+ io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_wrote_body (msg);
+ SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+ break;
+ }
+
+ if (!io->write_chunk) {
+ io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset);
+ if (!io->write_chunk) {
+ soup_message_io_pause (msg);
+ return;
+ }
+ if (io->write_chunk->length > io->write_length &&
+ io->write_encoding != SOUP_ENCODING_EOF) {
+ /* App is trying to write more than it
+ * claimed it would; we have to truncate.
+ */
+ SoupBuffer *truncated =
+ soup_buffer_new_subbuffer (io->write_chunk,
+ 0, io->write_length);
+ soup_buffer_free (io->write_chunk);
+ io->write_chunk = truncated;
+ } else if (io->write_encoding == SOUP_ENCODING_EOF &&
+ !io->write_chunk->length)
+ goto wrote_body;
+ }
+
+ if (!write_data (msg, io->write_chunk->data,
+ io->write_chunk->length, TRUE))
+ return;
+
+ if (io->mode == SOUP_MESSAGE_IO_SERVER ||
+ priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD)
+ soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
+ io->write_body_offset += io->write_chunk->length;
+ soup_buffer_free (io->write_chunk);
+ io->write_chunk = NULL;
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_wrote_chunk (msg);
+ SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+ break;
+
+ case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE:
+ if (!io->write_chunk) {
+ io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset);
+ if (!io->write_chunk) {
+ soup_message_io_pause (msg);
+ return;
+ }
+ g_string_append_printf (io->write_buf, "%lx\r\n",
+ (unsigned long) io->write_chunk->length);
+ io->write_body_offset += io->write_chunk->length;
+ }
+
+ if (!write_data (msg, io->write_buf->str,
+ io->write_buf->len, FALSE))
+ return;
+
+ g_string_truncate (io->write_buf, 0);
+
+ if (io->write_chunk->length == 0) {
+ /* The last chunk has no CHUNK_END... */
+ io->write_state = SOUP_MESSAGE_IO_STATE_TRAILERS;
+ break;
+ }
+
+ io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK;
+ /* fall through */
+
+
+ case SOUP_MESSAGE_IO_STATE_CHUNK:
+ if (!write_data (msg, io->write_chunk->data,
+ io->write_chunk->length, TRUE))
+ return;
+
+ if (io->mode == SOUP_MESSAGE_IO_SERVER ||
+ priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD)
+ soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
+ soup_buffer_free (io->write_chunk);
+ io->write_chunk = NULL;
+
+ io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_END;
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_wrote_chunk (msg);
+ SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+
+ /* fall through */
+
+
+ case SOUP_MESSAGE_IO_STATE_CHUNK_END:
+ if (!write_data (msg, SOUP_MESSAGE_IO_EOL,
+ SOUP_MESSAGE_IO_EOL_LEN, FALSE))
+ return;
+
+ io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
+ break;
+
+
+ case SOUP_MESSAGE_IO_STATE_TRAILERS:
+ if (!write_data (msg, SOUP_MESSAGE_IO_EOL,
+ SOUP_MESSAGE_IO_EOL_LEN, FALSE))
+ return;
+
+ io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_wrote_body (msg);
+ SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+ /* fall through */
+
+
+ case SOUP_MESSAGE_IO_STATE_FINISHING:
+ if (io->write_tag) {
+ g_signal_handler_disconnect (io->sock, io->write_tag);
+ io->write_tag = 0;
+ }
+ io->write_state = SOUP_MESSAGE_IO_STATE_DONE;
+
+ if (io->mode == SOUP_MESSAGE_IO_CLIENT) {
+ io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+ io_read (sock, msg);
+ } else
+ soup_message_io_finished (msg);
+ return;
+
+
+ case SOUP_MESSAGE_IO_STATE_DONE:
+ default:
+ g_return_if_reached ();
+ }
+
+ goto write_more;
+}
+
+static void
+io_read (SoupSocket *sock, SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ guint status;
+
+ read_more:
+ switch (io->read_state) {
+ case SOUP_MESSAGE_IO_STATE_NOT_STARTED:
+ return;
+
+
+ case SOUP_MESSAGE_IO_STATE_HEADERS:
+ if (!read_metadata (msg, TRUE))
+ return;
+
+ /* We need to "rewind" io->read_meta_buf back one line.
+ * That SHOULD be two characters (CR LF), but if the
+ * web server was stupid, it might only be one.
+ */
+ if (io->read_meta_buf->len < 3 ||
+ io->read_meta_buf->data[io->read_meta_buf->len - 2] == '\n')
+ io->read_meta_buf->len--;
+ else
+ io->read_meta_buf->len -= 2;
+ io->read_meta_buf->data[io->read_meta_buf->len] = '\0';
+ status = io->parse_headers_cb (msg, (char *)io->read_meta_buf->data,
+ io->read_meta_buf->len,
+ &io->read_encoding,
+ io->header_data);
+ g_byte_array_set_size (io->read_meta_buf, 0);
+
+ if (status != SOUP_STATUS_OK) {
+ /* Either we couldn't parse the headers, or they
+ * indicated something that would mean we wouldn't
+ * be able to parse the body. (Eg, unknown
+ * Transfer-Encoding.). Skip the rest of the
+ * reading, and make sure the connection gets
+ * closed when we're done.
+ */
+ soup_message_set_status (msg, status);
+ soup_message_headers_append (msg->request_headers,
+ "Connection", "close");
+ io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+ break;
+ }
+
+ if (io->read_encoding == SOUP_ENCODING_EOF)
+ io->read_eof_ok = TRUE;
+
+ if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
+ SoupMessageHeaders *hdrs =
+ (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
+ msg->response_headers : msg->request_headers;
+ io->read_length = soup_message_headers_get_content_length (hdrs);
+
+ if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+ !soup_message_is_keepalive (msg)) {
+ /* Some servers suck and send
+ * incorrect Content-Length values, so
+ * allow EOF termination in this case
+ * (iff the message is too short) too.
+ */
+ io->read_eof_ok = TRUE;
+ }
+ }
+
+ if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+ SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
+ if (msg->status_code == SOUP_STATUS_CONTINUE &&
+ io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) {
+ /* Pause the reader, unpause the writer */
+ io->read_state =
+ SOUP_MESSAGE_IO_STATE_BLOCKING;
+ io->write_state =
+ io_body_state (io->write_encoding);
+ } else {
+ /* Just stay in HEADERS */
+ io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+ }
+ } else if (io->mode == SOUP_MESSAGE_IO_SERVER &&
+ soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) {
+ /* The client requested a Continue response. The
+ * got_headers handler may change this to something
+ * else though.
+ */
+ soup_message_set_status (msg, SOUP_STATUS_CONTINUE);
+ io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+ io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
+ } else {
+ io->read_state = io_body_state (io->read_encoding);
+
+ /* If the client was waiting for a Continue
+ * but got something else, then it's done
+ * writing.
+ */
+ if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+ io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
+ io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+ }
+
+ if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+ SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_got_informational (msg);
+ soup_message_cleanup_response (msg);
+ SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+ } else {
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_got_headers (msg);
+ SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+ }
+ break;
+
+
+ case SOUP_MESSAGE_IO_STATE_BLOCKING:
+ io_write (sock, msg);
+
+ /* As in the io_write case, we *must* return here. */
+ return;
+
+
+ case SOUP_MESSAGE_IO_STATE_BODY:
+ if (!read_body_chunk (msg))
+ return;
+
+ got_body:
+ if (!io_handle_sniffing (msg, TRUE)) {
+ /* If the message was paused (as opposed to
+ * cancelled), we need to make sure we wind up
+ * back here when it's unpaused, even if it
+ * was doing a chunked or EOF-terminated read
+ * before.
+ */
+ if (io == priv->io_data) {
+ io->read_state = SOUP_MESSAGE_IO_STATE_BODY;
+ io->read_encoding = SOUP_ENCODING_CONTENT_LENGTH;
+ io->read_length = 0;
+ }
+ return;
+ }
+
+ io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_got_body (msg);
+ SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED;
+ break;
+
+
+ case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE:
+ if (!read_metadata (msg, FALSE))
+ return;
+
+ io->read_length = strtoul ((char *)io->read_meta_buf->data, NULL, 16);
+ g_byte_array_set_size (io->read_meta_buf, 0);
+
+ if (io->read_length > 0)
+ io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK;
+ else
+ io->read_state = SOUP_MESSAGE_IO_STATE_TRAILERS;
+ break;
+
+
+ case SOUP_MESSAGE_IO_STATE_CHUNK:
+ if (!read_body_chunk (msg))
+ return;
+
+ io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK_END;
+ break;
+
+
+ case SOUP_MESSAGE_IO_STATE_CHUNK_END:
+ if (!read_metadata (msg, FALSE))
+ return;
+
+ g_byte_array_set_size (io->read_meta_buf, 0);
+ io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
+ break;
+
+
+ case SOUP_MESSAGE_IO_STATE_TRAILERS:
+ if (!read_metadata (msg, FALSE))
+ return;
+
+ if (io->read_meta_buf->len <= SOUP_MESSAGE_IO_EOL_LEN)
+ goto got_body;
+
+ /* FIXME: process trailers */
+ g_byte_array_set_size (io->read_meta_buf, 0);
+ break;
+
+
+ case SOUP_MESSAGE_IO_STATE_FINISHING:
+ if (io->read_tag) {
+ g_signal_handler_disconnect (io->sock, io->read_tag);
+ io->read_tag = 0;
+ }
+ io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
+
+ if (io->mode == SOUP_MESSAGE_IO_SERVER) {
+ io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+ io_write (sock, msg);
+ } else
+ soup_message_io_finished (msg);
+ return;
+
+
+ case SOUP_MESSAGE_IO_STATE_DONE:
+ default:
+ g_return_if_reached ();
+ }
+
+ goto read_more;
+}
+
+static void
+socket_tls_certificate_changed (GObject *sock, GParamSpec *pspec,
+ gpointer msg)
+{
+ GTlsCertificate *certificate;
+ GTlsCertificateFlags errors;
+
+ g_object_get (sock,
+ SOUP_SOCKET_TLS_CERTIFICATE, &certificate,
+ SOUP_SOCKET_TLS_ERRORS, &errors,
+ NULL);
+ g_object_set (msg,
+ SOUP_MESSAGE_TLS_CERTIFICATE, certificate,
+ SOUP_MESSAGE_TLS_ERRORS, errors,
+ NULL);
+ if (certificate)
+ g_object_unref (certificate);
+}
+
+static SoupMessageIOData *
+new_iostate (SoupMessage *msg, SoupSocket *sock, SoupMessageIOMode mode,
+ SoupMessageGetHeadersFn get_headers_cb,
+ SoupMessageParseHeadersFn parse_headers_cb,
+ gpointer header_data,
+ SoupMessageCompletionFn completion_cb,
+ gpointer completion_data)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io;
+
+ io = g_slice_new0 (SoupMessageIOData);
+ io->sock = g_object_ref (sock);
+ io->mode = mode;
+ io->get_headers_cb = get_headers_cb;
+ io->parse_headers_cb = parse_headers_cb;
+ io->header_data = header_data;
+ io->completion_cb = completion_cb;
+ io->completion_data = completion_data;
+
+ io->read_meta_buf = g_byte_array_new ();
+ io->write_buf = g_string_new (NULL);
+
+ io->read_tag = g_signal_connect (io->sock, "readable",
+ G_CALLBACK (io_read), msg);
+ io->write_tag = g_signal_connect (io->sock, "writable",
+ G_CALLBACK (io_write), msg);
+
+ io->read_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
+ io->write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
+
+ if (soup_socket_is_ssl (io->sock)) {
+ io->tls_signal_id = g_signal_connect (io->sock, "notify::tls-certificate",
+ G_CALLBACK (socket_tls_certificate_changed), msg);
+ }
+
+ if (priv->io_data)
+ soup_message_io_cleanup (msg);
+ priv->io_data = io;
+ return io;
+}
+
+void
+soup_message_io_client (SoupMessageQueueItem *item,
+ SoupMessageGetHeadersFn get_headers_cb,
+ SoupMessageParseHeadersFn parse_headers_cb,
+ gpointer header_data,
+ SoupMessageCompletionFn completion_cb,
+ gpointer completion_data)
+{
+ SoupMessageIOData *io;
+ SoupSocket *sock = soup_connection_get_socket (item->conn);
+
+ io = new_iostate (item->msg, sock, SOUP_MESSAGE_IO_CLIENT,
+ get_headers_cb, parse_headers_cb, header_data,
+ completion_cb, completion_data);
+
+ io->item = item;
+ soup_message_queue_item_ref (item);
+ io->cancellable = item->cancellable;
+
+ io->read_body = item->msg->response_body;
+ io->write_body = item->msg->request_body;
+
+ io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+ io_write (sock, item->msg);
+}
+
+void
+soup_message_io_server (SoupMessage *msg, SoupSocket *sock,
+ SoupMessageGetHeadersFn get_headers_cb,
+ SoupMessageParseHeadersFn parse_headers_cb,
+ gpointer header_data,
+ SoupMessageCompletionFn completion_cb,
+ gpointer completion_data)
+{
+ SoupMessageIOData *io;
+
+ io = new_iostate (msg, sock, SOUP_MESSAGE_IO_SERVER,
+ get_headers_cb, parse_headers_cb, header_data,
+ completion_cb, completion_data);
+
+ io->read_body = msg->request_body;
+ io->write_body = msg->response_body;
+
+ io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+ io_read (sock, msg);
+}
+
+void
+soup_message_io_pause (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+
+ g_return_if_fail (io != NULL);
+
+ if (io->write_tag) {
+ g_signal_handler_disconnect (io->sock, io->write_tag);
+ io->write_tag = 0;
+ }
+ if (io->read_tag) {
+ g_signal_handler_disconnect (io->sock, io->read_tag);
+ io->read_tag = 0;
+ }
+
+ if (io->unpause_source) {
+ g_source_destroy (io->unpause_source);
+ io->unpause_source = NULL;
+ }
+}
+
+static gboolean
+io_unpause_internal (gpointer msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+
+ g_return_val_if_fail (io != NULL, FALSE);
+ io->unpause_source = NULL;
+
+ if (io->write_tag || io->read_tag)
+ return FALSE;
+
+ if (io->write_state != SOUP_MESSAGE_IO_STATE_DONE) {
+ io->write_tag = g_signal_connect (io->sock, "writable",
+ G_CALLBACK (io_write), msg);
+ }
+
+ if (io->read_state != SOUP_MESSAGE_IO_STATE_DONE) {
+ io->read_tag = g_signal_connect (io->sock, "readable",
+ G_CALLBACK (io_read), msg);
+ }
+
+ if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->write_state))
+ io_write (io->sock, msg);
+ else if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state))
+ io_read (io->sock, msg);
+
+ return FALSE;
+}
+
+void
+soup_message_io_unpause (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ gboolean non_blocking;
+ GMainContext *async_context;
+
+ g_return_if_fail (io != NULL);
+
+ g_object_get (io->sock,
+ SOUP_SOCKET_FLAG_NONBLOCKING, &non_blocking,
+ SOUP_SOCKET_ASYNC_CONTEXT, &async_context,
+ NULL);
+ if (non_blocking) {
+ if (!io->unpause_source) {
+ io->unpause_source = soup_add_completion (
+ async_context, io_unpause_internal, msg);
+ }
+ } else
+ io_unpause_internal (msg);
+ if (async_context)
+ g_main_context_unref (async_context);
+}
+
+/**
+ * soup_message_io_in_progress:
+ * @msg: a #SoupMessage
+ *
+ * Tests whether or not I/O is currently in progress on @msg.
+ *
+ * Return value: whether or not I/O is currently in progress.
+ **/
+gboolean
+soup_message_io_in_progress (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ return priv->io_data != NULL;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_PRIVATE_H
+#define SOUP_MESSAGE_PRIVATE_H 1
+
+#include "soup-message.h"
+#include "soup-auth.h"
+#include "soup-content-sniffer.h"
+
+typedef struct {
+ gpointer io_data;
+
+ SoupChunkAllocator chunk_allocator;
+ gpointer chunk_allocator_data;
+ GDestroyNotify chunk_allocator_dnotify;
+
+ guint msg_flags;
+ gboolean server_side;
+
+ SoupContentSniffer *sniffer;
+ gsize bytes_for_sniffing;
+
+ SoupHTTPVersion http_version, orig_http_version;
+
+ SoupURI *uri;
+ SoupAddress *addr;
+
+ SoupAuth *auth, *proxy_auth;
+
+ GSList *disabled_features;
+ GSList *decoders;
+
+ SoupURI *first_party;
+
+ GTlsCertificate *tls_certificate;
+ GTlsCertificateFlags tls_errors;
+} SoupMessagePrivate;
+#define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE, SoupMessagePrivate))
+
+void soup_message_cleanup_response (SoupMessage *req);
+
+
+typedef void (*SoupMessageGetHeadersFn) (SoupMessage *msg,
+ GString *headers,
+ SoupEncoding *encoding,
+ gpointer user_data);
+typedef guint (*SoupMessageParseHeadersFn)(SoupMessage *msg,
+ char *headers,
+ guint header_len,
+ SoupEncoding *encoding,
+ gpointer user_data);
+typedef void (*SoupMessageCompletionFn) (SoupMessage *msg,
+ gpointer user_data);
+
+
+void soup_message_send_request (SoupMessageQueueItem *item,
+ SoupMessageCompletionFn completion_cb,
+ gpointer user_data);
+void soup_message_read_request (SoupMessage *req,
+ SoupSocket *sock,
+ SoupMessageCompletionFn completion_cb,
+ gpointer user_data);
+
+void soup_message_io_client (SoupMessageQueueItem *item,
+ SoupMessageGetHeadersFn get_headers_cb,
+ SoupMessageParseHeadersFn parse_headers_cb,
+ gpointer headers_data,
+ SoupMessageCompletionFn completion_cb,
+ gpointer user_data);
+void soup_message_io_server (SoupMessage *msg,
+ SoupSocket *sock,
+ SoupMessageGetHeadersFn get_headers_cb,
+ SoupMessageParseHeadersFn parse_headers_cb,
+ gpointer headers_data,
+ SoupMessageCompletionFn completion_cb,
+ gpointer user_data);
+void soup_message_io_cleanup (SoupMessage *msg);
+
+/* Auth handling */
+void soup_message_set_auth (SoupMessage *msg,
+ SoupAuth *auth);
+SoupAuth *soup_message_get_auth (SoupMessage *msg);
+void soup_message_set_proxy_auth (SoupMessage *msg,
+ SoupAuth *auth);
+SoupAuth *soup_message_get_proxy_auth (SoupMessage *msg);
+
+/* I/O */
+void soup_message_io_stop (SoupMessage *msg);
+void soup_message_io_finished (SoupMessage *msg);
+void soup_message_io_pause (SoupMessage *msg);
+void soup_message_io_unpause (SoupMessage *msg);
+gboolean soup_message_io_in_progress (SoupMessage *msg);
+
+gboolean soup_message_disables_feature (SoupMessage *msg,
+ gpointer feature);
+#endif /* SOUP_MESSAGE_PRIVATE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-queue.c: Message queue
+ *
+ * Copyright (C) 2003 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-message-queue.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-message-queue
+ *
+ * This is an internal structure used by #SoupSession and its
+ * subclasses to keep track of the status of messages currently being
+ * processed.
+ *
+ * The #SoupMessageQueue itself is mostly just a linked list of
+ * #SoupMessageQueueItem, with some added cleverness to allow the list
+ * to be walked safely while other threads / re-entrant loops are
+ * adding items to and removing items from it. In particular, this is
+ * handled by refcounting items and then keeping "removed" items in
+ * the list until their ref_count drops to 0, but skipping over the
+ * "removed" ones when walking the queue.
+ **/
+
+struct _SoupMessageQueue {
+ SoupSession *session;
+
+ GMutex *mutex;
+ SoupMessageQueueItem *head, *tail;
+};
+
+SoupMessageQueue *
+soup_message_queue_new (SoupSession *session)
+{
+ SoupMessageQueue *queue;
+
+ queue = g_slice_new0 (SoupMessageQueue);
+ queue->session = session;
+ queue->mutex = g_mutex_new ();
+ return queue;
+}
+
+void
+soup_message_queue_destroy (SoupMessageQueue *queue)
+{
+ g_return_if_fail (queue->head == NULL);
+
+ g_mutex_free (queue->mutex);
+ g_slice_free (SoupMessageQueue, queue);
+}
+
+static void
+queue_message_restarted (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ if (item->proxy_addr) {
+ g_object_unref (item->proxy_addr);
+ item->proxy_addr = NULL;
+ }
+ if (item->proxy_uri) {
+ soup_uri_free (item->proxy_uri);
+ item->proxy_uri = NULL;
+ }
+
+ if (item->conn &&
+ (!soup_message_is_keepalive (msg) ||
+ SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
+ if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
+ soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ }
+
+ soup_message_cleanup_response (msg);
+
+ g_cancellable_reset (item->cancellable);
+
+ item->state = SOUP_MESSAGE_STARTING;
+}
+
+/**
+ * soup_message_queue_append:
+ * @queue: a #SoupMessageQueue
+ * @msg: a #SoupMessage
+ * @callback: the callback for @msg
+ * @user_data: the data to pass to @callback
+ *
+ * Creates a new #SoupMessageQueueItem and appends it to @queue.
+ *
+ * Return value: the new item, which you must unref with
+ * soup_message_queue_unref_item() when you are done with.
+ **/
+SoupMessageQueueItem *
+soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
+ SoupSessionCallback callback, gpointer user_data)
+{
+ SoupMessageQueueItem *item;
+
+ item = g_slice_new0 (SoupMessageQueueItem);
+ item->session = queue->session;
+ item->queue = queue;
+ item->msg = g_object_ref (msg);
+ item->callback = callback;
+ item->callback_data = user_data;
+ item->cancellable = g_cancellable_new ();
+
+ g_signal_connect (msg, "restarted",
+ G_CALLBACK (queue_message_restarted), item);
+
+ /* Note: the initial ref_count of 1 represents the caller's
+ * ref; the queue's own ref is indicated by the absence of the
+ * "removed" flag.
+ */
+ item->ref_count = 1;
+
+ g_mutex_lock (queue->mutex);
+ if (queue->head) {
+ queue->tail->next = item;
+ item->prev = queue->tail;
+ queue->tail = item;
+ } else
+ queue->head = queue->tail = item;
+
+ g_mutex_unlock (queue->mutex);
+ return item;
+}
+
+/**
+ * soup_message_queue_item_ref:
+ * @item: a #SoupMessageQueueItem
+ *
+ * Refs @item.
+ **/
+void
+soup_message_queue_item_ref (SoupMessageQueueItem *item)
+{
+ item->ref_count++;
+}
+
+/**
+ * soup_message_queue_item_unref:
+ * @item: a #SoupMessageQueueItem
+ *
+ * Unrefs @item; use this on a #SoupMessageQueueItem that you are done
+ * with (but that you aren't passing to
+ * soup_message_queue_item_next()).
+ **/
+void
+soup_message_queue_item_unref (SoupMessageQueueItem *item)
+{
+ g_mutex_lock (item->queue->mutex);
+
+ /* Decrement the ref_count; if it's still non-zero OR if the
+ * item is still in the queue, then return.
+ */
+ if (--item->ref_count || !item->removed) {
+ g_mutex_unlock (item->queue->mutex);
+ return;
+ }
+
+ /* OK, @item is dead. Rewrite @queue around it */
+ if (item->prev)
+ item->prev->next = item->next;
+ else
+ item->queue->head = item->next;
+ if (item->next)
+ item->next->prev = item->prev;
+ else
+ item->queue->tail = item->prev;
+
+ g_mutex_unlock (item->queue->mutex);
+
+ /* And free it */
+ g_signal_handlers_disconnect_by_func (item->msg,
+ queue_message_restarted, item);
+ g_object_unref (item->msg);
+ g_object_unref (item->cancellable);
+ if (item->proxy_addr)
+ g_object_unref (item->proxy_addr);
+ if (item->proxy_uri)
+ soup_uri_free (item->proxy_uri);
+ if (item->conn)
+ g_object_unref (item->conn);
+ g_slice_free (SoupMessageQueueItem, item);
+}
+
+/**
+ * soup_message_queue_lookup:
+ * @queue: a #SoupMessageQueue
+ * @msg: a #SoupMessage
+ *
+ * Finds the #SoupMessageQueueItem for @msg in @queue. You must unref
+ * the item with soup_message_queue_unref_item() when you are done
+ * with it.
+ *
+ * Return value: the queue item for @msg, or %NULL
+ **/
+SoupMessageQueueItem *
+soup_message_queue_lookup (SoupMessageQueue *queue, SoupMessage *msg)
+{
+ SoupMessageQueueItem *item;
+
+ g_mutex_lock (queue->mutex);
+
+ item = queue->tail;
+ while (item && (item->removed || item->msg != msg))
+ item = item->prev;
+
+ if (item)
+ item->ref_count++;
+
+ g_mutex_unlock (queue->mutex);
+ return item;
+}
+
+/**
+ * soup_message_queue_first:
+ * @queue: a #SoupMessageQueue
+ *
+ * Gets the first item in @queue. You must unref the item by calling
+ * soup_message_queue_unref_item() on it when you are done.
+ * (soup_message_queue_next() does this for you automatically, so you
+ * only need to unref the item yourself if you are not going to
+ * finishing walking the queue.)
+ *
+ * Return value: the first item in @queue.
+ **/
+SoupMessageQueueItem *
+soup_message_queue_first (SoupMessageQueue *queue)
+{
+ SoupMessageQueueItem *item;
+
+ g_mutex_lock (queue->mutex);
+
+ item = queue->head;
+ while (item && item->removed)
+ item = item->next;
+
+ if (item)
+ item->ref_count++;
+
+ g_mutex_unlock (queue->mutex);
+ return item;
+}
+
+/**
+ * soup_message_queue_next:
+ * @queue: a #SoupMessageQueue
+ * @item: a #SoupMessageQueueItem
+ *
+ * Unrefs @item and gets the next item after it in @queue. As with
+ * soup_message_queue_first(), you must unref the returned item
+ * yourself with soup_message_queue_unref_item() if you do not finish
+ * walking the queue.
+ *
+ * Return value: the next item in @queue.
+ **/
+SoupMessageQueueItem *
+soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueItem *item)
+{
+ SoupMessageQueueItem *next;
+
+ g_mutex_lock (queue->mutex);
+
+ next = item->next;
+ while (next && next->removed)
+ next = next->next;
+ if (next)
+ next->ref_count++;
+
+ g_mutex_unlock (queue->mutex);
+ soup_message_queue_item_unref (item);
+ return next;
+}
+
+/**
+ * soup_message_queue_remove:
+ * @queue: a #SoupMessageQueue
+ * @item: a #SoupMessageQueueItem
+ *
+ * Removes @item from @queue. Note that you probably also need to call
+ * soup_message_queue_unref_item() after this.
+ **/
+void
+soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item)
+{
+ g_return_if_fail (!item->removed);
+
+ g_mutex_lock (queue->mutex);
+ item->removed = TRUE;
+ g_mutex_unlock (queue->mutex);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_QUEUE_H
+#define SOUP_MESSAGE_QUEUE_H 1
+
+#include "soup-connection.h"
+#include "soup-message.h"
+#include "soup-session.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ SOUP_MESSAGE_STARTING,
+ SOUP_MESSAGE_RESOLVING_PROXY_URI,
+ SOUP_MESSAGE_RESOLVED_PROXY_URI,
+ SOUP_MESSAGE_RESOLVING_PROXY_ADDRESS,
+ SOUP_MESSAGE_RESOLVED_PROXY_ADDRESS,
+ SOUP_MESSAGE_AWAITING_CONNECTION,
+ SOUP_MESSAGE_GOT_CONNECTION,
+ SOUP_MESSAGE_CONNECTING,
+ SOUP_MESSAGE_CONNECTED,
+ SOUP_MESSAGE_TUNNELING,
+ SOUP_MESSAGE_TUNNELED,
+ SOUP_MESSAGE_READY,
+ SOUP_MESSAGE_RUNNING,
+ SOUP_MESSAGE_RESTARTING,
+ SOUP_MESSAGE_FINISHING,
+ SOUP_MESSAGE_FINISHED
+} SoupMessageQueueItemState;
+
+struct _SoupMessageQueueItem {
+ /*< public >*/
+ SoupSession *session;
+ SoupMessageQueue *queue;
+ SoupMessage *msg;
+ SoupSessionCallback callback;
+ gpointer callback_data;
+
+ GCancellable *cancellable;
+ SoupAddress *proxy_addr;
+ SoupURI *proxy_uri;
+ SoupConnection *conn;
+
+ guint redirection_count;
+
+ SoupMessageQueueItemState state;
+
+ /*< private >*/
+ guint removed : 1;
+ guint ref_count : 31;
+ SoupMessageQueueItem *prev, *next;
+ SoupMessageQueueItem *related;
+};
+
+SoupMessageQueue *soup_message_queue_new (SoupSession *session);
+SoupMessageQueueItem *soup_message_queue_append (SoupMessageQueue *queue,
+ SoupMessage *msg,
+ SoupSessionCallback callback,
+ gpointer user_data);
+
+SoupMessageQueueItem *soup_message_queue_lookup (SoupMessageQueue *queue,
+ SoupMessage *msg);
+
+SoupMessageQueueItem *soup_message_queue_first (SoupMessageQueue *queue);
+SoupMessageQueueItem *soup_message_queue_next (SoupMessageQueue *queue,
+ SoupMessageQueueItem *item);
+
+void soup_message_queue_remove (SoupMessageQueue *queue,
+ SoupMessageQueueItem *item);
+
+void soup_message_queue_item_ref (SoupMessageQueueItem *item);
+void soup_message_queue_item_unref (SoupMessageQueueItem *item);
+
+void soup_message_queue_destroy (SoupMessageQueue *queue);
+
+
+G_END_DECLS
+
+#endif /* SOUP_MESSAGE_QUEUE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-server-io.c: server-side request/response
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-message-private.h"
+#include "soup-address.h"
+#include "soup-auth.h"
+#include "soup-headers.h"
+#include "soup-multipart.h"
+#include "soup-server.h"
+#include "soup-socket.h"
+
+static guint
+parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
+ SoupEncoding *encoding, gpointer sock)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ char *req_method, *req_path, *url;
+ SoupHTTPVersion version;
+ const char *req_host;
+ guint status;
+ SoupURI *uri;
+
+ status = soup_headers_parse_request (headers, headers_len,
+ msg->request_headers,
+ &req_method,
+ &req_path,
+ &version);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ return status;
+
+ g_object_set (G_OBJECT (msg),
+ SOUP_MESSAGE_METHOD, req_method,
+ SOUP_MESSAGE_HTTP_VERSION, version,
+ NULL);
+ g_free (req_method);
+
+ /* Handle request body encoding */
+ *encoding = soup_message_headers_get_encoding (msg->request_headers);
+ if (*encoding == SOUP_ENCODING_UNRECOGNIZED) {
+ if (soup_message_headers_get_list (msg->request_headers, "Transfer-Encoding"))
+ return SOUP_STATUS_NOT_IMPLEMENTED;
+ else
+ return SOUP_STATUS_BAD_REQUEST;
+ }
+
+ /* Generate correct context for request */
+ req_host = soup_message_headers_get_one (msg->request_headers, "Host");
+ if (req_host && strchr (req_host, '/')) {
+ g_free (req_path);
+ return SOUP_STATUS_BAD_REQUEST;
+ }
+
+ if (!strcmp (req_path, "*") && req_host) {
+ /* Eg, "OPTIONS * HTTP/1.1" */
+ url = g_strdup_printf ("%s://%s",
+ soup_socket_is_ssl (sock) ? "https" : "http",
+ req_host);
+ uri = soup_uri_new (url);
+ if (uri)
+ soup_uri_set_path (uri, "*");
+ g_free (url);
+ } else if (*req_path != '/') {
+ /* Must be an absolute URI */
+ uri = soup_uri_new (req_path);
+ } else if (req_host) {
+ url = g_strdup_printf ("%s://%s%s",
+ soup_socket_is_ssl (sock) ? "https" : "http",
+ req_host, req_path);
+ uri = soup_uri_new (url);
+ g_free (url);
+ } else if (priv->http_version == SOUP_HTTP_1_0) {
+ /* No Host header, no AbsoluteUri */
+ SoupAddress *addr = soup_socket_get_local_address (sock);
+ const char *host = soup_address_get_physical (addr);
+
+ url = g_strdup_printf ("%s://%s:%d%s",
+ soup_socket_is_ssl (sock) ? "https" : "http",
+ host, soup_address_get_port (addr),
+ req_path);
+ uri = soup_uri_new (url);
+ g_free (url);
+ } else
+ uri = NULL;
+
+ g_free (req_path);
+ if (!uri)
+ return SOUP_STATUS_BAD_REQUEST;
+ soup_message_set_uri (msg, uri);
+ soup_uri_free (uri);
+
+ return SOUP_STATUS_OK;
+}
+
+static void
+handle_partial_get (SoupMessage *msg)
+{
+ SoupRange *ranges;
+ int nranges;
+ SoupBuffer *full_response;
+
+ /* Make sure the message is set up right for us to return a
+ * partial response; it has to be a GET, the status must be
+ * 200 OK (and in particular, NOT already 206 Partial
+ * Content), and the SoupServer must have already filled in
+ * the response body
+ */
+ if (msg->method != SOUP_METHOD_GET ||
+ msg->status_code != SOUP_STATUS_OK ||
+ soup_message_headers_get_encoding (msg->response_headers) !=
+ SOUP_ENCODING_CONTENT_LENGTH ||
+ msg->response_body->length == 0 ||
+ !soup_message_body_get_accumulate (msg->response_body))
+ return;
+
+ /* Oh, and there has to have been a valid Range header on the
+ * request, of course.
+ */
+ if (!soup_message_headers_get_ranges (msg->request_headers,
+ msg->response_body->length,
+ &ranges, &nranges))
+ return;
+
+ full_response = soup_message_body_flatten (msg->response_body);
+ if (!full_response) {
+ soup_message_headers_free_ranges (msg->request_headers, ranges);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
+ soup_message_body_truncate (msg->response_body);
+
+ if (nranges == 1) {
+ SoupBuffer *range_buf;
+
+ /* Single range, so just set Content-Range and fix the body. */
+
+ soup_message_headers_set_content_range (msg->response_headers,
+ ranges[0].start,
+ ranges[0].end,
+ full_response->length);
+ range_buf = soup_buffer_new_subbuffer (full_response,
+ ranges[0].start,
+ ranges[0].end - ranges[0].start + 1);
+ soup_message_body_append_buffer (msg->response_body, range_buf);
+ soup_buffer_free (range_buf);
+ } else {
+ SoupMultipart *multipart;
+ SoupMessageHeaders *part_headers;
+ SoupBuffer *part_body;
+ const char *content_type;
+ int i;
+
+ /* Multiple ranges, so build a multipart/byteranges response
+ * to replace msg->response_body with.
+ */
+
+ multipart = soup_multipart_new ("multipart/byteranges");
+ content_type = soup_message_headers_get_one (msg->response_headers,
+ "Content-Type");
+ for (i = 0; i < nranges; i++) {
+ part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ if (content_type) {
+ soup_message_headers_append (part_headers,
+ "Content-Type",
+ content_type);
+ }
+ soup_message_headers_set_content_range (part_headers,
+ ranges[i].start,
+ ranges[i].end,
+ full_response->length);
+ part_body = soup_buffer_new_subbuffer (full_response,
+ ranges[i].start,
+ ranges[i].end - ranges[i].start + 1);
+ soup_multipart_append_part (multipart, part_headers,
+ part_body);
+ soup_message_headers_free (part_headers);
+ soup_buffer_free (part_body);
+ }
+
+ soup_multipart_to_message (multipart, msg->response_headers,
+ msg->response_body);
+ soup_multipart_free (multipart);
+ }
+
+ soup_buffer_free (full_response);
+ soup_message_headers_free_ranges (msg->request_headers, ranges);
+}
+
+static void
+get_response_headers (SoupMessage *msg, GString *headers,
+ SoupEncoding *encoding, gpointer user_data)
+{
+ SoupEncoding claimed_encoding;
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+
+ handle_partial_get (msg);
+
+ g_string_append_printf (headers, "HTTP/1.%c %d %s\r\n",
+ soup_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1',
+ msg->status_code, msg->reason_phrase);
+
+ claimed_encoding = soup_message_headers_get_encoding (msg->response_headers);
+ if ((msg->method == SOUP_METHOD_HEAD ||
+ msg->status_code == SOUP_STATUS_NO_CONTENT ||
+ msg->status_code == SOUP_STATUS_NOT_MODIFIED ||
+ SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) ||
+ (msg->method == SOUP_METHOD_CONNECT &&
+ SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)))
+ *encoding = SOUP_ENCODING_NONE;
+ else
+ *encoding = claimed_encoding;
+
+ if (claimed_encoding == SOUP_ENCODING_CONTENT_LENGTH &&
+ !soup_message_headers_get_content_length (msg->response_headers)) {
+ soup_message_headers_set_content_length (msg->response_headers,
+ msg->response_body->length);
+ }
+
+ soup_message_headers_iter_init (&iter, msg->response_headers);
+ while (soup_message_headers_iter_next (&iter, &name, &value))
+ g_string_append_printf (headers, "%s: %s\r\n", name, value);
+ g_string_append (headers, "\r\n");
+}
+
+void
+soup_message_read_request (SoupMessage *msg,
+ SoupSocket *sock,
+ SoupMessageCompletionFn completion_cb,
+ gpointer user_data)
+{
+ soup_message_io_server (msg, sock,
+ get_response_headers,
+ parse_request_headers,
+ sock,
+ completion_cb, user_data);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message.c: HTTP request/response
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-address.h"
+#include "soup-auth.h"
+#include "soup-enum-types.h"
+#include "soup-marshal.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-misc.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-message
+ * @short_description: An HTTP request and response.
+ * @see_also: #SoupMessageHeaders, #SoupMessageBody
+ *
+ * A #SoupMessage represents an HTTP message that is being sent or
+ * received.
+ *
+ * For client-side usage, you would create a #SoupMessage with
+ * soup_message_new() or soup_message_new_from_uri(), set up its
+ * fields appropriate, and send it via a #SoupSession.
+ *
+ * For server-side usage, #SoupServer will create #SoupMessage<!--
+ * -->s automatically for incoming requests, which your application
+ * will receive via handlers.
+ *
+ * Note that libsoup's terminology here does not quite match the HTTP
+ * specification: in RFC 2616, an "HTTP-message" is
+ * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a
+ * Response. In libsoup, a #SoupMessage combines both the request and
+ * the response.
+ **/
+
+/**
+ * SoupMessage:
+ * @method: the HTTP method
+ * @status_code: the HTTP status code
+ * @reason_phrase: the status phrase associated with @status_code
+ * @request_body: the request body
+ * @request_headers: the request headers
+ * @response_body: the response body
+ * @response_headers: the response headers
+ *
+ * Represents an HTTP message being sent or received.
+ *
+ * @status_code will normally be a #SoupKnownStatusCode, eg,
+ * %SOUP_STATUS_OK, though of course it might actually be an unknown
+ * status code. @reason_phrase is the actual text returned from the
+ * server, which may or may not correspond to the "standard"
+ * description of @status_code. At any rate, it is almost certainly
+ * not localized, and not very descriptive even if it is in the user's
+ * language; you should not use @reason_phrase in user-visible
+ * messages. Rather, you should look at @status_code, and determine an
+ * end-user-appropriate message based on that and on what you were
+ * trying to do.
+ *
+ * As described in the #SoupMessageBody documentation, the
+ * @request_body and @response_body %data fields will not necessarily
+ * be filled in at all times. When they are filled in, they will be
+ * terminated with a '\0' byte (which is not included in the %length),
+ * so you can use them as ordinary C strings (assuming that you know
+ * that the body doesn't have any other '\0' bytes).
+ *
+ * For a client-side #SoupMessage, @request_body's %data is usually
+ * filled in right before libsoup writes the request to the network,
+ * but you should not count on this; use soup_message_body_flatten()
+ * if you want to ensure that %data is filled in. @response_body's
+ * %data will be filled in before #SoupMessage::finished is emitted.
+ *
+ * For a server-side #SoupMessage, @request_body's %data will be
+ * filled in before #SoupMessage::got_body is emitted.
+ *
+ * To prevent the %data field from being filled in at all (eg, if you
+ * are handling the data from a #SoupMessage::got_chunk, and so don't
+ * need to see it all at the end), call
+ * soup_message_body_set_accumulate() on @response_body or
+ * @request_body as appropriate, passing %FALSE.
+ **/
+
+G_DEFINE_TYPE (SoupMessage, soup_message, G_TYPE_OBJECT)
+
+enum {
+ WROTE_INFORMATIONAL,
+ WROTE_HEADERS,
+ WROTE_CHUNK,
+ WROTE_BODY_DATA,
+ WROTE_BODY,
+
+ GOT_INFORMATIONAL,
+ GOT_HEADERS,
+ GOT_CHUNK,
+ GOT_BODY,
+ CONTENT_SNIFFED,
+
+ RESTARTED,
+ FINISHED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+
+ PROP_METHOD,
+ PROP_URI,
+ PROP_HTTP_VERSION,
+ PROP_FLAGS,
+ PROP_SERVER_SIDE,
+ PROP_STATUS_CODE,
+ PROP_REASON_PHRASE,
+ PROP_FIRST_PARTY,
+ PROP_REQUEST_BODY,
+ PROP_REQUEST_HEADERS,
+ PROP_RESPONSE_BODY,
+ PROP_RESPONSE_HEADERS,
+ PROP_TLS_CERTIFICATE,
+ PROP_TLS_ERRORS,
+
+ LAST_PROP
+};
+
+static void got_body (SoupMessage *req);
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+soup_message_init (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ priv->http_version = priv->orig_http_version = SOUP_HTTP_1_1;
+
+ msg->request_body = soup_message_body_new ();
+ msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
+ msg->response_body = soup_message_body_new ();
+ msg->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupMessage *msg = SOUP_MESSAGE (object);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ soup_message_io_cleanup (msg);
+ if (priv->chunk_allocator_dnotify)
+ priv->chunk_allocator_dnotify (priv->chunk_allocator_data);
+
+ if (priv->uri)
+ soup_uri_free (priv->uri);
+ if (priv->first_party)
+ soup_uri_free (priv->first_party);
+ if (priv->addr)
+ g_object_unref (priv->addr);
+
+ if (priv->auth)
+ g_object_unref (priv->auth);
+ if (priv->proxy_auth)
+ g_object_unref (priv->proxy_auth);
+
+ g_slist_free (priv->disabled_features);
+
+ while (priv->decoders) {
+ g_object_unref (priv->decoders->data);
+ priv->decoders = g_slist_delete_link (priv->decoders, priv->decoders);
+ }
+
+ if (priv->tls_certificate)
+ g_object_unref (priv->tls_certificate);
+
+ soup_message_body_free (msg->request_body);
+ soup_message_headers_free (msg->request_headers);
+ soup_message_body_free (msg->response_body);
+ soup_message_headers_free (msg->response_headers);
+
+ g_free (msg->reason_phrase);
+
+ G_OBJECT_CLASS (soup_message_parent_class)->finalize (object);
+}
+
+static void
+soup_message_class_init (SoupMessageClass *message_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (message_class);
+
+ g_type_class_add_private (message_class, sizeof (SoupMessagePrivate));
+
+ /* virtual method definition */
+ message_class->got_body = got_body;
+
+ /* virtual method override */
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /* signals */
+
+ /**
+ * SoupMessage::wrote-informational:
+ * @msg: the message
+ *
+ * Emitted immediately after writing a 1xx (Informational)
+ * response for a (server-side) message.
+ **/
+ signals[WROTE_INFORMATIONAL] =
+ g_signal_new ("wrote_informational",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupMessage::wrote-headers:
+ * @msg: the message
+ *
+ * Emitted immediately after writing the headers for a
+ * message. (For a client-side message, this is after writing
+ * the request headers; for a server-side message, it is after
+ * writing the response headers.)
+ **/
+ signals[WROTE_HEADERS] =
+ g_signal_new ("wrote_headers",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupMessage::wrote-chunk:
+ * @msg: the message
+ *
+ * Emitted immediately after writing a body chunk for a message.
+ *
+ * Note that this signal is not parallel to
+ * #SoupMessage::got_chunk; it is emitted only when a complete
+ * chunk (added with soup_message_body_append() or
+ * soup_message_body_append_buffer()) has been written. To get
+ * more useful continuous progress information, use
+ * #SoupMessage::wrote_body_data.
+ **/
+ signals[WROTE_CHUNK] =
+ g_signal_new ("wrote_chunk",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupMessage::wrote-body-data:
+ * @msg: the message
+ * @chunk: the data written
+ *
+ * Emitted immediately after writing a portion of the message
+ * body to the network.
+ *
+ * Unlike #SoupMessage::wrote_chunk, this is emitted after
+ * every successful write() call, not only after finishing a
+ * complete "chunk".
+ *
+ * Since: 2.4.1
+ **/
+ signals[WROTE_BODY_DATA] =
+ g_signal_new ("wrote_body_data",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* FIXME after next ABI break */
+ NULL, NULL,
+ soup_marshal_NONE__BOXED,
+ G_TYPE_NONE, 1,
+ SOUP_TYPE_BUFFER);
+
+ /**
+ * SoupMessage::wrote-body:
+ * @msg: the message
+ *
+ * Emitted immediately after writing the complete body for a
+ * message. (For a client-side message, this means that
+ * libsoup is done writing and is now waiting for the response
+ * from the server. For a server-side message, this means that
+ * libsoup has finished writing the response and is nearly
+ * done with the message.)
+ **/
+ signals[WROTE_BODY] =
+ g_signal_new ("wrote_body",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupMessage::got-informational:
+ * @msg: the message
+ *
+ * Emitted after receiving a 1xx (Informational) response for
+ * a (client-side) message. The response_headers will be
+ * filled in with the headers associated with the
+ * informational response; however, those header values will
+ * be erased after this signal is done.
+ *
+ * If you cancel or requeue @msg while processing this signal,
+ * then the current HTTP I/O will be stopped after this signal
+ * emission finished, and @msg's connection will be closed.
+ **/
+ signals[GOT_INFORMATIONAL] =
+ g_signal_new ("got_informational",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, got_informational),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupMessage::got-headers:
+ * @msg: the message
+ *
+ * Emitted after receiving all message headers for a message.
+ * (For a client-side message, this is after receiving the
+ * Status-Line and response headers; for a server-side
+ * message, it is after receiving the Request-Line and request
+ * headers.)
+ *
+ * See also soup_message_add_header_handler() and
+ * soup_message_add_status_code_handler(), which can be used
+ * to connect to a subset of emissions of this signal.
+ *
+ * If you cancel or requeue @msg while processing this signal,
+ * then the current HTTP I/O will be stopped after this signal
+ * emission finished, and @msg's connection will be closed.
+ * (If you need to requeue a message--eg, after handling
+ * authentication or redirection--it is usually better to
+ * requeue it from a #SoupMessage::got_body handler rather
+ * than a #SoupMessage::got_header handler, so that the
+ * existing HTTP connection can be reused.)
+ **/
+ signals[GOT_HEADERS] =
+ g_signal_new ("got_headers",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, got_headers),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupMessage::got-chunk:
+ * @msg: the message
+ * @chunk: the just-read chunk
+ *
+ * Emitted after receiving a chunk of a message body. Note
+ * that "chunk" in this context means any subpiece of the
+ * body, not necessarily the specific HTTP 1.1 chunks sent by
+ * the other side.
+ *
+ * If you cancel or requeue @msg while processing this signal,
+ * then the current HTTP I/O will be stopped after this signal
+ * emission finished, and @msg's connection will be closed.
+ **/
+ signals[GOT_CHUNK] =
+ g_signal_new ("got_chunk",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
+ NULL, NULL,
+ soup_marshal_NONE__BOXED,
+ G_TYPE_NONE, 1,
+ /* Use %G_SIGNAL_TYPE_STATIC_SCOPE so that
+ * the %SOUP_MEMORY_TEMPORARY buffers used
+ * by soup-message-io.c when emitting this
+ * signal don't get forcibly copied by
+ * g_signal_emit().
+ */
+ SOUP_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ /**
+ * SoupMessage::got-body:
+ * @msg: the message
+ *
+ * Emitted after receiving the complete message body. (For a
+ * server-side message, this means it has received the request
+ * body. For a client-side message, this means it has received
+ * the response body and is nearly done with the message.)
+ *
+ * See also soup_message_add_header_handler() and
+ * soup_message_add_status_code_handler(), which can be used
+ * to connect to a subset of emissions of this signal.
+ **/
+ signals[GOT_BODY] =
+ g_signal_new ("got_body",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, got_body),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupMessage::content-sniffed:
+ * @msg: the message
+ * @type: the content type that we got from sniffing
+ * @params: (element-type utf8 utf8): a #GHashTable with the parameters
+ *
+ * This signal is emitted after %got-headers, and before the
+ * first %got-chunk. If content sniffing is disabled, or no
+ * content sniffing will be performed, due to the sniffer
+ * deciding to trust the Content-Type sent by the server, this
+ * signal is emitted immediately after %got_headers, and @type
+ * is %NULL.
+ *
+ * If the #SoupContentSniffer feature is enabled, and the
+ * sniffer decided to perform sniffing, the first %got_chunk
+ * emission may be delayed, so that the sniffer has enough
+ * data to correctly sniff the content. It notified the
+ * library user that the content has been sniffed, and allows
+ * it to change the header contents in the message, if
+ * desired.
+ *
+ * After this signal is emitted, the data that was spooled so
+ * that sniffing could be done is delivered on the first
+ * emission of %got_chunk.
+ *
+ * Since: 2.27.3
+ **/
+ signals[CONTENT_SNIFFED] =
+ g_signal_new ("content_sniffed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ soup_marshal_NONE__STRING_BOXED,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_HASH_TABLE);
+
+ /**
+ * SoupMessage::restarted:
+ * @msg: the message
+ *
+ * Emitted when a request that was already sent once is now
+ * being sent again (eg, because the first attempt received a
+ * redirection response, or because we needed to use
+ * authentication).
+ **/
+ signals[RESTARTED] =
+ g_signal_new ("restarted",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, restarted),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupMessage::finished:
+ * @msg: the message
+ *
+ * Emitted when all HTTP processing is finished for a message.
+ * (After #SoupMessage::got_body for client-side messages, or
+ * after #SoupMessage::wrote_body for server-side messages.)
+ **/
+ signals[FINISHED] =
+ g_signal_new ("finished",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupMessageClass, finished),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /* properties */
+ /**
+ * SOUP_MESSAGE_METHOD:
+ *
+ * Alias for the #SoupMessage:method property. (The message's
+ * HTTP method.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_METHOD,
+ g_param_spec_string (SOUP_MESSAGE_METHOD,
+ "Method",
+ "The message's HTTP method",
+ SOUP_METHOD_GET,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_URI:
+ *
+ * Alias for the #SoupMessage:uri property. (The message's
+ * #SoupURI.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_URI,
+ g_param_spec_boxed (SOUP_MESSAGE_URI,
+ "URI",
+ "The message's Request-URI",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_HTTP_VERSION:
+ *
+ * Alias for the #SoupMessage:http-version property. (The
+ * message's #SoupHTTPVersion.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_HTTP_VERSION,
+ g_param_spec_enum (SOUP_MESSAGE_HTTP_VERSION,
+ "HTTP Version",
+ "The HTTP protocol version to use",
+ SOUP_TYPE_HTTP_VERSION,
+ SOUP_HTTP_1_1,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_FLAGS:
+ *
+ * Alias for the #SoupMessage:flags property. (The message's
+ * #SoupMessageFlags.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_FLAGS,
+ g_param_spec_flags (SOUP_MESSAGE_FLAGS,
+ "Flags",
+ "Various message options",
+ SOUP_TYPE_MESSAGE_FLAGS,
+ 0,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_SERVER_SIDE:
+ *
+ * Alias for the #SoupMessage:server-side property. (%TRUE if
+ * the message was created by #SoupServer.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SERVER_SIDE,
+ g_param_spec_boolean (SOUP_MESSAGE_SERVER_SIDE,
+ "Server-side",
+ "Whether or not the message is server-side rather than client-side",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_MESSAGE_STATUS_CODE:
+ *
+ * Alias for the #SoupMessage:status-code property. (The
+ * message's HTTP response status code.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_STATUS_CODE,
+ g_param_spec_uint (SOUP_MESSAGE_STATUS_CODE,
+ "Status code",
+ "The HTTP response status code",
+ 0, 599, 0,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_REASON_PHRASE:
+ *
+ * Alias for the #SoupMessage:reason-phrase property. (The
+ * message's HTTP response reason phrase.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REASON_PHRASE,
+ g_param_spec_string (SOUP_MESSAGE_REASON_PHRASE,
+ "Reason phrase",
+ "The HTTP response reason phrase",
+ NULL,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_FIRST_PARTY:
+ *
+ * Alias for the #SoupMessage:first-party property. (The
+ * #SoupURI loaded in the application when the message was
+ * queued.)
+ *
+ * Since: 2.30
+ **/
+ /**
+ * SoupMessage:first-party:
+ *
+ * The #SoupURI loaded in the application when the message was
+ * queued.
+ *
+ * Since: 2.30
+ */
+ g_object_class_install_property (
+ object_class, PROP_FIRST_PARTY,
+ g_param_spec_boxed (SOUP_MESSAGE_FIRST_PARTY,
+ "First party",
+ "The URI loaded in the application when the message was requested.",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_REQUEST_BODY:
+ *
+ * Alias for the #SoupMessage:request-body property. (The
+ * message's HTTP request body.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REQUEST_BODY,
+ g_param_spec_boxed (SOUP_MESSAGE_REQUEST_BODY,
+ "Request Body",
+ "The HTTP request content",
+ SOUP_TYPE_MESSAGE_BODY,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_MESSAGE_REQUEST_HEADERS:
+ *
+ * Alias for the #SoupMessage:request-headers property. (The
+ * message's HTTP request headers.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REQUEST_HEADERS,
+ g_param_spec_boxed (SOUP_MESSAGE_REQUEST_HEADERS,
+ "Request Headers",
+ "The HTTP request headers",
+ SOUP_TYPE_MESSAGE_HEADERS,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_MESSAGE_RESPONSE_BODY:
+ *
+ * Alias for the #SoupMessage:response-body property. (The
+ * message's HTTP response body.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_RESPONSE_BODY,
+ g_param_spec_boxed (SOUP_MESSAGE_RESPONSE_BODY,
+ "Response Body",
+ "The HTTP response content",
+ SOUP_TYPE_MESSAGE_BODY,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_MESSAGE_RESPONSE_HEADERS:
+ *
+ * Alias for the #SoupMessage:response-headers property. (The
+ * message's HTTP response headers.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_RESPONSE_HEADERS,
+ g_param_spec_boxed (SOUP_MESSAGE_RESPONSE_HEADERS,
+ "Response Headers",
+ "The HTTP response headers",
+ SOUP_TYPE_MESSAGE_HEADERS,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_MESSAGE_TLS_CERTIFICATE:
+ *
+ * Alias for the #SoupMessage:tls-certificate property. (The
+ * TLS certificate associated with the message, if any.)
+ *
+ * Since: 2.34
+ **/
+ /**
+ * SoupMessage:tls-certificate:
+ *
+ * The #GTlsCertificate associated with the message
+ *
+ * Since: 2.34
+ */
+ g_object_class_install_property (
+ object_class, PROP_TLS_CERTIFICATE,
+ g_param_spec_object (SOUP_MESSAGE_TLS_CERTIFICATE,
+ "TLS Certificate",
+ "The TLS certificate associated with the message",
+ G_TYPE_TLS_CERTIFICATE,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_TLS_ERRORS:
+ *
+ * Alias for the #SoupMessage:tls-errors property. (The
+ * verification errors on #SoupMessage:tls-certificate.)
+ *
+ * Since: 2.34
+ **/
+ /**
+ * SoupMessage:tls-certificate:
+ *
+ * The verification errors on #SoupMessage:tls-certificate
+ *
+ * Since: 2.34
+ */
+ g_object_class_install_property (
+ object_class, PROP_TLS_ERRORS,
+ g_param_spec_flags (SOUP_MESSAGE_TLS_ERRORS,
+ "TLS Errors",
+ "The verification errors on the message's TLS certificate",
+ G_TYPE_TLS_CERTIFICATE_FLAGS, 0,
+ G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupMessage *msg = SOUP_MESSAGE (object);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ switch (prop_id) {
+ case PROP_METHOD:
+ msg->method = g_intern_string (g_value_get_string (value));
+ break;
+ case PROP_URI:
+ soup_message_set_uri (msg, g_value_get_boxed (value));
+ break;
+ case PROP_HTTP_VERSION:
+ soup_message_set_http_version (msg, g_value_get_enum (value));
+ break;
+ case PROP_FLAGS:
+ soup_message_set_flags (msg, g_value_get_flags (value));
+ break;
+ case PROP_SERVER_SIDE:
+ priv->server_side = g_value_get_boolean (value);
+ if (priv->server_side) {
+ soup_message_headers_set_encoding (msg->response_headers,
+ SOUP_ENCODING_CONTENT_LENGTH);
+ }
+ break;
+ case PROP_STATUS_CODE:
+ soup_message_set_status (msg, g_value_get_uint (value));
+ break;
+ case PROP_REASON_PHRASE:
+ soup_message_set_status_full (msg, msg->status_code,
+ g_value_get_string (value));
+ break;
+ case PROP_FIRST_PARTY:
+ soup_message_set_first_party (msg, g_value_get_boxed (value));
+ break;
+ case PROP_TLS_CERTIFICATE:
+ if (priv->tls_certificate)
+ g_object_unref (priv->tls_certificate);
+ priv->tls_certificate = g_value_dup_object (value);
+ if (priv->tls_certificate && !priv->tls_errors)
+ priv->msg_flags |= SOUP_MESSAGE_CERTIFICATE_TRUSTED;
+ break;
+ case PROP_TLS_ERRORS:
+ priv->tls_errors = g_value_get_flags (value);
+ if (priv->tls_errors)
+ priv->msg_flags &= ~SOUP_MESSAGE_CERTIFICATE_TRUSTED;
+ else if (priv->tls_certificate)
+ priv->msg_flags |= SOUP_MESSAGE_CERTIFICATE_TRUSTED;
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupMessage *msg = SOUP_MESSAGE (object);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ switch (prop_id) {
+ case PROP_METHOD:
+ g_value_set_string (value, msg->method);
+ break;
+ case PROP_URI:
+ g_value_set_boxed (value, priv->uri);
+ break;
+ case PROP_HTTP_VERSION:
+ g_value_set_enum (value, priv->http_version);
+ break;
+ case PROP_FLAGS:
+ g_value_set_flags (value, priv->msg_flags);
+ break;
+ case PROP_SERVER_SIDE:
+ g_value_set_boolean (value, priv->server_side);
+ break;
+ case PROP_STATUS_CODE:
+ g_value_set_uint (value, msg->status_code);
+ break;
+ case PROP_REASON_PHRASE:
+ g_value_set_string (value, msg->reason_phrase);
+ break;
+ case PROP_FIRST_PARTY:
+ g_value_set_boxed (value, priv->first_party);
+ break;
+ case PROP_REQUEST_BODY:
+ g_value_set_boxed (value, msg->request_body);
+ break;
+ case PROP_REQUEST_HEADERS:
+ g_value_set_boxed (value, msg->request_headers);
+ break;
+ case PROP_RESPONSE_BODY:
+ g_value_set_boxed (value, msg->response_body);
+ break;
+ case PROP_RESPONSE_HEADERS:
+ g_value_set_boxed (value, msg->response_headers);
+ break;
+ case PROP_TLS_CERTIFICATE:
+ g_value_set_object (value, priv->tls_certificate);
+ break;
+ case PROP_TLS_ERRORS:
+ g_value_set_flags (value, priv->tls_errors);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+/**
+ * soup_message_new:
+ * @method: the HTTP method for the created request
+ * @uri_string: the destination endpoint (as a string)
+ *
+ * Creates a new empty #SoupMessage, which will connect to @uri
+ *
+ * Return value: the new #SoupMessage (or %NULL if @uri could not
+ * be parsed).
+ */
+SoupMessage *
+soup_message_new (const char *method, const char *uri_string)
+{
+ SoupMessage *msg;
+ SoupURI *uri;
+
+ g_return_val_if_fail (method != NULL, NULL);
+ g_return_val_if_fail (uri_string != NULL, NULL);
+
+ uri = soup_uri_new (uri_string);
+ if (!uri)
+ return NULL;
+ if (!uri->host) {
+ soup_uri_free (uri);
+ return NULL;
+ }
+
+ msg = soup_message_new_from_uri (method, uri);
+ soup_uri_free (uri);
+ return msg;
+}
+
+/**
+ * soup_message_new_from_uri:
+ * @method: the HTTP method for the created request
+ * @uri: the destination endpoint (as a #SoupURI)
+ *
+ * Creates a new empty #SoupMessage, which will connect to @uri
+ *
+ * Return value: the new #SoupMessage
+ */
+SoupMessage *
+soup_message_new_from_uri (const char *method, SoupURI *uri)
+{
+ return g_object_new (SOUP_TYPE_MESSAGE,
+ SOUP_MESSAGE_METHOD, method,
+ SOUP_MESSAGE_URI, uri,
+ NULL);
+}
+
+/**
+ * soup_message_set_request:
+ * @msg: the message
+ * @content_type: MIME Content-Type of the body
+ * @req_use: a #SoupMemoryUse describing how to handle @req_body
+ * @req_body: a data buffer containing the body of the message request.
+ * @req_length: the byte length of @req_body.
+ *
+ * Convenience function to set the request body of a #SoupMessage. If
+ * @content_type is %NULL, the request body must be empty as well.
+ */
+void
+soup_message_set_request (SoupMessage *msg,
+ const char *content_type,
+ SoupMemoryUse req_use,
+ const char *req_body,
+ gsize req_length)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (content_type != NULL || req_length == 0);
+
+ if (content_type) {
+ soup_message_headers_replace (msg->request_headers,
+ "Content-Type", content_type);
+ soup_message_body_append (msg->request_body, req_use,
+ req_body, req_length);
+ } else {
+ soup_message_headers_remove (msg->request_headers,
+ "Content-Type");
+ soup_message_body_truncate (msg->request_body);
+ }
+}
+
+/**
+ * soup_message_set_response:
+ * @msg: the message
+ * @content_type: (allow-none): MIME Content-Type of the body
+ * @resp_use: a #SoupMemoryUse describing how to handle @resp_body
+ * @resp_body: (array length=resp_length) (element-type guint8): a data buffer
+ * containing the body of the message response.
+ * @resp_length: the byte length of @resp_body.
+ *
+ * Convenience function to set the response body of a #SoupMessage. If
+ * @content_type is %NULL, the response body must be empty as well.
+ */
+void
+soup_message_set_response (SoupMessage *msg,
+ const char *content_type,
+ SoupMemoryUse resp_use,
+ const char *resp_body,
+ gsize resp_length)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (content_type != NULL || resp_length == 0);
+
+ if (content_type) {
+ soup_message_headers_replace (msg->response_headers,
+ "Content-Type", content_type);
+ soup_message_body_append (msg->response_body, resp_use,
+ resp_body, resp_length);
+ } else {
+ soup_message_headers_remove (msg->response_headers,
+ "Content-Type");
+ soup_message_body_truncate (msg->response_body);
+ }
+}
+
+/**
+ * soup_message_wrote_informational:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %wrote_informational signal, indicating that the IO layer
+ * finished writing an informational (1xx) response for @msg.
+ **/
+void
+soup_message_wrote_informational (SoupMessage *msg)
+{
+ g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
+}
+
+/**
+ * soup_message_wrote_headers:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %wrote_headers signal, indicating that the IO layer
+ * finished writing the (non-informational) headers for @msg.
+ **/
+void
+soup_message_wrote_headers (SoupMessage *msg)
+{
+ g_signal_emit (msg, signals[WROTE_HEADERS], 0);
+}
+
+/**
+ * soup_message_wrote_chunk:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %wrote_chunk signal, indicating that the IO layer
+ * finished writing a chunk of @msg's body.
+ **/
+void
+soup_message_wrote_chunk (SoupMessage *msg)
+{
+ g_signal_emit (msg, signals[WROTE_CHUNK], 0);
+}
+
+/**
+ * soup_message_wrote_body_data:
+ * @msg: a #SoupMessage
+ * @chunk: the data written
+ *
+ * Emits the %wrote_body_data signal, indicating that the IO layer
+ * finished writing a portion of @msg's body.
+ **/
+void
+soup_message_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk)
+{
+ g_signal_emit (msg, signals[WROTE_BODY_DATA], 0, chunk);
+}
+
+/**
+ * soup_message_wrote_body:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %wrote_body signal, indicating that the IO layer finished
+ * writing the body for @msg.
+ **/
+void
+soup_message_wrote_body (SoupMessage *msg)
+{
+ g_signal_emit (msg, signals[WROTE_BODY], 0);
+}
+
+/**
+ * soup_message_got_informational:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %got_informational signal, indicating that the IO layer
+ * read a complete informational (1xx) response for @msg.
+ **/
+void
+soup_message_got_informational (SoupMessage *msg)
+{
+ g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
+}
+
+/**
+ * soup_message_got_headers:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %got_headers signal, indicating that the IO layer
+ * finished reading the (non-informational) headers for @msg.
+ **/
+void
+soup_message_got_headers (SoupMessage *msg)
+{
+ g_signal_emit (msg, signals[GOT_HEADERS], 0);
+}
+
+/**
+ * soup_message_got_chunk:
+ * @msg: a #SoupMessage
+ * @chunk: the newly-read chunk
+ *
+ * Emits the %got_chunk signal, indicating that the IO layer finished
+ * reading a chunk of @msg's body.
+ **/
+void
+soup_message_got_chunk (SoupMessage *msg, SoupBuffer *chunk)
+{
+ g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk);
+}
+
+static void
+got_body (SoupMessage *req)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+ SoupMessageBody *body;
+
+ body = priv->server_side ? req->request_body : req->response_body;
+ if (soup_message_body_get_accumulate (body)) {
+ SoupBuffer *buffer;
+
+ buffer = soup_message_body_flatten (body);
+ soup_buffer_free (buffer);
+ }
+}
+
+/**
+ * soup_message_got_body:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %got_body signal, indicating that the IO layer finished
+ * reading the body for @msg.
+ **/
+void
+soup_message_got_body (SoupMessage *msg)
+{
+ g_signal_emit (msg, signals[GOT_BODY], 0);
+}
+
+/**
+ * soup_message_content_sniffed:
+ * @msg: a #SoupMessage
+ * @type: a string with the sniffed content type
+ * @params: a #GHashTable with the parameters
+ *
+ * Emits the %content_sniffed signal, indicating that the IO layer
+ * finished sniffing the content type for @msg. If content sniffing
+ * will not be performed, due to the sniffer deciding to trust the
+ * Content-Type sent by the server, this signal is emitted immediately
+ * after %got_headers, with %NULL as @content_type.
+ **/
+void
+soup_message_content_sniffed (SoupMessage *msg, const char *content_type, GHashTable *params)
+{
+ g_signal_emit (msg, signals[CONTENT_SNIFFED], 0, content_type, params);
+}
+
+/**
+ * soup_message_restarted:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %restarted signal, indicating that @msg should be
+ * requeued.
+ **/
+void
+soup_message_restarted (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD)
+ soup_message_body_truncate (msg->request_body);
+
+ g_signal_emit (msg, signals[RESTARTED], 0);
+}
+
+/**
+ * soup_message_finished:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %finished signal, indicating that @msg has been completely
+ * processed.
+ **/
+void
+soup_message_finished (SoupMessage *msg)
+{
+ g_signal_emit (msg, signals[FINISHED], 0);
+}
+
+static void
+header_handler_free (gpointer header_name, GClosure *closure)
+{
+ g_free (header_name);
+}
+
+static void
+header_handler_metamarshal (GClosure *closure, GValue *return_value,
+ guint n_param_values, const GValue *param_values,
+ gpointer invocation_hint, gpointer marshal_data)
+{
+ SoupMessage *msg = g_value_get_object (¶m_values[0]);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ const char *header_name = marshal_data;
+ SoupMessageHeaders *hdrs;
+
+#ifdef FIXME
+ if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
+ return;
+#endif
+
+ hdrs = priv->server_side ? msg->request_headers : msg->response_headers;
+ if (soup_message_headers_get_one (hdrs, header_name)) {
+ closure->marshal (closure, return_value, n_param_values,
+ param_values, invocation_hint,
+ ((GCClosure *)closure)->callback);
+ }
+}
+
+/**
+ * soup_message_add_header_handler:
+ * @msg: a #SoupMessage
+ * @signal: signal to connect the handler to.
+ * @header: HTTP response header to match against
+ * @callback: the header handler
+ * @user_data: data to pass to @handler_cb
+ *
+ * Adds a signal handler to @msg for @signal, as with
+ * g_signal_connect(), but with two differences: the @callback will
+ * only be run if @msg has a header named @header, and it will only be
+ * run if no earlier handler cancelled or requeued the message.
+ *
+ * If @signal is one of the "got" signals (eg, "got_headers"), or
+ * "finished" or "restarted", then @header is matched against the
+ * incoming message headers (that is, the #request_headers for a
+ * client #SoupMessage, or the #response_headers for a server
+ * #SoupMessage). If @signal is one of the "wrote" signals, then
+ * @header is matched against the outgoing message headers.
+ *
+ * Return value: the handler ID from g_signal_connect()
+ **/
+guint
+soup_message_add_header_handler (SoupMessage *msg,
+ const char *signal,
+ const char *header,
+ GCallback callback,
+ gpointer user_data)
+{
+ GClosure *closure;
+ char *header_name;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
+ g_return_val_if_fail (signal != NULL, 0);
+ g_return_val_if_fail (header != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+
+ closure = g_cclosure_new (callback, user_data, NULL);
+
+ header_name = g_strdup (header);
+ g_closure_set_meta_marshal (closure, header_name,
+ header_handler_metamarshal);
+ g_closure_add_finalize_notifier (closure, header_name,
+ header_handler_free);
+
+ return g_signal_connect_closure (msg, signal, closure, FALSE);
+}
+
+static void
+status_handler_metamarshal (GClosure *closure, GValue *return_value,
+ guint n_param_values, const GValue *param_values,
+ gpointer invocation_hint, gpointer marshal_data)
+{
+ SoupMessage *msg = g_value_get_object (¶m_values[0]);
+ guint status = GPOINTER_TO_UINT (marshal_data);
+
+#ifdef FIXME
+ if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
+ return;
+#endif
+
+ if (msg->status_code == status) {
+ closure->marshal (closure, return_value, n_param_values,
+ param_values, invocation_hint,
+ ((GCClosure *)closure)->callback);
+ }
+}
+
+/**
+ * soup_message_add_status_code_handler:
+ * @msg: a #SoupMessage
+ * @signal: signal to connect the handler to.
+ * @status_code: status code to match against
+ * @callback: the header handler
+ * @user_data: data to pass to @handler_cb
+ *
+ * Adds a signal handler to @msg for @signal, as with
+ * g_signal_connect() but with two differences: the @callback will
+ * only be run if @msg has the status @status_code, and it will only
+ * be run if no earlier handler cancelled or requeued the message.
+ *
+ * @signal must be a signal that will be emitted after @msg's status
+ * is set. For a client #SoupMessage, this means it can't be a "wrote"
+ * signal. For a server #SoupMessage, this means it can't be a "got"
+ * signal.
+ *
+ * Return value: the handler ID from g_signal_connect()
+ **/
+guint
+soup_message_add_status_code_handler (SoupMessage *msg,
+ const char *signal,
+ guint status_code,
+ GCallback callback,
+ gpointer user_data)
+{
+ GClosure *closure;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
+ g_return_val_if_fail (signal != NULL, 0);
+ g_return_val_if_fail (callback != NULL, 0);
+
+ closure = g_cclosure_new (callback, user_data, NULL);
+ g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (status_code),
+ status_handler_metamarshal);
+
+ return g_signal_connect_closure (msg, signal, closure, FALSE);
+}
+
+
+/**
+ * soup_message_set_auth:
+ * @msg: a #SoupMessage
+ * @auth: a #SoupAuth, or %NULL
+ *
+ * Sets @msg to authenticate to its destination using @auth, which
+ * must have already been fully authenticated. If @auth is %NULL, @msg
+ * will not authenticate to its destination.
+ **/
+void
+soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
+{
+ SoupMessagePrivate *priv;
+ char *token;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (auth == NULL || SOUP_IS_AUTH (auth));
+ g_return_if_fail (auth == NULL || soup_auth_is_authenticated (auth));
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->auth) {
+ g_object_unref (priv->auth);
+ soup_message_headers_remove (msg->request_headers,
+ "Authorization");
+ }
+ priv->auth = auth;
+ if (!priv->auth)
+ return;
+
+ g_object_ref (priv->auth);
+ token = soup_auth_get_authorization (auth, msg);
+ soup_message_headers_replace (msg->request_headers,
+ "Authorization", token);
+ g_free (token);
+}
+
+/**
+ * soup_message_get_auth:
+ * @msg: a #SoupMessage
+ *
+ * Gets the #SoupAuth used by @msg for authentication.
+ *
+ * Return value: (transfer none): the #SoupAuth used by @msg for
+ * authentication, or %NULL if @msg is unauthenticated.
+ **/
+SoupAuth *
+soup_message_get_auth (SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ return SOUP_MESSAGE_GET_PRIVATE (msg)->auth;
+}
+
+/**
+ * soup_message_set_proxy_auth:
+ * @msg: a #SoupMessage
+ * @auth: a #SoupAuth, or %NULL
+ *
+ * Sets @msg to authenticate to its proxy using @auth, which must have
+ * already been fully authenticated. If @auth is %NULL, @msg will not
+ * authenticate to its proxy.
+ **/
+void
+soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *auth)
+{
+ SoupMessagePrivate *priv;
+ char *token;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (auth == NULL || SOUP_IS_AUTH (auth));
+ g_return_if_fail (auth == NULL || soup_auth_is_authenticated (auth));
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->proxy_auth) {
+ g_object_unref (priv->proxy_auth);
+ soup_message_headers_remove (msg->request_headers,
+ "Proxy-Authorization");
+ }
+ priv->proxy_auth = auth;
+ if (!priv->proxy_auth)
+ return;
+
+ g_object_ref (priv->proxy_auth);
+ token = soup_auth_get_authorization (auth, msg);
+ soup_message_headers_replace (msg->request_headers,
+ "Proxy-Authorization", token);
+ g_free (token);
+}
+
+/**
+ * soup_message_get_proxy_auth:
+ * @msg: a #SoupMessage
+ *
+ * Gets the #SoupAuth used by @msg for authentication to its proxy..
+ *
+ * Return value: the #SoupAuth used by @msg for authentication to its
+ * proxy, or %NULL if @msg isn't authenticated to its proxy.
+ **/
+SoupAuth *
+soup_message_get_proxy_auth (SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ return SOUP_MESSAGE_GET_PRIVATE (msg)->proxy_auth;
+}
+
+/**
+ * soup_message_cleanup_response:
+ * @req: a #SoupMessage
+ *
+ * Cleans up all response data on @req, so that the request can be sent
+ * again and receive a new response. (Eg, as a result of a redirect or
+ * authorization request.)
+ **/
+void
+soup_message_cleanup_response (SoupMessage *req)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+
+ soup_message_body_truncate (req->response_body);
+ soup_message_headers_clear (req->response_headers);
+ if (priv->server_side) {
+ soup_message_headers_set_encoding (req->response_headers,
+ SOUP_ENCODING_CONTENT_LENGTH);
+ }
+
+ while (priv->decoders) {
+ g_object_unref (priv->decoders->data);
+ priv->decoders = g_slist_delete_link (priv->decoders, priv->decoders);
+ }
+ priv->msg_flags &= ~SOUP_MESSAGE_CONTENT_DECODED;
+ priv->msg_flags &= ~SOUP_MESSAGE_CERTIFICATE_TRUSTED;
+
+ req->status_code = SOUP_STATUS_NONE;
+ if (req->reason_phrase) {
+ g_free (req->reason_phrase);
+ req->reason_phrase = NULL;
+ }
+ priv->http_version = priv->orig_http_version;
+
+ if (priv->tls_certificate) {
+ g_object_unref (priv->tls_certificate);
+ priv->tls_certificate = NULL;
+ }
+ priv->tls_errors = 0;
+
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_FLAGS);
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_TLS_CERTIFICATE);
+ g_object_notify (G_OBJECT (req), SOUP_MESSAGE_TLS_ERRORS);
+}
+
+/**
+ * SoupMessageFlags:
+ * @SOUP_MESSAGE_NO_REDIRECT: The session should not follow redirect
+ * (3xx) responses received by this message.
+ * @SOUP_MESSAGE_CAN_REBUILD: The caller will rebuild the request
+ * body if the message is restarted; see
+ * soup_message_body_set_accumulate() for more details.
+ * @SOUP_MESSAGE_OVERWRITE_CHUNKS: Deprecated: equivalent to calling
+ * soup_message_body_set_accumulate() on the incoming message body
+ * (ie, %response_body for a client-side request), passing %FALSE.
+ * @SOUP_MESSAGE_CONTENT_DECODED: Set by #SoupContentDecoder to
+ * indicate that it has removed the Content-Encoding on a message (and
+ * so headers such as Content-Length may no longer accurately describe
+ * the body).
+ * @SOUP_MESSAGE_CERTIFICATE_TRUSTED: if %TRUE after an https response
+ * has been received, indicates that the server's SSL certificate is
+ * trusted according to the session's CA.
+ *
+ * Various flags that can be set on a #SoupMessage to alter its
+ * behavior.
+ **/
+
+/**
+ * soup_message_set_flags:
+ * @msg: a #SoupMessage
+ * @flags: a set of #SoupMessageFlags values
+ *
+ * Sets the specified flags on @msg.
+ **/
+void
+soup_message_set_flags (SoupMessage *msg, SoupMessageFlags flags)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if ((priv->msg_flags ^ flags) & SOUP_MESSAGE_OVERWRITE_CHUNKS) {
+ soup_message_body_set_accumulate (
+ priv->server_side ? msg->request_body : msg->response_body,
+ !(flags & SOUP_MESSAGE_OVERWRITE_CHUNKS));
+ }
+
+ priv->msg_flags = flags;
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FLAGS);
+}
+
+/**
+ * soup_message_get_flags:
+ * @msg: a #SoupMessage
+ *
+ * Gets the flags on @msg
+ *
+ * Return value: the flags
+ **/
+SoupMessageFlags
+soup_message_get_flags (SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
+
+ return SOUP_MESSAGE_GET_PRIVATE (msg)->msg_flags;
+}
+
+/**
+ * SoupHTTPVersion:
+ * @SOUP_HTTP_1_0: HTTP 1.0 (RFC 1945)
+ * @SOUP_HTTP_1_1: HTTP 1.1 (RFC 2616)
+ *
+ * Indicates the HTTP protocol version being used.
+ **/
+
+/**
+ * soup_message_set_http_version:
+ * @msg: a #SoupMessage
+ * @version: the HTTP version
+ *
+ * Sets the HTTP version on @msg. The default version is
+ * %SOUP_HTTP_1_1. Setting it to %SOUP_HTTP_1_0 will prevent certain
+ * functionality from being used.
+ **/
+void
+soup_message_set_http_version (SoupMessage *msg, SoupHTTPVersion version)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ priv->http_version = version;
+ if (msg->status_code == SOUP_STATUS_NONE)
+ priv->orig_http_version = version;
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_HTTP_VERSION);
+}
+
+/**
+ * soup_message_get_http_version:
+ * @msg: a #SoupMessage
+ *
+ * Gets the HTTP version of @msg. This is the minimum of the
+ * version from the request and the version from the response.
+ *
+ * Return value: the HTTP version
+ **/
+SoupHTTPVersion
+soup_message_get_http_version (SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
+
+ return SOUP_MESSAGE_GET_PRIVATE (msg)->http_version;
+}
+
+/**
+ * soup_message_is_keepalive:
+ * @msg: a #SoupMessage
+ *
+ * Determines whether or not @msg's connection can be kept alive for
+ * further requests after processing @msg, based on the HTTP version,
+ * Connection header, etc.
+ *
+ * Return value: %TRUE or %FALSE.
+ **/
+gboolean
+soup_message_is_keepalive (SoupMessage *msg)
+{
+ const char *c_conn, *s_conn;
+
+ c_conn = soup_message_headers_get_list (msg->request_headers,
+ "Connection");
+ s_conn = soup_message_headers_get_list (msg->response_headers,
+ "Connection");
+
+ if (msg->status_code == SOUP_STATUS_OK &&
+ msg->method == SOUP_METHOD_CONNECT)
+ return TRUE;
+
+ /* Not persistent if the server sent a terminate-by-EOF response */
+ if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_EOF)
+ return FALSE;
+
+ if (SOUP_MESSAGE_GET_PRIVATE (msg)->http_version == SOUP_HTTP_1_0) {
+ /* In theory, HTTP/1.0 connections are only persistent
+ * if the client requests it, and the server agrees.
+ * But some servers do keep-alive even if the client
+ * doesn't request it. So ignore c_conn.
+ */
+
+ if (!s_conn || !soup_header_contains (s_conn, "Keep-Alive"))
+ return FALSE;
+ } else {
+ /* Normally persistent unless either side requested otherwise */
+ if (c_conn && soup_header_contains (c_conn, "close"))
+ return FALSE;
+ if (s_conn && soup_header_contains (s_conn, "close"))
+ return FALSE;
+
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * soup_message_set_uri:
+ * @msg: a #SoupMessage
+ * @uri: the new #SoupURI
+ *
+ * Sets @msg's URI to @uri. If @msg has already been sent and you want
+ * to re-send it with the new URI, you need to call
+ * soup_session_requeue_message().
+ **/
+void
+soup_message_set_uri (SoupMessage *msg, SoupURI *uri)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->uri)
+ soup_uri_free (priv->uri);
+ if (priv->addr) {
+ g_object_unref (priv->addr);
+ priv->addr = NULL;
+ }
+ priv->uri = soup_uri_copy (uri);
+
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_URI);
+}
+
+/**
+ * soup_message_get_uri:
+ * @msg: a #SoupMessage
+ *
+ * Gets @msg's URI
+ *
+ * Return value: (transfer none): the URI @msg is targeted for.
+ **/
+SoupURI *
+soup_message_get_uri (SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ return SOUP_MESSAGE_GET_PRIVATE (msg)->uri;
+}
+
+/**
+ * soup_message_get_address:
+ * @msg: a #SoupMessage
+ *
+ * Gets the address @msg's URI points to. After first setting the
+ * URI on a message, this will be unresolved, although the message's
+ * session will resolve it before sending the message.
+ *
+ * Return value: (transfer none): the address @msg's URI points to
+ *
+ * Since: 2.26
+ **/
+SoupAddress *
+soup_message_get_address (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ if (!priv->addr) {
+ priv->addr = soup_address_new (priv->uri->host,
+ priv->uri->port);
+ }
+ return priv->addr;
+}
+
+/**
+ * soup_message_set_status:
+ * @msg: a #SoupMessage
+ * @status_code: an HTTP status code
+ *
+ * Sets @msg's status code to @status_code. If @status_code is a
+ * known value, it will also set @msg's reason_phrase.
+ **/
+void
+soup_message_set_status (SoupMessage *msg, guint status_code)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (status_code != 0);
+
+ g_free (msg->reason_phrase);
+
+ msg->status_code = status_code;
+ msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
+}
+
+/**
+ * soup_message_set_status_full:
+ * @msg: a #SoupMessage
+ * @status_code: an HTTP status code
+ * @reason_phrase: a description of the status
+ *
+ * Sets @msg's status code and reason phrase.
+ **/
+void
+soup_message_set_status_full (SoupMessage *msg,
+ guint status_code,
+ const char *reason_phrase)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (status_code != 0);
+ g_return_if_fail (reason_phrase != NULL);
+
+ g_free (msg->reason_phrase);
+
+ msg->status_code = status_code;
+ msg->reason_phrase = g_strdup (reason_phrase);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
+}
+
+/**
+ * SoupChunkAllocator:
+ * @msg: the #SoupMessage the chunk is being allocated for
+ * @max_len: the maximum length that will be read, or 0.
+ * @user_data: the data passed to soup_message_set_chunk_allocator()
+ *
+ * The prototype for a chunk allocation callback. This should allocate
+ * a new #SoupBuffer and return it for the I/O layer to read message
+ * body data off the network into.
+ *
+ * If @max_len is non-0, it indicates the maximum number of bytes that
+ * could be read, based on what is known about the message size. Note
+ * that this might be a very large number, and you should not simply
+ * try to allocate that many bytes blindly. If @max_len is 0, that
+ * means that libsoup does not know how many bytes remain to be read,
+ * and the allocator should return a buffer of a size that it finds
+ * convenient.
+ *
+ * If the allocator returns %NULL, the message will be paused. It is
+ * up to the application to make sure that it gets unpaused when it
+ * becomes possible to allocate a new buffer.
+ *
+ * Return value: the new buffer (or %NULL)
+ **/
+
+/**
+ * soup_message_set_chunk_allocator:
+ * @msg: a #SoupMessage
+ * @allocator: the chunk allocator callback
+ * @user_data: data to pass to @allocator
+ * @destroy_notify: destroy notifier to free @user_data when @msg is
+ * destroyed
+ *
+ * Sets an alternate chunk-allocation function to use when reading
+ * @msg's body. Every time data is available to read, libsoup will
+ * call @allocator, which should return a #SoupBuffer. (See
+ * #SoupChunkAllocator for additional details.) Libsoup will then read
+ * data from the network into that buffer, and update the buffer's
+ * %length to indicate how much data it read.
+ *
+ * Generally, a custom chunk allocator would be used in conjunction
+ * with soup_message_body_set_accumulate() %FALSE and
+ * #SoupMessage::got_chunk, as part of a strategy to avoid unnecessary
+ * copying of data. However, you cannot assume that every call to the
+ * allocator will be followed by a call to your %got_chunk handler; if
+ * an I/O error occurs, then the buffer will be unreffed without ever
+ * having been used. If your buffer-allocation strategy requires
+ * special cleanup, use soup_buffer_new_with_owner() rather than doing
+ * the cleanup from the %got_chunk handler.
+ *
+ * The other thing to remember when using non-accumulating message
+ * bodies is that the buffer passed to the %got_chunk handler will be
+ * unreffed after the handler returns, just as it would be in the
+ * non-custom-allocated case. If you want to hand the chunk data off
+ * to some other part of your program to use later, you'll need to ref
+ * the #SoupBuffer (or its owner, in the soup_buffer_new_with_owner()
+ * case) to ensure that the data remains valid.
+ **/
+void
+soup_message_set_chunk_allocator (SoupMessage *msg,
+ SoupChunkAllocator allocator,
+ gpointer user_data,
+ GDestroyNotify destroy_notify)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->chunk_allocator_dnotify)
+ priv->chunk_allocator_dnotify (priv->chunk_allocator_data);
+
+ priv->chunk_allocator = allocator;
+ priv->chunk_allocator_data = user_data;
+ priv->chunk_allocator_dnotify = destroy_notify;
+}
+
+/**
+ * soup_message_disable_feature:
+ * @msg: a #SoupMessage
+ * @feature_type: the #GType of a #SoupSessionFeature
+ *
+ * This disables the actions of #SoupSessionFeature<!-- -->s with the
+ * given @feature_type (or a subclass of that type) on @msg, so that
+ * @msg is processed as though the feature(s) hadn't been added to the
+ * session. Eg, passing #SOUP_TYPE_PROXY_RESOLVER for @feature_type
+ * will disable proxy handling and cause @msg to be sent directly to
+ * the indicated origin server, regardless of system proxy
+ * configuration.
+ *
+ * You must call this before queueing @msg on a session; calling it on
+ * a message that has already been queued is undefined. In particular,
+ * you cannot call this on a message that is being requeued after a
+ * redirect or authentication.
+ *
+ * Since: 2.28
+ **/
+void
+soup_message_disable_feature (SoupMessage *msg, GType feature_type)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ priv->disabled_features = g_slist_prepend (priv->disabled_features,
+ GSIZE_TO_POINTER (feature_type));
+}
+
+gboolean
+soup_message_disables_feature (SoupMessage *msg, gpointer feature)
+{
+ SoupMessagePrivate *priv;
+ GSList *f;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ for (f = priv->disabled_features; f; f = f->next) {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (feature, (GType) GPOINTER_TO_SIZE (f->data)))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * soup_message_get_first_party:
+ * @msg: a #SoupMessage
+ *
+ * Returns: (transfer none): the @msg's first party #SoupURI
+ *
+ * Since: 2.30
+ **/
+SoupURI *
+soup_message_get_first_party (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ return priv->first_party;
+}
+
+/**
+ * soup_message_set_first_party:
+ * @msg: a #SoupMessage
+ * @first_party: the #SoupURI for the @msg's first party
+ *
+ * Sets @first_party as the main document #SoupURI for @msg. For
+ * details of when and how this is used refer to the documentation for
+ * #SoupCookieJarAcceptPolicy.
+ *
+ * Since: 2.30
+ **/
+void
+soup_message_set_first_party (SoupMessage *msg,
+ SoupURI *first_party)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (first_party != NULL);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->first_party) {
+ if (soup_uri_equal (priv->first_party, first_party))
+ return;
+
+ soup_uri_free (priv->first_party);
+ }
+
+ priv->first_party = soup_uri_copy (first_party);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FIRST_PARTY);
+}
+
+/**
+ * soup_message_get_https_status:
+ * @msg: a #SoupMessage
+ * @certificate: (out) (transfer none): @msg's TLS certificate
+ * @errors: (out): the verification status of @certificate
+ *
+ * If @msg is using https, this retrieves the #GTlsCertificate
+ * associated with its connection, and the #GTlsCertificateFlags showing
+ * what problems, if any, have been found with that certificate.
+ *
+ * Return value: %TRUE if @msg uses https, %FALSE if not
+ *
+ * Since: 2.34
+ */
+gboolean
+soup_message_get_https_status (SoupMessage *msg,
+ GTlsCertificate **certificate,
+ GTlsCertificateFlags *errors)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (certificate)
+ *certificate = priv->tls_certificate;
+ if (errors)
+ *errors = priv->tls_errors;
+ return priv->tls_certificate != NULL;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_H
+#define SOUP_MESSAGE_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+#include <libsoup/soup-message-headers.h>
+#include <libsoup/soup-method.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_MESSAGE (soup_message_get_type ())
+#define SOUP_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_MESSAGE, SoupMessage))
+#define SOUP_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_MESSAGE, SoupMessageClass))
+#define SOUP_IS_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_MESSAGE))
+#define SOUP_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_MESSAGE))
+#define SOUP_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_MESSAGE, SoupMessageClass))
+
+struct _SoupMessage {
+ GObject parent;
+
+ /*< public >*/
+ const char *method;
+
+ guint status_code;
+ char *reason_phrase;
+
+ SoupMessageBody *request_body;
+ SoupMessageHeaders *request_headers;
+
+ SoupMessageBody *response_body;
+ SoupMessageHeaders *response_headers;
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*wrote_informational) (SoupMessage *msg);
+ void (*wrote_headers) (SoupMessage *msg);
+ void (*wrote_chunk) (SoupMessage *msg);
+ void (*wrote_body) (SoupMessage *msg);
+ void (*got_informational) (SoupMessage *msg);
+ void (*got_headers) (SoupMessage *msg);
+ void (*got_chunk) (SoupMessage *msg, SoupBuffer *chunk);
+ void (*got_body) (SoupMessage *msg);
+ void (*restarted) (SoupMessage *msg);
+ void (*finished) (SoupMessage *msg);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupMessageClass;
+
+GType soup_message_get_type (void);
+
+#define SOUP_MESSAGE_METHOD "method"
+#define SOUP_MESSAGE_URI "uri"
+#define SOUP_MESSAGE_HTTP_VERSION "http-version"
+#define SOUP_MESSAGE_FLAGS "flags"
+#define SOUP_MESSAGE_SERVER_SIDE "server-side"
+#define SOUP_MESSAGE_STATUS_CODE "status-code"
+#define SOUP_MESSAGE_REASON_PHRASE "reason-phrase"
+#define SOUP_MESSAGE_FIRST_PARTY "first-party"
+#define SOUP_MESSAGE_REQUEST_BODY "request-body"
+#define SOUP_MESSAGE_REQUEST_HEADERS "request-headers"
+#define SOUP_MESSAGE_RESPONSE_BODY "response-body"
+#define SOUP_MESSAGE_RESPONSE_HEADERS "response-headers"
+#define SOUP_MESSAGE_TLS_CERTIFICATE "tls-certificate"
+#define SOUP_MESSAGE_TLS_ERRORS "tls-errors"
+
+SoupMessage *soup_message_new (const char *method,
+ const char *uri_string);
+SoupMessage *soup_message_new_from_uri (const char *method,
+ SoupURI *uri);
+
+void soup_message_set_request (SoupMessage *msg,
+ const char *content_type,
+ SoupMemoryUse req_use,
+ const char *req_body,
+ gsize req_length);
+void soup_message_set_response (SoupMessage *msg,
+ const char *content_type,
+ SoupMemoryUse resp_use,
+ const char *resp_body,
+ gsize resp_length);
+
+typedef enum {
+ SOUP_HTTP_1_0 = 0, /*< nick=http-1-0 >*/
+ SOUP_HTTP_1_1 = 1 /*< nick=http-1-1 >*/
+} SoupHTTPVersion;
+
+void soup_message_set_http_version (SoupMessage *msg,
+ SoupHTTPVersion version);
+SoupHTTPVersion soup_message_get_http_version (SoupMessage *msg);
+
+gboolean soup_message_is_keepalive (SoupMessage *msg);
+
+SoupURI *soup_message_get_uri (SoupMessage *msg);
+void soup_message_set_uri (SoupMessage *msg,
+ SoupURI *uri);
+SoupAddress *soup_message_get_address (SoupMessage *msg);
+
+SoupURI *soup_message_get_first_party (SoupMessage *msg);
+void soup_message_set_first_party (SoupMessage *msg,
+ SoupURI *first_party);
+
+typedef enum {
+ SOUP_MESSAGE_NO_REDIRECT = (1 << 1),
+ SOUP_MESSAGE_CAN_REBUILD = (1 << 2),
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+ SOUP_MESSAGE_OVERWRITE_CHUNKS = (1 << 3),
+#endif
+ SOUP_MESSAGE_CONTENT_DECODED = (1 << 4),
+ SOUP_MESSAGE_CERTIFICATE_TRUSTED = (1 << 5)
+} SoupMessageFlags;
+
+void soup_message_set_flags (SoupMessage *msg,
+ SoupMessageFlags flags);
+
+SoupMessageFlags soup_message_get_flags (SoupMessage *msg);
+
+gboolean soup_message_get_https_status (SoupMessage *msg,
+ GTlsCertificate **certificate,
+ GTlsCertificateFlags *errors);
+
+
+/* Specialized signal handlers */
+guint soup_message_add_header_handler (SoupMessage *msg,
+ const char *signal,
+ const char *header,
+ GCallback callback,
+ gpointer user_data);
+
+guint soup_message_add_status_code_handler (
+ SoupMessage *msg,
+ const char *signal,
+ guint status_code,
+ GCallback callback,
+ gpointer user_data);
+
+/*
+ * Status Setting
+ */
+void soup_message_set_status (SoupMessage *msg,
+ guint status_code);
+
+void soup_message_set_status_full (SoupMessage *msg,
+ guint status_code,
+ const char *reason_phrase);
+
+/* I/O */
+typedef SoupBuffer * (*SoupChunkAllocator) (SoupMessage *msg,
+ gsize max_len,
+ gpointer user_data);
+
+void soup_message_set_chunk_allocator (SoupMessage *msg,
+ SoupChunkAllocator allocator,
+ gpointer user_data,
+ GDestroyNotify destroy_notify);
+
+void soup_message_disable_feature (SoupMessage *msg,
+ GType feature_type);
+
+void soup_message_wrote_informational (SoupMessage *msg);
+void soup_message_wrote_headers (SoupMessage *msg);
+void soup_message_wrote_chunk (SoupMessage *msg);
+void soup_message_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk);
+void soup_message_wrote_body (SoupMessage *msg);
+void soup_message_got_informational (SoupMessage *msg);
+void soup_message_got_headers (SoupMessage *msg);
+void soup_message_got_chunk (SoupMessage *msg, SoupBuffer *chunk);
+void soup_message_got_body (SoupMessage *msg);
+void soup_message_content_sniffed (SoupMessage *msg, const char *content_type, GHashTable *params);
+void soup_message_restarted (SoupMessage *msg);
+void soup_message_finished (SoupMessage *msg);
+
+G_END_DECLS
+
+#endif /*SOUP_MESSAGE_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-method.c: declarations of _SOUP_METHOD_* variables
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-method.h"
+
+/* Explicit assignment to NULL is to help the OS X linker not be
+ * stupid. #522957
+ */
+gpointer _SOUP_METHOD_CONNECT = NULL;
+gpointer _SOUP_METHOD_COPY = NULL;
+gpointer _SOUP_METHOD_DELETE = NULL;
+gpointer _SOUP_METHOD_GET = NULL;
+gpointer _SOUP_METHOD_HEAD = NULL;
+gpointer _SOUP_METHOD_LOCK = NULL;
+gpointer _SOUP_METHOD_MKCOL = NULL;
+gpointer _SOUP_METHOD_MOVE = NULL;
+gpointer _SOUP_METHOD_OPTIONS = NULL;
+gpointer _SOUP_METHOD_POST = NULL;
+gpointer _SOUP_METHOD_PROPFIND = NULL;
+gpointer _SOUP_METHOD_PROPPATCH = NULL;
+gpointer _SOUP_METHOD_PUT = NULL;
+gpointer _SOUP_METHOD_TRACE = NULL;
+gpointer _SOUP_METHOD_UNLOCK = NULL;
+
+/**
+ * SOUP_METHOD_OPTIONS:
+ *
+ * "OPTIONS" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_GET:
+ *
+ * "GET" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_HEAD:
+ *
+ * "HEAD" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_POST:
+ *
+ * "POST" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_PUT:
+ *
+ * "PUT" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_DELETE:
+ *
+ * "DELETE" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_TRACE:
+ *
+ * "TRACE" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_CONNECT:
+ *
+ * "CONNECT" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_PROPFIND:
+ *
+ * "PROPFIND" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_PROPPATCH:
+ *
+ * "PROPPATCH" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_MKCOL:
+ *
+ * "MKCOL" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_COPY:
+ *
+ * "COPY" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_MOVE:
+ *
+ * "MOVE" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_LOCK:
+ *
+ * "LOCK" as an interned string.
+ **/
+/**
+ * SOUP_METHOD_UNLOCK:
+ *
+ * "UNLOCK" as an interned string.
+ **/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_METHOD_H
+#define SOUP_METHOD_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-misc.h>
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:soup-method
+ * @short_description: HTTP method definitions
+ *
+ * soup-method.h contains a number of defines for standard HTTP and
+ * WebDAV headers. You do not need to use these defines; you can pass
+ * arbitrary strings to soup_message_new() if you prefer.
+ *
+ * The thing that these defines <emphasis>are</emphasis> useful for is
+ * performing quick comparisons against #SoupMessage's %method field;
+ * because that field always contains an interned string, and these
+ * macros return interned strings, you can compare %method directly
+ * against these macros rather than needing to use strcmp(). This is
+ * most useful in SoupServer handlers. Eg:
+ *
+ * <informalexample><programlisting>
+ * if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
+ * soup_message_set_status (msg, SOUP_METHOD_NOT_IMPLEMENTED);
+ * return;
+ * }
+ * </programlisting></informalexample>
+ **/
+
+#define _SOUP_INTERN_METHOD(method) (_SOUP_ATOMIC_INTERN_STRING (_SOUP_METHOD_##method, #method))
+
+/* HTTP/1.1 methods */
+#define SOUP_METHOD_OPTIONS _SOUP_INTERN_METHOD (OPTIONS)
+#define SOUP_METHOD_GET _SOUP_INTERN_METHOD (GET)
+#define SOUP_METHOD_HEAD _SOUP_INTERN_METHOD (HEAD)
+#define SOUP_METHOD_POST _SOUP_INTERN_METHOD (POST)
+#define SOUP_METHOD_PUT _SOUP_INTERN_METHOD (PUT)
+#define SOUP_METHOD_DELETE _SOUP_INTERN_METHOD (DELETE)
+#define SOUP_METHOD_TRACE _SOUP_INTERN_METHOD (TRACE)
+#define SOUP_METHOD_CONNECT _SOUP_INTERN_METHOD (CONNECT)
+
+/* WebDAV methods */
+#define SOUP_METHOD_PROPFIND _SOUP_INTERN_METHOD (PROPFIND)
+#define SOUP_METHOD_PROPPATCH _SOUP_INTERN_METHOD (PROPPATCH)
+#define SOUP_METHOD_MKCOL _SOUP_INTERN_METHOD (MKCOL)
+#define SOUP_METHOD_COPY _SOUP_INTERN_METHOD (COPY)
+#define SOUP_METHOD_MOVE _SOUP_INTERN_METHOD (MOVE)
+#define SOUP_METHOD_LOCK _SOUP_INTERN_METHOD (LOCK)
+#define SOUP_METHOD_UNLOCK _SOUP_INTERN_METHOD (UNLOCK)
+
+/* Do not use these variables directly; use the macros above, which
+ * ensure that they get initialized properly.
+ */
+extern gpointer _SOUP_METHOD_OPTIONS;
+extern gpointer _SOUP_METHOD_GET;
+extern gpointer _SOUP_METHOD_HEAD;
+extern gpointer _SOUP_METHOD_POST;
+extern gpointer _SOUP_METHOD_PUT;
+extern gpointer _SOUP_METHOD_DELETE;
+extern gpointer _SOUP_METHOD_TRACE;
+extern gpointer _SOUP_METHOD_CONNECT;
+
+extern gpointer _SOUP_METHOD_PROPFIND;
+extern gpointer _SOUP_METHOD_PROPPATCH;
+extern gpointer _SOUP_METHOD_MKCOL;
+extern gpointer _SOUP_METHOD_COPY;
+extern gpointer _SOUP_METHOD_MOVE;
+extern gpointer _SOUP_METHOD_LOCK;
+extern gpointer _SOUP_METHOD_UNLOCK;
+
+G_END_DECLS
+
+#endif /* SOUP_METHOD_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2011 Igalia, S.L.
+ * Copyright 2011 Red Hat, Inc.
+ */
+
+#ifndef SOUP_URI_PRIVATE_H
+#define SOUP_URI_PRIVATE_H 1
+
+#include "soup-socket.h"
+
+char *uri_decoded_copy (const char *str, int length);
+
+guint soup_socket_handshake_sync (SoupSocket *sock,
+ GCancellable *cancellable);
+void soup_socket_handshake_async (SoupSocket *sock,
+ GCancellable *cancellable,
+ SoupSocketCallback callback,
+ gpointer user_data);
+
+#endif /* SOUP_URI_PRIVATE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-misc.c: Miscellaneous functions
+
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include "soup-misc.h"
+
+/**
+ * SECTION:soup-misc
+ * @short_description: Miscellaneous functions
+ *
+ **/
+
+/**
+ * soup_str_case_hash:
+ * @key: ASCII string to hash
+ *
+ * Hashes @key in a case-insensitive manner.
+ *
+ * Return value: the hash code.
+ **/
+guint
+soup_str_case_hash (gconstpointer key)
+{
+ const char *p = key;
+ guint h = g_ascii_toupper(*p);
+
+ if (h)
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + g_ascii_toupper(*p);
+
+ return h;
+}
+
+/**
+ * soup_str_case_equal:
+ * @v1: an ASCII string
+ * @v2: another ASCII string
+ *
+ * Compares @v1 and @v2 in a case-insensitive manner
+ *
+ * Return value: %TRUE if they are equal (modulo case)
+ **/
+gboolean
+soup_str_case_equal (gconstpointer v1,
+ gconstpointer v2)
+{
+ const char *string1 = v1;
+ const char *string2 = v2;
+
+ return g_ascii_strcasecmp (string1, string2) == 0;
+}
+
+/**
+ * soup_add_io_watch: (skip)
+ * @async_context: (allow-none): the #GMainContext to dispatch the I/O
+ * watch in, or %NULL for the default context
+ * @chan: the #GIOChannel to watch
+ * @condition: the condition to watch for
+ * @function: the callback to invoke when @condition occurs
+ * @data: user data to pass to @function
+ *
+ * Adds an I/O watch as with g_io_add_watch(), but using the given
+ * @async_context.
+ *
+ * Return value: a #GSource, which can be removed from @async_context
+ * with g_source_destroy().
+ **/
+GSource *
+soup_add_io_watch (GMainContext *async_context,
+ GIOChannel *chan, GIOCondition condition,
+ GIOFunc function, gpointer data)
+{
+ GSource *watch = g_io_create_watch (chan, condition);
+ g_source_set_callback (watch, (GSourceFunc) function, data, NULL);
+ g_source_attach (watch, async_context);
+ g_source_unref (watch);
+ return watch;
+}
+
+/**
+ * soup_add_idle: (skip)
+ * @async_context: (allow-none): the #GMainContext to dispatch the I/O
+ * watch in, or %NULL for the default context
+ * @function: the callback to invoke at idle time
+ * @data: user data to pass to @function
+ *
+ * Adds an idle event as with g_idle_add(), but using the given
+ * @async_context.
+ *
+ * If you want @function to run "right away", use
+ * soup_add_completion(), since that sets a higher priority on the
+ * #GSource than soup_add_idle() does.
+ *
+ * Return value: a #GSource, which can be removed from @async_context
+ * with g_source_destroy().
+ **/
+GSource *
+soup_add_idle (GMainContext *async_context,
+ GSourceFunc function, gpointer data)
+{
+ GSource *source = g_idle_source_new ();
+ g_source_set_callback (source, function, data, NULL);
+ g_source_attach (source, async_context);
+ g_source_unref (source);
+ return source;
+}
+
+/**
+ * soup_add_completion: (skip)
+ * @async_context: (allow-none): the #GMainContext to dispatch the I/O
+ * watch in, or %NULL for the default context
+ * @function: the callback to invoke
+ * @data: user data to pass to @function
+ *
+ * Adds @function to be executed from inside @async_context with the
+ * default priority. Use this when you want to complete an action in
+ * @async_context's main loop, as soon as possible.
+ *
+ * Return value: a #GSource, which can be removed from @async_context
+ * with g_source_destroy().
+ *
+ * Since: 2.24
+ **/
+GSource *
+soup_add_completion (GMainContext *async_context,
+ GSourceFunc function, gpointer data)
+{
+ GSource *source = g_idle_source_new ();
+ g_source_set_priority (source, G_PRIORITY_DEFAULT);
+ g_source_set_callback (source, function, data, NULL);
+ g_source_attach (source, async_context);
+ g_source_unref (source);
+ return source;
+}
+
+/**
+ * soup_add_timeout: (skip)
+ * @async_context: (allow-none): the #GMainContext to dispatch the I/O
+ * watch in, or %NULL for the default context
+ * @interval: the timeout interval, in milliseconds
+ * @function: the callback to invoke at timeout time
+ * @data: user data to pass to @function
+ *
+ * Adds a timeout as with g_timeout_add(), but using the given
+ * @async_context.
+ *
+ * Return value: a #GSource, which can be removed from @async_context
+ * with g_source_destroy().
+ **/
+GSource *
+soup_add_timeout (GMainContext *async_context,
+ guint interval,
+ GSourceFunc function, gpointer data)
+{
+ GSource *source = g_timeout_source_new (interval);
+ g_source_set_callback (source, function, data, NULL);
+ g_source_attach (source, async_context);
+ g_source_unref (source);
+ return source;
+}
+
+/* 00 URI_UNRESERVED
+ * 01 URI_PCT_ENCODED
+ * 02 URI_GEN_DELIMS
+ * 04 URI_SUB_DELIMS
+ * 08 HTTP_SEPARATOR
+ * 10 HTTP_CTL
+ */
+const char soup_char_attributes[] = {
+ /* 0x00 - 0x07 */
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ /* 0x08 - 0x0f */
+ 0x11, 0x19, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ /* 0x10 - 0x17 */
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ /* 0x18 - 0x1f */
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ /* !"#$%&' */
+ 0x09, 0x04, 0x09, 0x02, 0x04, 0x01, 0x04, 0x04,
+ /* ()*+,-./ */
+ 0x0c, 0x0c, 0x04, 0x04, 0x0c, 0x00, 0x00, 0x0a,
+ /* 01234567 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 89:;<=>? */
+ 0x00, 0x00, 0x0a, 0x0c, 0x09, 0x0a, 0x09, 0x0a,
+ /* @ABCDEFG */
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* HIJKLMNO */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* PQRSTUVW */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* XYZ[\]^_ */
+ 0x00, 0x00, 0x00, 0x0a, 0x09, 0x0a, 0x01, 0x00,
+ /* `abcdefg */
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* hijklmno */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* pqrstuvw */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* xyz{|}~ */
+ 0x00, 0x00, 0x00, 0x09, 0x01, 0x09, 0x00, 0x11,
+ /* 0x80 - 0xFF */
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_MISC_H
+#define SOUP_MISC_H 1
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* Non-default-GMainContext operations */
+GSource *soup_add_io_watch (GMainContext *async_context,
+ GIOChannel *chan,
+ GIOCondition condition,
+ GIOFunc function,
+ gpointer data);
+GSource *soup_add_idle (GMainContext *async_context,
+ GSourceFunc function,
+ gpointer data);
+GSource *soup_add_completion (GMainContext *async_context,
+ GSourceFunc function,
+ gpointer data);
+GSource *soup_add_timeout (GMainContext *async_context,
+ guint interval,
+ GSourceFunc function,
+ gpointer data);
+
+/* Misc utils */
+
+guint soup_str_case_hash (gconstpointer key);
+gboolean soup_str_case_equal (gconstpointer v1,
+ gconstpointer v2);
+
+#define _SOUP_ATOMIC_INTERN_STRING(variable, value) ((const char *)(g_atomic_pointer_get (&(variable)) ? (variable) : (g_atomic_pointer_set (&(variable), (gpointer)g_intern_static_string (value)), (variable))))
+
+/* character classes */
+
+extern const char soup_char_attributes[];
+#define SOUP_CHAR_URI_PERCENT_ENCODED 0x01
+#define SOUP_CHAR_URI_GEN_DELIMS 0x02
+#define SOUP_CHAR_URI_SUB_DELIMS 0x04
+#define SOUP_CHAR_HTTP_SEPARATOR 0x08
+#define SOUP_CHAR_HTTP_CTL 0x10
+
+#define soup_char_is_uri_percent_encoded(ch) (soup_char_attributes[(guchar)ch] & SOUP_CHAR_URI_PERCENT_ENCODED)
+#define soup_char_is_uri_gen_delims(ch) (soup_char_attributes[(guchar)ch] & SOUP_CHAR_URI_GEN_DELIMS)
+#define soup_char_is_uri_sub_delims(ch) (soup_char_attributes[(guchar)ch] & SOUP_CHAR_URI_SUB_DELIMS)
+#define soup_char_is_uri_unreserved(ch) (!(soup_char_attributes[(guchar)ch] & (SOUP_CHAR_URI_PERCENT_ENCODED | SOUP_CHAR_URI_GEN_DELIMS | SOUP_CHAR_URI_SUB_DELIMS)))
+#define soup_char_is_token(ch) (!(soup_char_attributes[(guchar)ch] & (SOUP_CHAR_HTTP_SEPARATOR | SOUP_CHAR_HTTP_CTL)))
+
+/* SSL stuff */
+
+extern const gboolean soup_ssl_supported;
+
+#define SOUP_SSL_ERROR soup_ssl_error_quark()
+
+GQuark soup_ssl_error_quark (void);
+
+typedef enum {
+ SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ,
+ SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE,
+ SOUP_SSL_ERROR_CERTIFICATE,
+ SOUP_SSL_ERROR_HANDSHAKE_FAILED
+} SoupSSLError;
+
+typedef enum {
+ SOUP_CONNECTION_NEW,
+ SOUP_CONNECTION_CONNECTING,
+ SOUP_CONNECTION_IDLE,
+ SOUP_CONNECTION_IN_USE,
+ SOUP_CONNECTION_REMOTE_DISCONNECTED,
+ SOUP_CONNECTION_DISCONNECTED
+} SoupConnectionState;
+
+G_END_DECLS
+
+#endif /* SOUP_MISC_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-multipart.c: multipart HTTP message bodies
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "soup-multipart.h"
+#include "soup-headers.h"
+
+/**
+ * SECTION:soup-multipart
+ * @short_description: multipart HTTP message bodies
+ * @see_also: #SoupMessageBody, #SoupMessageHeaders
+ *
+ **/
+
+/**
+ * SoupMultipart:
+ *
+ * Represents a multipart HTTP message body, parsed according to the
+ * syntax of RFC 2046. Of particular interest to HTTP are
+ * <literal>multipart/byte-ranges</literal> and
+ * <literal>multipart/form-data</literal>.
+ *
+ * Although the headers of a #SoupMultipart body part will contain the
+ * full headers from that body part, libsoup does not interpret them
+ * according to MIME rules. For example, each body part is assumed to
+ * have "binary" Content-Transfer-Encoding, even if its headers
+ * explicitly state otherwise. In other words, don't try to use
+ * #SoupMultipart for handling real MIME multiparts.
+ *
+ * Since: 2.26
+ **/
+
+struct SoupMultipart {
+ char *mime_type, *boundary;
+ GPtrArray *headers, *bodies;
+};
+
+static SoupMultipart *
+soup_multipart_new_internal (char *mime_type, char *boundary)
+{
+ SoupMultipart *multipart;
+
+ multipart = g_slice_new (SoupMultipart);
+ multipart->mime_type = mime_type;
+ multipart->boundary = boundary;
+ multipart->headers = g_ptr_array_new ();
+ multipart->bodies = g_ptr_array_new ();
+
+ return multipart;
+}
+
+static char *
+generate_boundary (void)
+{
+ static int counter;
+ struct {
+ GTimeVal timeval;
+ int counter;
+ } data;
+
+ /* avoid valgrind warning */
+ if (sizeof (data) != sizeof (data.timeval) + sizeof (data.counter))
+ memset (&data, 0, sizeof (data));
+
+ g_get_current_time (&data.timeval);
+ data.counter = counter++;
+
+ /* The maximum boundary string length is 69 characters, and a
+ * stringified SHA256 checksum is 64 bytes long.
+ */
+ return g_compute_checksum_for_data (G_CHECKSUM_SHA256,
+ (const guchar *)&data,
+ sizeof (data));
+}
+
+/**
+ * soup_multipart_new:
+ * @mime_type: the MIME type of the multipart to create.
+ *
+ * Creates a new empty #SoupMultipart with a randomly-generated
+ * boundary string. Note that @mime_type must be the full MIME type,
+ * including "multipart/".
+ *
+ * Return value: a new empty #SoupMultipart of the given @mime_type
+ *
+ * Since: 2.26
+ **/
+SoupMultipart *
+soup_multipart_new (const char *mime_type)
+{
+ return soup_multipart_new_internal (g_strdup (mime_type),
+ generate_boundary ());
+}
+
+static const char *
+find_boundary (const char *start, const char *end,
+ const char *boundary, int boundary_len)
+{
+ const char *b;
+
+ for (b = memchr (start, '-', end - start);
+ b && b + boundary_len + 4 < end;
+ b = memchr (b + 2, '-', end - (b + 2))) {
+ /* Check for "--boundary" */
+ if (b[1] != '-' ||
+ memcmp (b + 2, boundary, boundary_len) != 0)
+ continue;
+
+ /* Check that it's at start of line */
+ if (!(b == start || (b[-1] == '\n' && b[-2] == '\r')))
+ continue;
+
+ /* Check for "--" or "\r\n" after boundary */
+ if ((b[boundary_len + 2] == '-' && b[boundary_len + 3] == '-') ||
+ (b[boundary_len + 2] == '\r' && b[boundary_len + 3] == '\n'))
+ return b;
+ }
+ return NULL;
+}
+
+/**
+ * soup_multipart_new_from_message:
+ * @headers: the headers of the HTTP message to parse
+ * @body: the body of the HTTP message to parse
+ *
+ * Parses @headers and @body to form a new #SoupMultipart
+ *
+ * Return value: a new #SoupMultipart (or %NULL if the message couldn't
+ * be parsed or wasn't multipart).
+ *
+ * Since: 2.26
+ **/
+SoupMultipart *
+soup_multipart_new_from_message (SoupMessageHeaders *headers,
+ SoupMessageBody *body)
+{
+ SoupMultipart *multipart;
+ const char *content_type, *boundary;
+ GHashTable *params;
+ int boundary_len;
+ SoupBuffer *flattened;
+ const char *start, *split, *end, *body_end;
+ SoupMessageHeaders *part_headers;
+ SoupBuffer *part_body;
+
+ content_type = soup_message_headers_get_content_type (headers, ¶ms);
+ if (!content_type)
+ return NULL;
+
+ boundary = g_hash_table_lookup (params, "boundary");
+ if (strncmp (content_type, "multipart/", 10) != 0 || !boundary) {
+ g_hash_table_destroy (params);
+ return NULL;
+ }
+
+ multipart = soup_multipart_new_internal (
+ g_strdup (content_type), g_strdup (boundary));
+ g_hash_table_destroy (params);
+
+ flattened = soup_message_body_flatten (body);
+ body_end = flattened->data + flattened->length;
+ boundary = multipart->boundary;
+ boundary_len = strlen (boundary);
+
+ /* skip preamble */
+ start = find_boundary (flattened->data, body_end,
+ boundary, boundary_len);
+ if (!start) {
+ soup_multipart_free (multipart);
+ soup_buffer_free (flattened);
+ return NULL;
+ }
+
+ while (start[2 + boundary_len] != '-') {
+ end = find_boundary (start + 2 + boundary_len, body_end,
+ boundary, boundary_len);
+ if (!end) {
+ soup_multipart_free (multipart);
+ soup_buffer_free (flattened);
+ return NULL;
+ }
+
+ split = strstr (start, "\r\n\r\n");
+ if (!split || split > end) {
+ soup_multipart_free (multipart);
+ soup_buffer_free (flattened);
+ return NULL;
+ }
+ split += 4;
+
+ /* @start points to the start of the boundary line
+ * preceding this part, and @split points to the end
+ * of the headers / start of the body.
+ *
+ * We tell soup_headers_parse() to start parsing at
+ * @start, because it skips the first line of the
+ * input anyway (expecting it to be either a
+ * Request-Line or Status-Line).
+ */
+ part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ g_ptr_array_add (multipart->headers, part_headers);
+ if (!soup_headers_parse (start, split - 2 - start,
+ part_headers)) {
+ soup_multipart_free (multipart);
+ soup_buffer_free (flattened);
+ return NULL;
+ }
+
+ /* @split, as previously mentioned, points to the
+ * start of the body, and @end points to the start of
+ * the following boundary line, which is to say 2 bytes
+ * after the end of the body.
+ */
+ part_body = soup_buffer_new_subbuffer (flattened,
+ split - flattened->data,
+ end - 2 - split);
+ g_ptr_array_add (multipart->bodies, part_body);
+
+ start = end;
+ }
+
+ soup_buffer_free (flattened);
+ return multipart;
+}
+
+/**
+ * soup_multipart_get_length:
+ * @multipart: a #SoupMultipart
+ *
+ * Gets the number of body parts in @multipart
+ *
+ * Return value: the number of body parts in @multipart
+ *
+ * Since: 2.26
+ **/
+int
+soup_multipart_get_length (SoupMultipart *multipart)
+{
+ return multipart->bodies->len;
+}
+
+/**
+ * soup_multipart_get_part:
+ * @multipart: a #SoupMultipart
+ * @part: the part number to get (counting from 0)
+ * @headers: (out) (transfer none): return location for the MIME part
+ * headers
+ * @body: (out) (transfer none): return location for the MIME part
+ * body
+ *
+ * Gets the indicated body part from @multipart.
+ *
+ * Return value: %TRUE on success, %FALSE if @part is out of range (in
+ * which case @headers and @body won't be set)
+ *
+ * Since: 2.26
+ **/
+gboolean
+soup_multipart_get_part (SoupMultipart *multipart, int part,
+ SoupMessageHeaders **headers, SoupBuffer **body)
+{
+ if (part < 0 || part >= multipart->bodies->len)
+ return FALSE;
+ *headers = multipart->headers->pdata[part];
+ *body = multipart->bodies->pdata[part];
+ return TRUE;
+}
+
+/**
+ * soup_multipart_append_part:
+ * @multipart: a #SoupMultipart
+ * @headers: the MIME part headers
+ * @body: the MIME part body
+ *
+ * Adds a new MIME part to @multipart with the given headers and body.
+ * (The multipart will make its own copies of @headers and @body, so
+ * you should free your copies if you are not using them for anything
+ * else.)
+ *
+ * Since: 2.26
+ **/
+void
+soup_multipart_append_part (SoupMultipart *multipart,
+ SoupMessageHeaders *headers,
+ SoupBuffer *body)
+{
+ SoupMessageHeaders *headers_copy;
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+
+ /* Copying @headers is annoying, but the alternatives seem
+ * worse:
+ *
+ * 1) We don't want to use g_boxed_copy, because
+ * SoupMessageHeaders actually implements that as just a
+ * ref, which would be confusing since SoupMessageHeaders
+ * is mutable and the caller might modify @headers after
+ * appending it.
+ *
+ * 2) We can't change SoupMessageHeaders to not just do a ref
+ * from g_boxed_copy, because that would break language
+ * bindings (which need to be able to hold a ref on
+ * msg->request_headers, but don't want to duplicate it).
+ *
+ * 3) We don't want to steal the reference to @headers,
+ * because then we'd have to either also steal the
+ * reference to @body (which would be inconsistent with
+ * other SoupBuffer methods), or NOT steal the reference to
+ * @body, in which case there'd be inconsistency just
+ * between the two arguments of this method!
+ */
+ headers_copy = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ soup_message_headers_iter_init (&iter, headers);
+ while (soup_message_headers_iter_next (&iter, &name, &value))
+ soup_message_headers_append (headers_copy, name, value);
+
+ g_ptr_array_add (multipart->headers, headers_copy);
+ g_ptr_array_add (multipart->bodies, soup_buffer_copy (body));
+}
+
+/**
+ * soup_multipart_append_form_string:
+ * @multipart: a multipart (presumably of type "multipart/form-data")
+ * @control_name: the name of the control associated with @data
+ * @data: the body data
+ *
+ * Adds a new MIME part containing @data to @multipart, using
+ * "Content-Disposition: form-data", as per the HTML forms
+ * specification. See soup_form_request_new_from_multipart() for more
+ * details.
+ *
+ * Since: 2.26
+ **/
+void
+soup_multipart_append_form_string (SoupMultipart *multipart,
+ const char *control_name, const char *data)
+{
+ SoupBuffer *body;
+
+ body = soup_buffer_new (SOUP_MEMORY_COPY, data, strlen (data));
+ soup_multipart_append_form_file (multipart, control_name,
+ NULL, NULL, body);
+ soup_buffer_free (body);
+}
+
+/**
+ * soup_multipart_append_form_file:
+ * @multipart: a multipart (presumably of type "multipart/form-data")
+ * @control_name: the name of the control associated with this file
+ * @filename: the name of the file, or %NULL if not known
+ * @content_type: the MIME type of the file, or %NULL if not known
+ * @body: the file data
+ *
+ * Adds a new MIME part containing @body to @multipart, using
+ * "Content-Disposition: form-data", as per the HTML forms
+ * specification. See soup_form_request_new_from_multipart() for more
+ * details.
+ *
+ * Since: 2.26
+ **/
+void
+soup_multipart_append_form_file (SoupMultipart *multipart,
+ const char *control_name, const char *filename,
+ const char *content_type, SoupBuffer *body)
+{
+ SoupMessageHeaders *headers;
+ GString *disposition;
+
+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ disposition = g_string_new ("form-data; ");
+ soup_header_g_string_append_param_quoted (disposition, "name", control_name);
+ if (filename) {
+ g_string_append (disposition, "; ");
+ soup_header_g_string_append_param_quoted (disposition, "filename", filename);
+ }
+ soup_message_headers_append (headers, "Content-Disposition",
+ disposition->str);
+ g_string_free (disposition, TRUE);
+
+ if (content_type) {
+ soup_message_headers_append (headers, "Content-Type",
+ content_type);
+ }
+
+ g_ptr_array_add (multipart->headers, headers);
+ g_ptr_array_add (multipart->bodies, soup_buffer_copy (body));
+}
+
+/**
+ * soup_multipart_to_message:
+ * @multipart: a #SoupMultipart
+ * @dest_headers: the headers of the HTTP message to serialize @multipart to
+ * @dest_body: the body of the HTTP message to serialize @multipart to
+ *
+ * Serializes @multipart to @dest_headers and @dest_body.
+ *
+ * Since: 2.26
+ **/
+void
+soup_multipart_to_message (SoupMultipart *multipart,
+ SoupMessageHeaders *dest_headers,
+ SoupMessageBody *dest_body)
+{
+ SoupMessageHeaders *part_headers;
+ SoupBuffer *part_body;
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+ GString *str;
+ GHashTable *params;
+ int i;
+
+ params = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (params, "boundary", multipart->boundary);
+ soup_message_headers_set_content_type (dest_headers,
+ multipart->mime_type,
+ params);
+ g_hash_table_destroy (params);
+
+ for (i = 0; i < multipart->bodies->len; i++) {
+ part_headers = multipart->headers->pdata[i];
+ part_body = multipart->bodies->pdata[i];
+
+ str = g_string_new (i == 0 ? NULL : "\r\n");
+ g_string_append (str, "--");
+ g_string_append (str, multipart->boundary);
+ g_string_append (str, "\r\n");
+ soup_message_headers_iter_init (&iter, part_headers);
+ while (soup_message_headers_iter_next (&iter, &name, &value))
+ g_string_append_printf (str, "%s: %s\r\n", name, value);
+ g_string_append (str, "\r\n");
+ soup_message_body_append (dest_body, SOUP_MEMORY_TAKE,
+ str->str, str->len);
+ g_string_free (str, FALSE);
+
+ soup_message_body_append_buffer (dest_body, part_body);
+ }
+
+ str = g_string_new ("\r\n--");
+ g_string_append (str, multipart->boundary);
+ g_string_append (str, "--\r\n");
+ soup_message_body_append (dest_body, SOUP_MEMORY_TAKE,
+ str->str, str->len);
+ g_string_free (str, FALSE);
+
+ /* (The "\r\n" after the close-delimiter seems wrong according
+ * to my reading of RFCs 2046 and 2616, but that's what
+ * everyone else does.)
+ */
+}
+
+/**
+ * soup_multipart_free:
+ * @multipart: a #SoupMultipart
+ *
+ * Frees @multipart
+ *
+ * Since: 2.26
+ **/
+void
+soup_multipart_free (SoupMultipart *multipart)
+{
+ int i;
+
+ g_free (multipart->mime_type);
+ g_free (multipart->boundary);
+ for (i = 0; i < multipart->headers->len; i++)
+ soup_message_headers_free (multipart->headers->pdata[i]);
+ g_ptr_array_free (multipart->headers, TRUE);
+ for (i = 0; i < multipart->bodies->len; i++)
+ soup_buffer_free (multipart->bodies->pdata[i]);
+ g_ptr_array_free (multipart->bodies, TRUE);
+
+ g_slice_free (SoupMultipart, multipart);
+}
+
+static SoupMultipart *
+soup_multipart_copy (SoupMultipart *multipart)
+{
+ SoupMultipart *copy;
+ int i;
+
+ copy = soup_multipart_new_internal (g_strdup (multipart->mime_type),
+ g_strdup (multipart->boundary));
+ for (i = 0; i < multipart->bodies->len; i++) {
+ soup_multipart_append_part (copy,
+ multipart->headers->pdata[i],
+ multipart->bodies->pdata[i]);
+ }
+ return copy;
+}
+
+GType
+soup_multipart_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupMultipart"),
+ (GBoxedCopyFunc) soup_multipart_copy,
+ (GBoxedFreeFunc) soup_multipart_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_MULTIPART_H
+#define SOUP_MULTIPART_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+#include <libsoup/soup-message-headers.h>
+
+G_BEGIN_DECLS
+
+typedef struct SoupMultipart SoupMultipart;
+
+GType soup_multipart_get_type (void);
+#define SOUP_TYPE_MULTIPART (soup_multipart_get_type ())
+
+SoupMultipart *soup_multipart_new (const char *mime_type);
+SoupMultipart *soup_multipart_new_from_message (SoupMessageHeaders *headers,
+ SoupMessageBody *body);
+
+int soup_multipart_get_length (SoupMultipart *multipart);
+gboolean soup_multipart_get_part (SoupMultipart *multipart,
+ int part,
+ SoupMessageHeaders **headers,
+ SoupBuffer **body);
+
+void soup_multipart_append_part (SoupMultipart *multipart,
+ SoupMessageHeaders *headers,
+ SoupBuffer *body);
+
+void soup_multipart_append_form_string (SoupMultipart *multipart,
+ const char *control_name,
+ const char *data);
+void soup_multipart_append_form_file (SoupMultipart *multipart,
+ const char *control_name,
+ const char *filename,
+ const char *content_type,
+ SoupBuffer *body);
+
+void soup_multipart_to_message (SoupMultipart *multipart,
+ SoupMessageHeaders *dest_headers,
+ SoupMessageBody *dest_body);
+
+void soup_multipart_free (SoupMultipart *multipart);
+
+G_END_DECLS
+
+#endif /* SOUP_MULTIPART_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-password-manager-gnome.c: GNOME-keyring-based password manager
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+
+#include "soup-password-manager-gnome.h"
+#include "soup-auth.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+#include <gnome-keyring.h>
+
+static void soup_password_manager_gnome_interface_init (SoupPasswordManagerInterface *password_manager_interface);
+
+G_DEFINE_TYPE_EXTENDED (SoupPasswordManagerGNOME, soup_password_manager_gnome, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, NULL)
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_PASSWORD_MANAGER, soup_password_manager_gnome_interface_init))
+
+static void get_passwords_async (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data);
+static void get_passwords_sync (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ GCancellable *cancellable);
+
+static void
+soup_password_manager_gnome_init (SoupPasswordManagerGNOME *manager_gnome)
+{
+}
+
+static void
+soup_password_manager_gnome_class_init (SoupPasswordManagerGNOMEClass *gnome_class)
+{
+}
+
+static void
+soup_password_manager_gnome_interface_init (SoupPasswordManagerInterface *password_manager_interface)
+{
+ password_manager_interface->get_passwords_async = get_passwords_async;
+ password_manager_interface->get_passwords_sync = get_passwords_sync;
+}
+
+
+static void
+save_password_callback (GnomeKeyringResult result, guint32 val, gpointer data)
+{
+}
+
+static void
+async_save_password (SoupAuth *auth, const char *username,
+ const char *password, gpointer user_data)
+{
+ SoupURI *uri = user_data;
+
+ gnome_keyring_set_network_password (
+ NULL, /* use default keyring */
+ username,
+ soup_auth_get_realm (auth),
+ uri->host,
+ NULL,
+ uri->scheme,
+ soup_auth_get_scheme_name (auth),
+ uri->port,
+ password,
+ save_password_callback, NULL, NULL);
+}
+
+static void
+sync_save_password (SoupAuth *auth, const char *username,
+ const char *password, gpointer user_data)
+{
+ SoupURI *uri = user_data;
+ guint32 item_id;
+
+ gnome_keyring_set_network_password_sync (
+ NULL, /* use default keyring */
+ username,
+ soup_auth_get_realm (auth),
+ uri->host,
+ NULL,
+ uri->scheme,
+ soup_auth_get_scheme_name (auth),
+ uri->port,
+ password,
+ &item_id);
+}
+
+static void
+update_auth_for_passwords (SoupAuth *auth, SoupMessage *msg,
+ GList *passwords, gboolean async)
+{
+ GnomeKeyringNetworkPasswordData *pdata;
+ SoupURI *uri;
+
+ while (passwords) {
+ pdata = passwords->data;
+ soup_auth_has_saved_password (auth, pdata->user,
+ pdata->password);
+ passwords = passwords->next;
+ }
+
+ uri = g_object_get_data (G_OBJECT (auth),
+ "SoupPasswordManagerGNOME-save_password-uri");
+ if (uri) {
+ g_signal_handlers_disconnect_by_func (auth, async_save_password, uri);
+ g_signal_handlers_disconnect_by_func (auth, sync_save_password, uri);
+ }
+
+ uri = soup_uri_copy (soup_message_get_uri (msg));
+ g_signal_connect (auth, "save_password",
+ G_CALLBACK (async ? async_save_password : sync_save_password),
+ uri);
+ g_object_set_data_full (G_OBJECT (auth),
+ "SoupPasswordManagerGNOME-save_password-uri",
+ uri, (GDestroyNotify)soup_uri_free);
+}
+
+typedef struct {
+ SoupPasswordManager *password_manager;
+ SoupMessage *msg;
+ SoupAuth *auth;
+ gboolean retrying;
+
+ SoupPasswordManagerCallback callback;
+ gpointer user_data;
+
+ gpointer request;
+} SoupPasswordManagerGNOMEAuthData;
+
+static void
+find_password_callback (GnomeKeyringResult result, GList *list,
+ gpointer user_data)
+{
+ SoupPasswordManagerGNOMEAuthData *auth_data = user_data;
+
+ /* FIXME: check result? */
+
+ update_auth_for_passwords (auth_data->auth, auth_data->msg, list, TRUE);
+ auth_data->callback (auth_data->password_manager,
+ auth_data->msg, auth_data->auth,
+ auth_data->retrying, auth_data->user_data);
+
+ /* gnome-keyring will call free_auth_data to clean up for us. */
+}
+
+static void
+free_auth_data (gpointer data)
+{
+ SoupPasswordManagerGNOMEAuthData *auth_data = data;
+
+ g_object_unref (auth_data->auth);
+ g_object_unref (auth_data->msg);
+ g_slice_free (SoupPasswordManagerGNOMEAuthData, auth_data);
+}
+
+static void
+get_passwords_async (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data)
+{
+ SoupPasswordManagerGNOMEAuthData *auth_data;
+ SoupURI *uri = soup_message_get_uri (msg);
+
+ auth_data = g_slice_new (SoupPasswordManagerGNOMEAuthData);
+ auth_data->password_manager = password_manager;
+ auth_data->msg = g_object_ref (msg);
+ auth_data->auth = g_object_ref (auth);
+ auth_data->retrying = retrying;
+
+ /* FIXME: async_context, cancellable */
+
+ auth_data->callback = callback;
+ auth_data->user_data = user_data;
+
+ /* FIXME: should we be specifying protocol and port here, or
+ * leaving them NULL/0 and filtering results in the callback?
+ * We don't want to send https passwords to http, but the
+ * reverse might be OK (if that's how other clients tend to
+ * behave).
+ */
+ auth_data->request = gnome_keyring_find_network_password (
+ NULL, /* user -- accept any */
+ soup_auth_get_realm (auth), /* domain */
+ uri->host, /* server */
+ NULL, /* object -- unused */
+ uri->scheme, /* protocol */
+ soup_auth_get_scheme_name (auth), /* authtype */
+ uri->port, /* port */
+ find_password_callback, auth_data, free_auth_data);
+}
+
+static void
+get_passwords_sync (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ GCancellable *cancellable)
+{
+ SoupURI *uri = soup_message_get_uri (msg);
+ GList *results = NULL;
+
+ /* FIXME: cancellable */
+
+ gnome_keyring_find_network_password_sync (
+ NULL, /* user -- accept any */
+ soup_auth_get_realm (auth), /* domain */
+ uri->host, /* server */
+ NULL, /* object -- unused */
+ uri->scheme, /* protocol */
+ soup_auth_get_scheme_name (auth), /* authtype */
+ uri->port, /* port */
+ &results);
+
+ update_auth_for_passwords (auth, msg, results, FALSE);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PASSWORD_MANAGER_GNOME_H
+#define SOUP_PASSWORD_MANAGER_GNOME_H 1
+
+#include "soup-password-manager.h"
+#include "soup-gnome-features.h"
+
+#define SOUP_PASSWORD_MANAGER_GNOME(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PASSWORD_MANAGER_GNOME, SoupPasswordManagerGNOME))
+#define SOUP_PASSWORD_MANAGER_GNOME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PASSWORD_MANAGER_GNOME, SoupPasswordManagerGNOMEClass))
+#define SOUP_IS_PASSWORD_MANAGER_GNOME(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PASSWORD_MANAGER_GNOME))
+#define SOUP_IS_PASSWORD_MANAGER_GNOME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PASSWORD_MANAGER_GNOME))
+#define SOUP_PASSWORD_MANAGER_GNOME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_PASSWORD_MANAGER_GNOME, SoupPasswordManagerGNOMEClass))
+
+typedef struct {
+ GObject parent;
+
+} SoupPasswordManagerGNOME;
+
+typedef struct {
+ GObjectClass parent_class;
+
+} SoupPasswordManagerGNOMEClass;
+
+#endif /* SOUP_PASSWORD_MANAGER_GNOME_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-password-manager.c: HTTP auth password manager interface
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+
+#include "soup-password-manager.h"
+#include "soup-session-feature.h"
+
+GType
+soup_password_manager_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ GType g_define_type_id =
+ g_type_register_static_simple (G_TYPE_INTERFACE,
+ g_intern_static_string ("SoupPasswordManager"),
+ sizeof (SoupPasswordManagerInterface),
+ (GClassInitFunc)NULL,
+ 0,
+ (GInstanceInitFunc)NULL,
+ (GTypeFlags) 0);
+ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (g_define_type_id, SOUP_TYPE_SESSION_FEATURE);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+/**
+ * soup_password_manager_get_passwords_async:
+ * @password_manager: the #SoupPasswordManager
+ * @msg: the #SoupMessage being authenticated
+ * @auth: the #SoupAuth being authenticated
+ * @retrying: whether or not this is a re-attempt to authenticate
+ * @async_context: (allow-none): the #GMainContext to invoke @callback in
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to invoke after fetching passwords
+ * @user_data: data for @callback
+ *
+ * Asynchronously attempts to look up saved passwords for @auth/@msg
+ * and then calls @callback after updating @auth with the information.
+ * Also registers @auth with @password_manager so that if the caller
+ * calls soup_auth_save_password() on it, the password will be saved.
+ *
+ * #SoupPasswordManager does not actually use the @retrying flag itself;
+ * it just passes its value on to @callback.
+ *
+ * If @cancellable is cancelled, @callback will still be invoked.
+ *
+ * Since: 2.28
+ **/
+void
+soup_password_manager_get_passwords_async (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data)
+{
+ SOUP_PASSWORD_MANAGER_GET_CLASS (password_manager)->
+ get_passwords_async (password_manager, msg, auth, retrying,
+ async_context, cancellable,
+ callback, user_data);
+}
+
+/**
+ * soup_password_manager_get_passwords_sync:
+ * @password_manager: the #SoupPasswordManager
+ * @msg: the #SoupMessage being authenticated
+ * @auth: the #SoupAuth being authenticated
+ * @cancellable: a #GCancellable, or %NULL
+ *
+ * Synchronously attempts to look up saved passwords for @auth/@msg
+ * and updates @auth with the information. Also registers @auth with
+ * @password_manager so that if the caller calls
+ * soup_auth_save_password() on it, the password will be saved.
+ *
+ * Since: 2.28
+ **/
+void
+soup_password_manager_get_passwords_sync (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ GCancellable *cancellable)
+{
+ SOUP_PASSWORD_MANAGER_GET_CLASS (password_manager)->
+ get_passwords_sync (password_manager, msg, auth, cancellable);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PASSWORD_MANAGER_H
+#define SOUP_PASSWORD_MANAGER_H 1
+
+#ifdef LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+
+#include <libsoup/soup-types.h>
+
+#define SOUP_TYPE_PASSWORD_MANAGER (soup_password_manager_get_type ())
+#define SOUP_PASSWORD_MANAGER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PASSWORD_MANAGER, SoupPasswordManager))
+#define SOUP_PASSWORD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PASSWORD_MANAGER, SoupPasswordManagerInterface))
+#define SOUP_IS_PASSWORD_MANAGER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PASSWORD_MANAGER))
+#define SOUP_IS_PASSWORD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PASSWORD_MANAGER))
+#define SOUP_PASSWORD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SOUP_TYPE_PASSWORD_MANAGER, SoupPasswordManagerInterface))
+
+typedef struct _SoupPasswordManager SoupPasswordManager;
+
+typedef void (*SoupPasswordManagerCallback) (SoupPasswordManager *,
+ SoupMessage *, SoupAuth *,
+ gboolean retrying,
+ gpointer user_data);
+
+typedef struct {
+ GTypeInterface base;
+
+ /* virtual methods */
+ void (*get_passwords_async) (SoupPasswordManager *, SoupMessage *,
+ SoupAuth *, gboolean,
+ GMainContext *, GCancellable *,
+ SoupPasswordManagerCallback, gpointer);
+ void (*get_passwords_sync) (SoupPasswordManager *, SoupMessage *,
+ SoupAuth *, GCancellable *);
+
+} SoupPasswordManagerInterface;
+
+GType soup_password_manager_get_type (void);
+
+void soup_password_manager_get_passwords_async (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data);
+
+void soup_password_manager_get_passwords_sync (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ GCancellable *cancellable);
+
+#endif /* LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY */
+
+#endif /* SOUP_PASSWORD_MANAGER_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-path-map.c: URI path prefix-matcher
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#include "soup-path-map.h"
+#include <string.h>
+
+/* This could be replaced with something more clever, like a Patricia
+ * trie, but it's probably not worth it since the total number of
+ * mappings is likely to always be small. So we keep an array of
+ * paths, sorted by decreasing length. (The first prefix match will
+ * therefore be the longest.)
+ */
+
+typedef struct {
+ char *path;
+ int len;
+ gpointer data;
+} SoupPathMapping;
+
+struct SoupPathMap {
+ GArray *mappings;
+ GDestroyNotify free_func;
+};
+
+/**
+ * soup_path_map_new:
+ * @data_free_func: function to use to free data added with
+ * soup_path_map_add().
+ *
+ * Creates a new %SoupPathMap.
+ *
+ * Return value: the new %SoupPathMap
+ **/
+SoupPathMap *
+soup_path_map_new (GDestroyNotify data_free_func)
+{
+ SoupPathMap *map;
+
+ map = g_slice_new0 (SoupPathMap);
+ map->mappings = g_array_new (FALSE, FALSE, sizeof (SoupPathMapping));
+ map->free_func = data_free_func;
+
+ return map;
+}
+
+/**
+ * soup_path_map_free:
+ * @map: a %SoupPathMap
+ *
+ * Frees @map and all data stored in it.
+ **/
+void
+soup_path_map_free (SoupPathMap *map)
+{
+ SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+ int i;
+
+ for (i = 0; i < map->mappings->len; i++) {
+ g_free (mappings[i].path);
+ if (map->free_func)
+ map->free_func (mappings[i].data);
+ }
+ g_array_free (map->mappings, TRUE);
+
+ g_slice_free (SoupPathMap, map);
+}
+
+/* Scan @map looking for @path or one of its ancestors.
+ * Sets *@match to the index of a match, or -1 if no match is found.
+ * Sets *@insert to the index to insert @path at if a new mapping is
+ * desired. Returns %TRUE if *@match is an exact match.
+ */
+static gboolean
+mapping_lookup (SoupPathMap *map, const char *path, int *match, int *insert)
+{
+ SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+ int i, path_len;
+ gboolean exact = FALSE;
+
+ *match = -1;
+
+ path_len = strcspn (path, "?");
+ for (i = 0; i < map->mappings->len; i++) {
+ if (mappings[i].len > path_len)
+ continue;
+
+ if (insert && mappings[i].len < path_len) {
+ *insert = i;
+ /* Clear insert so we don't try to set it again */
+ insert = NULL;
+ }
+
+ if (!strncmp (mappings[i].path, path, mappings[i].len)) {
+ *match = i;
+ if (path_len == mappings[i].len)
+ exact = TRUE;
+ if (!insert)
+ return exact;
+ }
+ }
+
+ if (insert)
+ *insert = i;
+ return exact;
+}
+
+/**
+ * soup_path_map_add:
+ * @map: a %SoupPathMap
+ * @path: the path
+ * @data: the data
+ *
+ * Adds @data to @map at @path. If there was already data at @path it
+ * will be freed.
+ **/
+void
+soup_path_map_add (SoupPathMap *map, const char *path, gpointer data)
+{
+ SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+ int match, insert;
+
+ if (mapping_lookup (map, path, &match, &insert)) {
+ if (map->free_func)
+ map->free_func (mappings[match].data);
+ mappings[match].data = data;
+ } else {
+ SoupPathMapping mapping;
+
+ mapping.path = g_strdup (path);
+ mapping.len = strlen (path);
+ mapping.data = data;
+ g_array_insert_val (map->mappings, insert, mapping);
+ }
+}
+
+/**
+ * soup_path_map_remove:
+ * @map: a %SoupPathMap
+ * @path: the path
+ *
+ * Removes @data from @map at @path. (This must be called with the same
+ * path the data was originally added with, not a subdirectory.)
+ **/
+void
+soup_path_map_remove (SoupPathMap *map, const char *path)
+{
+ SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+ int match;
+
+ if (!mapping_lookup (map, path, &match, NULL))
+ return;
+
+ if (map->free_func)
+ map->free_func (mappings[match].data);
+ g_free (mappings[match].path);
+ g_array_remove_index (map->mappings, match);
+}
+
+/**
+ * soup_path_map_lookup:
+ * @map: a %SoupPathMap
+ * @path: the path
+ *
+ * Finds the data associated with @path in @map. If there is no data
+ * specifically associated with @path, it will return the data for the
+ * closest parent directory of @path that has data associated with it.
+ *
+ * Return value: the data set with soup_path_map_add(), or %NULL if no
+ * data could be found for @path or any of its ancestors.
+ **/
+gpointer
+soup_path_map_lookup (SoupPathMap *map, const char *path)
+{
+ SoupPathMapping *mappings = (SoupPathMapping *)map->mappings->data;
+ int match;
+
+ mapping_lookup (map, path, &match, NULL);
+ if (match == -1)
+ return NULL;
+ else
+ return mappings[match].data;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#ifndef SOUP_PATH_MAP_H
+#define SOUP_PATH_MAP_H 1
+
+#include <libsoup/soup-types.h>
+
+typedef struct SoupPathMap SoupPathMap;
+
+SoupPathMap *soup_path_map_new (GDestroyNotify data_free_func);
+void soup_path_map_free (SoupPathMap *map);
+
+void soup_path_map_add (SoupPathMap *map,
+ const char *path,
+ gpointer data);
+void soup_path_map_remove (SoupPathMap *map,
+ const char *path);
+
+gpointer soup_path_map_lookup (SoupPathMap *map,
+ const char *path);
+
+
+#endif /* SOUP_PATH_MAP_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005, Novell, Inc.
+ */
+
+#ifndef SOUP_PORTABILITY_H
+#define SOUP_PORTABILITY_H
+
+#include <libsoup/soup-types.h>
+
+#ifdef G_OS_WIN32
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#else
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#endif
+
+#endif /* SOUP_PORTABILITY_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-proxy-resolver-default.c: proxy resolution based on GIO's GProxyResolver
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "soup-proxy-resolver-default.h"
+#include "soup-proxy-uri-resolver.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+/**
+ * SoupProxyResolverDefault:
+ *
+ * A #SoupProxyURIResolver implementation that uses the default gio
+ * #GProxyResolver to resolve proxies.
+ *
+ * Since: 2.34
+ */
+
+static void soup_proxy_resolver_default_interface_init (SoupProxyURIResolverInterface *proxy_resolver_interface);
+
+G_DEFINE_TYPE_EXTENDED (SoupProxyResolverDefault, soup_proxy_resolver_default, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, NULL)
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_PROXY_URI_RESOLVER, soup_proxy_resolver_default_interface_init))
+
+enum {
+ PROP_0,
+ PROP_GPROXY_RESOLVER
+};
+
+typedef struct {
+ GProxyResolver *gproxy_resolver;
+} SoupProxyResolverDefaultPrivate;
+#define SOUP_PROXY_RESOLVER_DEFAULT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_PROXY_RESOLVER_DEFAULT, SoupProxyResolverDefaultPrivate))
+
+static void
+soup_proxy_resolver_default_init (SoupProxyResolverDefault *resolver)
+{
+}
+
+static void
+soup_proxy_resolver_default_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupProxyResolverDefaultPrivate *priv = SOUP_PROXY_RESOLVER_DEFAULT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_GPROXY_RESOLVER:
+ if (priv->gproxy_resolver)
+ g_object_unref (priv->gproxy_resolver);
+ priv->gproxy_resolver = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_proxy_resolver_default_constructed (GObject *object)
+{
+ SoupProxyResolverDefaultPrivate *priv = SOUP_PROXY_RESOLVER_DEFAULT_GET_PRIVATE (object);
+
+ if (!priv->gproxy_resolver) {
+ priv->gproxy_resolver = g_proxy_resolver_get_default ();
+ g_object_ref (priv->gproxy_resolver);
+ }
+
+ G_OBJECT_CLASS (soup_proxy_resolver_default_parent_class)->constructed (object);
+}
+
+static void
+soup_proxy_resolver_default_finalize (GObject *object)
+{
+ SoupProxyResolverDefaultPrivate *priv = SOUP_PROXY_RESOLVER_DEFAULT_GET_PRIVATE (object);
+
+ if (priv->gproxy_resolver)
+ g_object_unref (priv->gproxy_resolver);
+
+ G_OBJECT_CLASS (soup_proxy_resolver_default_parent_class)->finalize (object);
+}
+
+static void
+soup_proxy_resolver_default_class_init (SoupProxyResolverDefaultClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (SoupProxyResolverDefaultPrivate));
+
+ object_class->set_property = soup_proxy_resolver_default_set_property;
+ object_class->constructed = soup_proxy_resolver_default_constructed;
+ object_class->finalize = soup_proxy_resolver_default_finalize;
+
+ g_object_class_install_property (
+ object_class, PROP_GPROXY_RESOLVER,
+ g_param_spec_object ("gproxy-resolver",
+ "GProxyResolver",
+ "The underlying GProxyResolver",
+ G_TYPE_PROXY_RESOLVER,
+ G_PARAM_WRITABLE));
+}
+
+typedef struct {
+ SoupProxyURIResolver *resolver;
+ GCancellable *cancellable;
+ SoupProxyURIResolverCallback callback;
+ gpointer user_data;
+} SoupAsyncData;
+
+static void
+resolved_proxy (GObject *object, GAsyncResult *result, gpointer data)
+{
+ GProxyResolver *proxy_resolver = G_PROXY_RESOLVER (object);
+ SoupAsyncData *async_data = data;
+ GError *error = NULL;
+ char **proxy_uris = NULL;
+ SoupURI *proxy_uri = NULL;
+ guint status = SOUP_STATUS_OK;
+
+ proxy_uris = g_proxy_resolver_lookup_finish (proxy_resolver,
+ result,
+ &error);
+
+ if (error || proxy_uris == NULL || proxy_uris[0] == NULL) {
+ status = SOUP_STATUS_CANT_RESOLVE_PROXY;
+ goto finish;
+ }
+
+ /* We need to handle direct:// specially, otherwise
+ * SoupSession will try to resolve it as the proxy address.
+ */
+ if (!g_strcmp0 (proxy_uris[0], "direct://"))
+ goto finish;
+
+ proxy_uri = soup_uri_new (proxy_uris[0]);
+ if (proxy_uri == NULL)
+ status = SOUP_STATUS_CANT_RESOLVE_PROXY;
+
+finish:
+ async_data->callback (async_data->resolver,
+ status,
+ proxy_uri,
+ async_data->user_data);
+
+ if (async_data->cancellable)
+ g_object_unref (async_data->cancellable);
+
+ g_strfreev (proxy_uris);
+
+ if (proxy_uri)
+ soup_uri_free (proxy_uri);
+
+ g_object_unref (async_data->resolver);
+ g_slice_free (SoupAsyncData, async_data);
+}
+
+static void
+get_proxy_uri_async (SoupProxyURIResolver *resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data)
+{
+ SoupProxyResolverDefaultPrivate *priv = SOUP_PROXY_RESOLVER_DEFAULT_GET_PRIVATE (resolver);
+ SoupAsyncData *async_data;
+ char *uri_string;
+
+ async_data = g_slice_new0 (SoupAsyncData);
+ async_data->resolver = (SoupProxyURIResolver*) g_object_ref (resolver);
+ async_data->cancellable = cancellable;
+ async_data->callback = callback;
+ async_data->user_data = user_data;
+
+ uri_string = soup_uri_to_string (uri, FALSE);
+
+ if (async_context)
+ g_main_context_push_thread_default (async_context);
+
+ g_proxy_resolver_lookup_async (priv->gproxy_resolver,
+ uri_string,
+ cancellable ? g_object_ref (cancellable) : NULL,
+ resolved_proxy,
+ async_data);
+
+ if (async_context)
+ g_main_context_pop_thread_default (async_context);
+
+ g_free (uri_string);
+}
+
+static guint
+get_proxy_uri_sync (SoupProxyURIResolver *resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri)
+{
+ SoupProxyResolverDefaultPrivate *priv = SOUP_PROXY_RESOLVER_DEFAULT_GET_PRIVATE (resolver);
+ GError *error = NULL;
+ char** proxy_uris = NULL;
+ char *uri_string;
+ guint status = SOUP_STATUS_OK;
+
+ uri_string = soup_uri_to_string (uri, FALSE);
+
+ proxy_uris = g_proxy_resolver_lookup (priv->gproxy_resolver,
+ uri_string,
+ cancellable,
+ &error);
+
+ g_free (uri_string);
+
+ if (error || proxy_uris == NULL || proxy_uris[0] == NULL) {
+ status = SOUP_STATUS_CANT_RESOLVE_PROXY;
+ goto cleanup;
+ }
+
+ /* We need to handle direct:// specially, otherwise
+ * SoupSession will try to resolve it as the proxy address.
+ */
+ if (!g_strcmp0 (proxy_uris[0], "direct://"))
+ goto cleanup;
+
+ *proxy_uri = soup_uri_new (proxy_uris[0]);
+
+ if (!*proxy_uri)
+ status = SOUP_STATUS_CANT_RESOLVE_PROXY;
+
+cleanup:
+ g_strfreev (proxy_uris);
+ if (error)
+ g_clear_error (&error);
+ return status;
+}
+
+static void
+soup_proxy_resolver_default_interface_init (SoupProxyURIResolverInterface *iface)
+{
+ iface->get_proxy_uri_async = get_proxy_uri_async;
+ iface->get_proxy_uri_sync = get_proxy_uri_sync;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ */
+
+#ifndef SOUP_PROXY_RESOLVER_DEFAULT_H
+#define SOUP_PROXY_RESOLVER_DEFAULT_H 1
+
+#include <glib-object.h>
+
+#define SOUP_PROXY_RESOLVER_DEFAULT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_RESOLVER_DEFAULT, SoupProxyResolverDefault))
+#define SOUP_PROXY_RESOLVER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_RESOLVER_DEFAULT, SoupProxyResolverDefaultClass))
+#define SOUP_IS_PROXY_RESOLVER_DEFAULT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PROXY_RESOLVER_DEFAULT))
+#define SOUP_IS_PROXY_RESOLVER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PROXY_RESOLVER_DEFAULT))
+#define SOUP_PROXY_RESOLVER_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_PROXY_RESOLVER_DEFAULT, SoupProxyResolverDefaultClass))
+
+typedef struct {
+ GObject parent;
+
+} SoupProxyResolverDefault;
+
+typedef struct {
+ GObjectClass parent_class;
+
+} SoupProxyResolverDefaultClass;
+
+GType soup_proxy_resolver_default_get_type (void);
+#define SOUP_TYPE_PROXY_RESOLVER_DEFAULT (soup_proxy_resolver_default_get_type ())
+
+#endif /*SOUP_PROXY_RESOLVER_DEFAULT_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-proxy-resolver-gnome.c: GNOME proxy resolution
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-proxy-resolver-gnome.h"
+
+G_DEFINE_TYPE (SoupProxyResolverGNOME, soup_proxy_resolver_gnome, SOUP_TYPE_PROXY_RESOLVER_DEFAULT)
+
+static void
+soup_proxy_resolver_gnome_init (SoupProxyResolverGNOME *resolver_gnome)
+{
+ GProxyResolver *gproxyresolver;
+ GIOExtensionPoint *ep;
+ GIOExtension *ext;
+ GType type;
+
+ /* FIXME: there is no way to force _g_io_modules_ensure_loaded()
+ * to be run other than by requesting some extension that we
+ * don't necessarily want.
+ */
+ gproxyresolver = g_proxy_resolver_get_default ();
+ if (strcmp (G_OBJECT_TYPE_NAME (gproxyresolver),
+ "GProxyResolverGnome") == 0)
+ return;
+
+ ep = g_io_extension_point_lookup (G_PROXY_RESOLVER_EXTENSION_POINT_NAME);
+ if (!ep)
+ return;
+
+ ext = g_io_extension_point_get_extension_by_name (ep, "gnome");
+ if (!ext)
+ return;
+
+ type = g_io_extension_get_type (ext);
+ gproxyresolver = g_object_new (type, NULL);
+ g_object_set (G_OBJECT (resolver_gnome),
+ "gproxy-resolver", gproxyresolver,
+ NULL);
+ g_object_unref (gproxyresolver);
+}
+
+static void
+soup_proxy_resolver_gnome_class_init (SoupProxyResolverGNOMEClass *gnome_class)
+{
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PROXY_RESOLVER_GNOME_H
+#define SOUP_PROXY_RESOLVER_GNOME_H 1
+
+#include "soup-gnome-features.h"
+#include "soup-proxy-resolver-default.h"
+
+/* SOUP_TYPE_PROXY_RESOLVER_GNOME and soup_proxy_resolver_gnome_get_type()
+ * are declared in soup-gnome-features.h.
+ */
+
+#define SOUP_PROXY_RESOLVER_GNOME(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_RESOLVER_GNOME, SoupProxyResolverGNOME))
+#define SOUP_PROXY_RESOLVER_GNOME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_RESOLVER_GNOME, SoupProxyResolverGNOMEClass))
+#define SOUP_IS_PROXY_RESOLVER_GNOME(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PROXY_RESOLVER_GNOME))
+#define SOUP_IS_PROXY_RESOLVER_GNOME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PROXY_RESOLVER_GNOME))
+#define SOUP_PROXY_RESOLVER_GNOME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_PROXY_RESOLVER_GNOME, SoupProxyResolverGNOMEClass))
+
+typedef SoupProxyResolverDefault SoupProxyResolverGNOME;
+typedef SoupProxyResolverDefaultClass SoupProxyResolverGNOMEClass;
+
+#endif /*SOUP_PROXY_RESOLVER_GNOME_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-proxy-resolver-static.c: Static proxy "resolution"
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-proxy-resolver-static.h"
+#include "soup-address.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-session-feature.h"
+
+typedef struct {
+ SoupURI *proxy_uri;
+
+} SoupProxyResolverStaticPrivate;
+#define SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_PROXY_RESOLVER_STATIC, SoupProxyResolverStaticPrivate))
+
+static void soup_proxy_resolver_static_interface_init (SoupProxyURIResolverInterface *proxy_resolver_interface);
+
+G_DEFINE_TYPE_EXTENDED (SoupProxyResolverStatic, soup_proxy_resolver_static, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, NULL)
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_PROXY_URI_RESOLVER, soup_proxy_resolver_static_interface_init))
+
+enum {
+ PROP_0,
+
+ PROP_PROXY_URI,
+
+ LAST_PROP
+};
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void get_proxy_uri_async (SoupProxyURIResolver *proxy_resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data);
+static guint get_proxy_uri_sync (SoupProxyURIResolver *proxy_resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri);
+
+static void
+soup_proxy_resolver_static_init (SoupProxyResolverStatic *resolver_static)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupProxyResolverStaticPrivate *priv =
+ SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (object);
+
+ if (priv->proxy_uri)
+ soup_uri_free (priv->proxy_uri);
+
+ G_OBJECT_CLASS (soup_proxy_resolver_static_parent_class)->finalize (object);
+}
+
+static void
+soup_proxy_resolver_static_class_init (SoupProxyResolverStaticClass *static_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (static_class);
+
+ g_type_class_add_private (static_class, sizeof (SoupProxyResolverStaticPrivate));
+
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ g_object_class_install_property (
+ object_class, PROP_PROXY_URI,
+ g_param_spec_boxed (SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
+ "Proxy URI",
+ "The HTTP Proxy to use",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupProxyResolverStaticPrivate *priv =
+ SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (object);
+ SoupURI *uri;
+
+ switch (prop_id) {
+ case PROP_PROXY_URI:
+ uri = g_value_get_boxed (value);
+ if (priv->proxy_uri)
+ soup_uri_free (priv->proxy_uri);
+
+ priv->proxy_uri = uri ? soup_uri_copy (uri) : NULL;
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupProxyResolverStaticPrivate *priv =
+ SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_PROXY_URI:
+ g_value_set_boxed (value, priv->proxy_uri);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_proxy_resolver_static_interface_init (SoupProxyURIResolverInterface *proxy_uri_resolver_interface)
+{
+ proxy_uri_resolver_interface->get_proxy_uri_async = get_proxy_uri_async;
+ proxy_uri_resolver_interface->get_proxy_uri_sync = get_proxy_uri_sync;
+}
+
+SoupProxyURIResolver *
+soup_proxy_resolver_static_new (SoupURI *proxy_uri)
+{
+ return g_object_new (SOUP_TYPE_PROXY_RESOLVER_STATIC,
+ SOUP_PROXY_RESOLVER_STATIC_PROXY_URI, proxy_uri,
+ NULL);
+}
+
+typedef struct {
+ SoupProxyURIResolver *proxy_resolver;
+ SoupProxyURIResolverCallback callback;
+ gpointer user_data;
+} SoupStaticAsyncData;
+
+static gboolean
+idle_return_proxy_uri (gpointer data)
+{
+ SoupStaticAsyncData *ssad = data;
+ SoupProxyResolverStaticPrivate *priv =
+ SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (ssad->proxy_resolver);
+
+ ssad->callback (ssad->proxy_resolver,
+ SOUP_STATUS_OK, priv->proxy_uri,
+ ssad->user_data);
+ g_object_unref (ssad->proxy_resolver);
+ g_slice_free (SoupStaticAsyncData, ssad);
+
+ return FALSE;
+}
+
+static void
+get_proxy_uri_async (SoupProxyURIResolver *proxy_resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data)
+{
+ SoupStaticAsyncData *ssad;
+
+ ssad = g_slice_new0 (SoupStaticAsyncData);
+ ssad->proxy_resolver = g_object_ref (proxy_resolver);
+ ssad->callback = callback;
+ ssad->user_data = user_data;
+ soup_add_completion (async_context, idle_return_proxy_uri, ssad);
+}
+
+static guint
+get_proxy_uri_sync (SoupProxyURIResolver *proxy_resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri)
+{
+ SoupProxyResolverStaticPrivate *priv =
+ SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (proxy_resolver);
+
+ *proxy_uri = soup_uri_copy (priv->proxy_uri);
+ return SOUP_STATUS_OK;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PROXY_RESOLVER_STATIC_H
+#define SOUP_PROXY_RESOLVER_STATIC_H 1
+
+#include "soup-proxy-uri-resolver.h"
+#include "soup-uri.h"
+
+#define SOUP_TYPE_PROXY_RESOLVER_STATIC (soup_proxy_resolver_static_get_type ())
+#define SOUP_PROXY_RESOLVER_STATIC(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_RESOLVER_STATIC, SoupProxyResolverStatic))
+#define SOUP_PROXY_RESOLVER_STATIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_RESOLVER_STATIC, SoupProxyResolverStaticClass))
+#define SOUP_IS_PROXY_RESOLVER_STATIC(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PROXY_RESOLVER_STATIC))
+#define SOUP_IS_PROXY_RESOLVER_STATIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PROXY_RESOLVER_STATIC))
+#define SOUP_PROXY_RESOLVER_STATIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_PROXY_RESOLVER_STATIC, SoupProxyResolverStaticClass))
+
+typedef struct {
+ GObject parent;
+
+} SoupProxyResolverStatic;
+
+typedef struct {
+ GObjectClass parent_class;
+
+} SoupProxyResolverStaticClass;
+
+GType soup_proxy_resolver_static_get_type (void);
+
+#define SOUP_PROXY_RESOLVER_STATIC_PROXY_URI "proxy-uri"
+
+SoupProxyURIResolver *soup_proxy_resolver_static_new (SoupURI *proxy_uri);
+
+#endif /* SOUP_PROXY_RESOLVER_STATIC_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-proxy-resolver.c: HTTP proxy resolver interface
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-proxy-resolver.h"
+#include "soup-proxy-uri-resolver.h"
+#include "soup-address.h"
+#include "soup-message.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+static void soup_proxy_resolver_interface_init (GTypeInterface *iface);
+static void soup_proxy_resolver_uri_resolver_interface_init (SoupProxyURIResolverInterface *uri_resolver_interface);
+
+GType
+soup_proxy_resolver_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ GType g_define_type_id =
+ g_type_register_static_simple (G_TYPE_INTERFACE,
+ g_intern_static_string ("SoupProxyResolver"),
+ sizeof (SoupProxyResolverInterface),
+ (GClassInitFunc)soup_proxy_resolver_interface_init,
+ 0,
+ (GInstanceInitFunc)NULL,
+ (GTypeFlags) 0);
+ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (g_define_type_id, SOUP_TYPE_SESSION_FEATURE);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+static void
+proxy_resolver_interface_check (gpointer func_data, gpointer g_iface)
+{
+ GTypeInterface *iface = g_iface;
+
+ if (iface->g_type != SOUP_TYPE_PROXY_RESOLVER)
+ return;
+
+ /* If the class hasn't already declared that it implements
+ * SoupProxyURIResolver, add our own compat implementation.
+ */
+ if (!g_type_is_a (iface->g_instance_type, SOUP_TYPE_PROXY_URI_RESOLVER)) {
+ const GInterfaceInfo uri_resolver_interface_info = {
+ (GInterfaceInitFunc) soup_proxy_resolver_uri_resolver_interface_init, NULL, NULL
+ };
+ g_type_add_interface_static (iface->g_instance_type,
+ SOUP_TYPE_PROXY_URI_RESOLVER,
+ &uri_resolver_interface_info);
+ }
+}
+
+
+static void
+soup_proxy_resolver_interface_init (GTypeInterface *iface)
+{
+ /* Add an interface_check where we can kludgily add the
+ * SoupProxyURIResolver interface to all SoupProxyResolvers.
+ * (SoupProxyResolver can't just implement
+ * SoupProxyURIResolver itself because interface types can't
+ * implement other interfaces.) This is an ugly hack, but it
+ * only gets used if someone actually creates a
+ * SoupProxyResolver...
+ */
+ g_type_add_interface_check (NULL, proxy_resolver_interface_check);
+}
+
+void
+soup_proxy_resolver_get_proxy_async (SoupProxyResolver *proxy_resolver,
+ SoupMessage *msg,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyResolverCallback callback,
+ gpointer user_data)
+{
+ SOUP_PROXY_RESOLVER_GET_CLASS (proxy_resolver)->
+ get_proxy_async (proxy_resolver, msg,
+ async_context, cancellable,
+ callback, user_data);
+}
+
+guint
+soup_proxy_resolver_get_proxy_sync (SoupProxyResolver *proxy_resolver,
+ SoupMessage *msg,
+ GCancellable *cancellable,
+ SoupAddress **addr)
+{
+ return SOUP_PROXY_RESOLVER_GET_CLASS (proxy_resolver)->
+ get_proxy_sync (proxy_resolver, msg, cancellable, addr);
+}
+
+/* SoupProxyURIResolver implementation */
+
+static SoupURI *
+uri_from_address (SoupAddress *addr)
+{
+ SoupURI *proxy_uri;
+
+ proxy_uri = soup_uri_new (NULL);
+ soup_uri_set_scheme (proxy_uri, SOUP_URI_SCHEME_HTTP);
+ soup_uri_set_host (proxy_uri, soup_address_get_name (addr));
+ soup_uri_set_port (proxy_uri, soup_address_get_port (addr));
+ return proxy_uri;
+}
+
+typedef struct {
+ SoupProxyURIResolverCallback callback;
+ gpointer user_data;
+} ProxyURIResolverAsyncData;
+
+static void
+compat_got_proxy (SoupProxyResolver *proxy_resolver,
+ SoupMessage *msg, guint status, SoupAddress *proxy_addr,
+ gpointer user_data)
+{
+ ProxyURIResolverAsyncData *purad = user_data;
+ SoupURI *proxy_uri;
+
+ proxy_uri = proxy_addr ? uri_from_address (proxy_addr) : NULL;
+ purad->callback (SOUP_PROXY_URI_RESOLVER (proxy_resolver),
+ status, proxy_uri, purad->user_data);
+ g_object_unref (msg);
+ if (proxy_uri)
+ soup_uri_free (proxy_uri);
+ g_slice_free (ProxyURIResolverAsyncData, purad);
+}
+
+static void
+compat_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri, GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data)
+{
+ SoupMessage *dummy_msg;
+ ProxyURIResolverAsyncData *purad;
+
+ dummy_msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+
+ purad = g_slice_new (ProxyURIResolverAsyncData);
+ purad->callback = callback;
+ purad->user_data = user_data;
+
+ soup_proxy_resolver_get_proxy_async (
+ SOUP_PROXY_RESOLVER (proxy_uri_resolver), dummy_msg,
+ async_context, cancellable,
+ compat_got_proxy, purad);
+}
+
+static guint
+compat_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri, GCancellable *cancellable,
+ SoupURI **proxy_uri)
+{
+ SoupMessage *dummy_msg;
+ SoupAddress *proxy_addr = NULL;
+ guint status;
+
+ dummy_msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+ status = soup_proxy_resolver_get_proxy_sync (
+ SOUP_PROXY_RESOLVER (proxy_uri_resolver), dummy_msg,
+ cancellable, &proxy_addr);
+ g_object_unref (dummy_msg);
+ if (!proxy_addr)
+ return status;
+
+ *proxy_uri = uri_from_address (proxy_addr);
+ g_object_unref (proxy_addr);
+ return status;
+}
+
+static void
+soup_proxy_resolver_uri_resolver_interface_init (SoupProxyURIResolverInterface *uri_resolver_interface)
+{
+ uri_resolver_interface->get_proxy_uri_async = compat_get_proxy_uri_async;
+ uri_resolver_interface->get_proxy_uri_sync = compat_get_proxy_uri_sync;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PROXY_RESOLVER_H
+#define SOUP_PROXY_RESOLVER_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+
+#define SOUP_TYPE_PROXY_RESOLVER (soup_proxy_resolver_get_type ())
+#define SOUP_PROXY_RESOLVER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_RESOLVER, SoupProxyResolver))
+#define SOUP_PROXY_RESOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_RESOLVER, SoupProxyResolverInterface))
+#define SOUP_IS_PROXY_RESOLVER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PROXY_RESOLVER))
+#define SOUP_IS_PROXY_RESOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PROXY_RESOLVER))
+#define SOUP_PROXY_RESOLVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SOUP_TYPE_PROXY_RESOLVER, SoupProxyResolverInterface))
+
+typedef struct _SoupProxyResolver SoupProxyResolver;
+
+typedef void (*SoupProxyResolverCallback) (SoupProxyResolver *, SoupMessage *,
+ guint, SoupAddress *, gpointer);
+
+typedef struct {
+ GTypeInterface base;
+
+ /* virtual methods */
+ void (*get_proxy_async) (SoupProxyResolver *proxy_resolver,
+ SoupMessage *msg, GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyResolverCallback callaback,
+ gpointer user_data);
+ guint (*get_proxy_sync) (SoupProxyResolver *proxy_resolver,
+ SoupMessage *msg, GCancellable *cancellable,
+ SoupAddress **addr);
+
+} SoupProxyResolverInterface;
+
+GType soup_proxy_resolver_get_type (void);
+
+void soup_proxy_resolver_get_proxy_async (SoupProxyResolver *proxy_resolver,
+ SoupMessage *msg,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyResolverCallback callback,
+ gpointer user_data);
+guint soup_proxy_resolver_get_proxy_sync (SoupProxyResolver *proxy_resolver,
+ SoupMessage *msg,
+ GCancellable *cancellable,
+ SoupAddress **addr);
+
+#endif
+
+G_END_DECLS
+
+#endif /*SOUP_PROXY_RESOLVER_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-proxy-uri-resolver.c: HTTP proxy resolver interface, take 2
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-proxy-uri-resolver.h"
+#include "soup-session-feature.h"
+
+GType
+soup_proxy_uri_resolver_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ GType g_define_type_id =
+ g_type_register_static_simple (G_TYPE_INTERFACE,
+ g_intern_static_string ("SoupProxyURIResolver"),
+ sizeof (SoupProxyURIResolverInterface),
+ (GClassInitFunc)NULL,
+ 0,
+ (GInstanceInitFunc)NULL,
+ (GTypeFlags) 0);
+ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+/**
+ * SoupProxyURIResolverCallback:
+ * @resolver: the #SoupProxyURIResolver
+ * @status: a #SoupKnownStatusCode
+ * @proxy_uri: the resolved proxy URI, or %NULL
+ * @user_data: data passed to soup_proxy_uri_resolver_get_proxy_uri_async()
+ *
+ * Callback for soup_proxy_uri_resolver_get_proxy_uri_async()
+ **/
+
+/**
+ * soup_proxy_uri_resolver_get_proxy_uri_async:
+ * @proxy_uri_resolver: the #SoupProxyURIResolver
+ * @uri: the #SoupURI you want a proxy for
+ * @async_context: (allow-none): the #GMainContext to invoke @callback in
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: (scope async): callback to invoke with the proxy address
+ * @user_data: data for @callback
+ *
+ * Asynchronously determines a proxy URI to use for @msg and calls
+ * @callback.
+ *
+ * Since: 2.26.3
+ **/
+void
+soup_proxy_uri_resolver_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data)
+{
+ SOUP_PROXY_URI_RESOLVER_GET_CLASS (proxy_uri_resolver)->
+ get_proxy_uri_async (proxy_uri_resolver, uri,
+ async_context, cancellable,
+ callback, user_data);
+}
+
+/**
+ * soup_proxy_uri_resolver_get_proxy_uri_sync:
+ * @proxy_uri_resolver: the #SoupProxyURIResolver
+ * @uri: the #SoupURI you want a proxy for
+ * @cancellable: a #GCancellable, or %NULL
+ * @proxy_uri: (out): on return, will contain the proxy URI
+ *
+ * Synchronously determines a proxy URI to use for @uri. If @uri
+ * should be sent via proxy, *@proxy_uri will be set to the URI of the
+ * proxy, else it will be set to %NULL.
+ *
+ * Return value: %SOUP_STATUS_OK if successful, or a transport-level
+ * error.
+ *
+ * Since: 2.26.3
+ **/
+guint
+soup_proxy_uri_resolver_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri)
+{
+ return SOUP_PROXY_URI_RESOLVER_GET_CLASS (proxy_uri_resolver)->
+ get_proxy_uri_sync (proxy_uri_resolver, uri, cancellable, proxy_uri);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PROXY_URI_RESOLVER_H
+#define SOUP_PROXY_URI_RESOLVER_H 1
+
+#include <libsoup/soup-proxy-resolver.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_PROXY_URI_RESOLVER (soup_proxy_uri_resolver_get_type ())
+#define SOUP_PROXY_URI_RESOLVER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_URI_RESOLVER, SoupProxyURIResolver))
+#define SOUP_PROXY_URI_RESOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_URI_RESOLVER, SoupProxyURIResolverInterface))
+#define SOUP_IS_PROXY_URI_RESOLVER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PROXY_URI_RESOLVER))
+#define SOUP_IS_PROXY_URI_RESOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PROXY_URI_RESOLVER))
+#define SOUP_PROXY_URI_RESOLVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SOUP_TYPE_PROXY_URI_RESOLVER, SoupProxyURIResolverInterface))
+
+typedef struct _SoupProxyURIResolver SoupProxyURIResolver;
+
+typedef void (*SoupProxyURIResolverCallback) (SoupProxyURIResolver *resolver,
+ guint status, SoupURI *proxy_uri,
+ gpointer user_data);
+
+typedef struct {
+ GTypeInterface base;
+
+ /* virtual methods */
+ void (*get_proxy_uri_async) (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri, GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data);
+ guint (*get_proxy_uri_sync) (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri, GCancellable *cancellable,
+ SoupURI **proxy_uri);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupProxyURIResolverInterface;
+
+GType soup_proxy_uri_resolver_get_type (void);
+
+void soup_proxy_uri_resolver_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data);
+guint soup_proxy_uri_resolver_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri);
+
+G_END_DECLS
+
+#endif /*SOUP_PROXY_URI_RESOLVER_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-data.c: data: URI request object
+ *
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "soup-request-data.h"
+
+#include "soup-requester.h"
+#include "soup-misc-private.h"
+#include <libsoup/soup.h>
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE (SoupRequestData, soup_request_data, SOUP_TYPE_REQUEST)
+
+struct _SoupRequestDataPrivate {
+ gsize content_length;
+ char *content_type;
+};
+
+static void
+soup_request_data_init (SoupRequestData *data)
+{
+ data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, SOUP_TYPE_REQUEST_DATA, SoupRequestDataPrivate);
+}
+
+static void
+soup_request_data_finalize (GObject *object)
+{
+ SoupRequestData *data = SOUP_REQUEST_DATA (object);
+
+ g_free (data->priv->content_type);
+
+ G_OBJECT_CLASS (soup_request_data_parent_class)->finalize (object);
+}
+
+static gboolean
+soup_request_data_check_uri (SoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ return uri->host == NULL;
+}
+
+#define BASE64_INDICATOR ";base64"
+#define BASE64_INDICATOR_LEN (sizeof (";base64") - 1)
+
+static GInputStream *
+soup_request_data_send (SoupRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupRequestData *data = SOUP_REQUEST_DATA (request);
+ SoupURI *uri = soup_request_get_uri (request);
+ GInputStream *memstream;
+ const char *comma, *start, *end;
+ gboolean base64 = FALSE;
+ char *uristr;
+
+ uristr = soup_uri_to_string (uri, FALSE);
+ start = uristr + 5;
+ comma = strchr (start, ',');
+ if (comma && comma != start) {
+ /* Deal with MIME type / params */
+ if (comma > start + BASE64_INDICATOR_LEN && !g_ascii_strncasecmp (comma - BASE64_INDICATOR_LEN, BASE64_INDICATOR, BASE64_INDICATOR_LEN)) {
+ end = comma - BASE64_INDICATOR_LEN;
+ base64 = TRUE;
+ } else
+ end = comma;
+
+ if (end != start)
+ data->priv->content_type = uri_decoded_copy (start, end - start);
+ }
+
+ memstream = g_memory_input_stream_new ();
+
+ if (comma)
+ start = comma + 1;
+
+ if (*start) {
+ guchar *buf = (guchar *) soup_uri_decode (start);
+
+ if (base64)
+ buf = g_base64_decode_inplace ((gchar*) buf, &data->priv->content_length);
+ else
+ data->priv->content_length = strlen ((const char *) buf);
+
+ g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (memstream),
+ buf, data->priv->content_length,
+ g_free);
+ }
+ g_free (uristr);
+
+ return memstream;
+}
+
+static goffset
+soup_request_data_get_content_length (SoupRequest *request)
+{
+ SoupRequestData *data = SOUP_REQUEST_DATA (request);
+
+ return data->priv->content_length;
+}
+
+static const char *
+soup_request_data_get_content_type (SoupRequest *request)
+{
+ SoupRequestData *data = SOUP_REQUEST_DATA (request);
+
+ if (data->priv->content_type)
+ return data->priv->content_type;
+ else
+ return "text/plain;charset=US-ASCII";
+}
+
+static const char *data_schemes[] = { "data", NULL };
+
+static void
+soup_request_data_class_init (SoupRequestDataClass *request_data_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (request_data_class);
+ SoupRequestClass *request_class =
+ SOUP_REQUEST_CLASS (request_data_class);
+
+ g_type_class_add_private (request_data_class, sizeof (SoupRequestDataPrivate));
+
+ request_class->schemes = data_schemes;
+
+ object_class->finalize = soup_request_data_finalize;
+
+ request_class->check_uri = soup_request_data_check_uri;
+ request_class->send = soup_request_data_send;
+ request_class->get_content_length = soup_request_data_get_content_length;
+ request_class->get_content_type = soup_request_data_get_content_type;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SOUP_REQUEST_DATA_H
+#define SOUP_REQUEST_DATA_H 1
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_REQUEST_DATA (soup_request_data_get_type ())
+#define SOUP_REQUEST_DATA(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_REQUEST_DATA, SoupRequestData))
+#define SOUP_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUEST_DATA, SoupRequestDataClass))
+#define SOUP_IS_REQUEST_DATA(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_REQUEST_DATA))
+#define SOUP_IS_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST_DATA))
+#define SOUP_REQUEST_DATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST_DATA, SoupRequestDataClass))
+
+typedef struct _SoupRequestDataPrivate SoupRequestDataPrivate;
+
+typedef struct {
+ SoupRequest parent;
+
+ SoupRequestDataPrivate *priv;
+} SoupRequestData;
+
+typedef struct {
+ SoupRequestClass parent;
+} SoupRequestDataClass;
+
+GType soup_request_data_get_type (void);
+
+G_END_DECLS
+
+#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
+
+#endif /* SOUP_REQUEST_DATA_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-file.c: file: URI request object
+ *
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include <glib/gi18n.h>
+
+#include "soup-request-file.h"
+#include "soup-directory-input-stream.h"
+#include "soup-requester.h"
+#include "soup-uri.h"
+
+G_DEFINE_TYPE (SoupRequestFile, soup_request_file, SOUP_TYPE_REQUEST)
+
+struct _SoupRequestFilePrivate {
+ GFile *gfile;
+
+ char *mime_type;
+ goffset size;
+};
+
+static void
+soup_request_file_init (SoupRequestFile *file)
+{
+ file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, SOUP_TYPE_REQUEST_FILE, SoupRequestFilePrivate);
+
+ file->priv->size = -1;
+}
+
+static void
+soup_request_file_finalize (GObject *object)
+{
+ SoupRequestFile *file = SOUP_REQUEST_FILE (object);
+
+ if (file->priv->gfile)
+ g_object_unref (file->priv->gfile);
+ g_free (file->priv->mime_type);
+
+ G_OBJECT_CLASS (soup_request_file_parent_class)->finalize (object);
+}
+
+static gboolean
+soup_request_file_check_uri (SoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ /* "file:/foo" is not valid */
+ if (!uri->host)
+ return FALSE;
+
+ /* but it must be "file:///..." or "file://localhost/..." */
+ if (uri->scheme == SOUP_URI_SCHEME_FILE &&
+ *uri->host &&
+ g_ascii_strcasecmp (uri->host, "localhost") != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+soup_request_file_ensure_file (SoupRequestFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupURI *uri;
+
+ if (file->priv->gfile)
+ return TRUE;
+
+ uri = soup_request_get_uri (SOUP_REQUEST (file));
+ if (uri->scheme == SOUP_URI_SCHEME_FILE) {
+ gchar *decoded_uri = soup_uri_decode (uri->path);
+
+ if (decoded_uri) {
+ file->priv->gfile = g_file_new_for_path (decoded_uri);
+ g_free (decoded_uri);
+ }
+
+ return TRUE;
+ }
+
+ g_set_error (error, SOUP_REQUESTER_ERROR, SOUP_REQUESTER_ERROR_UNSUPPORTED_URI_SCHEME,
+ _("Unsupported URI scheme '%s'"), uri->scheme);
+ return FALSE;
+}
+
+static GInputStream *
+soup_request_file_send (SoupRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupRequestFile *file = SOUP_REQUEST_FILE (request);
+ GInputStream *stream;
+ GError *my_error = NULL;
+
+ if (!soup_request_file_ensure_file (file, cancellable, error))
+ return NULL;
+
+ stream = G_INPUT_STREAM (g_file_read (file->priv->gfile,
+ cancellable, &my_error));
+ if (stream == NULL) {
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) {
+ GFileEnumerator *enumerator;
+ g_clear_error (&my_error);
+ enumerator = g_file_enumerate_children (file->priv->gfile,
+ "*",
+ G_FILE_QUERY_INFO_NONE,
+ cancellable,
+ error);
+ if (enumerator) {
+ stream = soup_directory_input_stream_new (enumerator,
+ soup_request_get_uri (request));
+ g_object_unref (enumerator);
+ file->priv->mime_type = g_strdup ("text/html");
+ }
+ } else
+ g_propagate_error (error, my_error);
+ } else {
+ GFileInfo *info = g_file_query_info (file->priv->gfile,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ 0, cancellable, NULL);
+ if (info) {
+ const char *content_type;
+ file->priv->size = g_file_info_get_size (info);
+ content_type = g_file_info_get_content_type (info);
+
+ if (content_type)
+ file->priv->mime_type = g_content_type_get_mime_type (content_type);
+ g_object_unref (info);
+ }
+ }
+
+ return stream;
+}
+
+static void
+soup_request_file_send_async_thread (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ GInputStream *stream;
+ SoupRequest *request;
+ GError *error = NULL;
+
+ request = SOUP_REQUEST (object);
+
+ stream = soup_request_file_send (request, cancellable, &error);
+
+ if (stream == NULL) {
+ g_simple_async_result_set_from_error (res, error);
+ g_error_free (error);
+ } else {
+ g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+ }
+}
+
+static void
+soup_request_file_send_async (SoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+
+ res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, soup_request_file_send_async);
+
+ g_simple_async_result_run_in_thread (res, soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable);
+ g_object_unref (res);
+}
+
+static GInputStream *
+soup_request_file_send_finish (SoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == soup_request_file_send_async);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static goffset
+soup_request_file_get_content_length (SoupRequest *request)
+{
+ SoupRequestFile *file = SOUP_REQUEST_FILE (request);
+
+ return file->priv->size;
+}
+
+static const char *
+soup_request_file_get_content_type (SoupRequest *request)
+{
+ SoupRequestFile *file = SOUP_REQUEST_FILE (request);
+
+ if (!file->priv->mime_type)
+ return "application/octet-stream";
+
+ return file->priv->mime_type;
+}
+
+static const char *file_schemes[] = { "file", NULL };
+
+static void
+soup_request_file_class_init (SoupRequestFileClass *request_file_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (request_file_class);
+ SoupRequestClass *request_class =
+ SOUP_REQUEST_CLASS (request_file_class);
+
+ g_type_class_add_private (request_file_class, sizeof (SoupRequestFilePrivate));
+
+ request_class->schemes = file_schemes;
+
+ object_class->finalize = soup_request_file_finalize;
+
+ request_class->check_uri = soup_request_file_check_uri;
+ request_class->send = soup_request_file_send;
+ request_class->send_async = soup_request_file_send_async;
+ request_class->send_finish = soup_request_file_send_finish;
+ request_class->get_content_length = soup_request_file_get_content_length;
+ request_class->get_content_type = soup_request_file_get_content_type;
+}
+
+GFile *
+soup_request_file_get_file (SoupRequestFile *file)
+{
+ return g_object_ref (file->priv->gfile);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SOUP_REQUEST_FILE_H
+#define SOUP_REQUEST_FILE_H 1
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_REQUEST_FILE (soup_request_file_get_type ())
+#define SOUP_REQUEST_FILE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_REQUEST_FILE, SoupRequestFile))
+#define SOUP_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUEST_FILE, SoupRequestFileClass))
+#define SOUP_IS_REQUEST_FILE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_REQUEST_FILE))
+#define SOUP_IS_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST_FILE))
+#define SOUP_REQUEST_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST_FILE, SoupRequestFileClass))
+
+typedef struct _SoupRequestFilePrivate SoupRequestFilePrivate;
+
+typedef struct {
+ SoupRequest parent;
+
+ SoupRequestFilePrivate *priv;
+} SoupRequestFile;
+
+typedef struct {
+ SoupRequestClass parent;
+} SoupRequestFileClass;
+
+GType soup_request_file_get_type (void);
+
+GFile *soup_request_file_get_file (SoupRequestFile *file);
+
+G_END_DECLS
+
+#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
+
+#endif /* SOUP_REQUEST_FILE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request-http.c: http: URI request object
+ *
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "soup-request-http.h"
+#include "soup-cache.h"
+#include "soup-cache-private.h"
+#include "soup-content-sniffer.h"
+#include "soup-http-input-stream.h"
+#include "soup-message.h"
+#include "soup-session.h"
+#include "soup-uri.h"
+
+G_DEFINE_TYPE (SoupRequestHTTP, soup_request_http, SOUP_TYPE_REQUEST)
+
+struct _SoupRequestHTTPPrivate {
+ SoupMessage *msg;
+};
+
+static void
+soup_request_http_init (SoupRequestHTTP *http)
+{
+ http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTPPrivate);
+}
+
+static gboolean
+soup_request_http_check_uri (SoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
+
+ if (!SOUP_URI_VALID_FOR_HTTP (uri))
+ return FALSE;
+
+ http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+ return TRUE;
+}
+
+static void
+soup_request_http_finalize (GObject *object)
+{
+ SoupRequestHTTP *http = SOUP_REQUEST_HTTP (object);
+
+ if (http->priv->msg)
+ g_object_unref (http->priv->msg);
+
+ G_OBJECT_CLASS (soup_request_http_parent_class)->finalize (object);
+}
+
+static GInputStream *
+soup_request_http_send (SoupRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTPInputStream *httpstream;
+ SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
+
+ httpstream = soup_http_input_stream_new (soup_request_get_session (request), http->priv->msg);
+ if (!soup_http_input_stream_send (httpstream, cancellable, error)) {
+ g_object_unref (httpstream);
+ return NULL;
+ }
+ return (GInputStream *)httpstream;
+}
+
+
+static void
+sent_async (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ SoupHTTPInputStream *httpstream = SOUP_HTTP_INPUT_STREAM (source);
+ GSimpleAsyncResult *simple = user_data;
+ GError *error = NULL;
+
+ if (soup_http_input_stream_send_finish (httpstream, result, &error)) {
+ g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
+ } else {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ g_object_unref (httpstream);
+ }
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+
+typedef struct {
+ SoupRequestHTTP *req;
+ SoupMessage *original;
+ GCancellable *cancellable;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+} ConditionalHelper;
+
+static void
+conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ ConditionalHelper *helper = (ConditionalHelper *)user_data;
+ GSimpleAsyncResult *simple;
+ SoupHTTPInputStream *httpstream;
+
+ simple = g_simple_async_result_new (G_OBJECT (helper->req),
+ helper->callback, helper->user_data,
+ conditional_get_ready_cb);
+
+ if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
+ SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
+
+ httpstream = (SoupHTTPInputStream *)soup_cache_send_response (cache, helper->original);
+ if (httpstream) {
+ g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref);
+
+ soup_message_got_headers (helper->original);
+
+ if (soup_session_get_feature_for_message (session, SOUP_TYPE_CONTENT_SNIFFER, helper->original)) {
+ const char *content_type =
+ soup_message_headers_get_content_type (helper->original->response_headers, NULL);
+ soup_message_content_sniffed (helper->original, content_type, NULL);
+ }
+
+ g_simple_async_result_complete (simple);
+
+ soup_message_finished (helper->original);
+
+ g_object_unref (simple);
+ } else {
+ /* Ask again for the resource, somehow the cache cannot locate it */
+ httpstream = soup_http_input_stream_new (session, helper->original);
+ soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+ helper->cancellable, sent_async, simple);
+ }
+ } else {
+ /* It is in the cache but it was modified remotely */
+ httpstream = soup_http_input_stream_new (session, helper->original);
+ soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+ helper->cancellable, sent_async, simple);
+ }
+
+ g_object_unref (helper->req);
+ g_object_unref (helper->original);
+ g_slice_free (ConditionalHelper, helper);
+}
+
+typedef struct {
+ SoupRequestHTTP *http;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+ SoupHTTPInputStream *httpstream;
+} SendAsyncHelper;
+
+static void soup_request_http_send_async (SoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+static gboolean
+send_async_cb (gpointer data)
+{
+ GSimpleAsyncResult *simple;
+ SoupSession *session;
+ SendAsyncHelper *helper = (SendAsyncHelper *)data;
+
+ session = soup_request_get_session (SOUP_REQUEST (helper->http));
+ simple = g_simple_async_result_new (G_OBJECT (helper->http),
+ helper->callback, helper->user_data,
+ soup_request_http_send_async);
+
+ g_simple_async_result_set_op_res_gpointer (simple, helper->httpstream, g_object_unref);
+
+ /* Issue signals */
+ soup_message_got_headers (helper->http->priv->msg);
+
+ if (soup_session_get_feature_for_message (session, SOUP_TYPE_CONTENT_SNIFFER, helper->http->priv->msg)) {
+ const char *content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL);
+ soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL);
+ }
+
+ g_simple_async_result_complete (simple);
+
+ soup_message_finished (helper->http->priv->msg);
+
+ g_object_unref (simple);
+
+ g_object_unref (helper->http);
+ g_slice_free (SendAsyncHelper, helper);
+
+ return FALSE;
+}
+
+static void
+soup_request_http_send_async (SoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
+ SoupHTTPInputStream *httpstream;
+ GSimpleAsyncResult *simple;
+ SoupSession *session;
+ SoupCache *cache;
+
+ session = soup_request_get_session (request);
+ cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
+
+ if (cache) {
+ SoupCacheResponse response;
+
+ response = soup_cache_has_response (cache, http->priv->msg);
+ if (response == SOUP_CACHE_RESPONSE_FRESH) {
+ SoupHTTPInputStream *httpstream;
+
+ httpstream = (SoupHTTPInputStream *)
+ soup_cache_send_response (cache, http->priv->msg);
+
+ /* Cached resource file could have been deleted outside
+ */
+ if (httpstream) {
+ /* Do return the stream asynchronously as in
+ * the other cases. It's not enough to use
+ * g_simple_async_result_complete_in_idle as
+ * the signals must be also emitted
+ * asynchronously
+ */
+ SendAsyncHelper *helper = g_slice_new (SendAsyncHelper);
+ helper->http = g_object_ref (http);
+ helper->callback = callback;
+ helper->user_data = user_data;
+ helper->httpstream = httpstream;
+ soup_add_timeout (soup_session_get_async_context (session),
+ 0, send_async_cb, helper);
+ return;
+ }
+ } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
+ SoupMessage *conditional_msg;
+ ConditionalHelper *helper;
+
+ conditional_msg = soup_cache_generate_conditional_request (cache, http->priv->msg);
+
+ helper = g_slice_new0 (ConditionalHelper);
+ helper->req = g_object_ref (http);
+ helper->original = g_object_ref (http->priv->msg);
+ helper->cancellable = cancellable;
+ helper->callback = callback;
+ helper->user_data = user_data;
+ soup_session_queue_message (session, conditional_msg,
+ conditional_get_ready_cb,
+ helper);
+ return;
+ }
+ }
+
+ simple = g_simple_async_result_new (G_OBJECT (http),
+ callback, user_data,
+ soup_request_http_send_async);
+ httpstream = soup_http_input_stream_new (soup_request_get_session (request),
+ http->priv->msg);
+ soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT,
+ cancellable, sent_async, simple);
+}
+
+static GInputStream *
+soup_request_http_send_finish (SoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), soup_request_http_send_async) || g_simple_async_result_is_valid (result, G_OBJECT (request), conditional_get_ready_cb), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static goffset
+soup_request_http_get_content_length (SoupRequest *request)
+{
+ SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
+
+ return soup_message_headers_get_content_length (http->priv->msg->response_headers);
+}
+
+static const char *
+soup_request_http_get_content_type (SoupRequest *request)
+{
+ SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
+
+ return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL);
+}
+
+static const char *http_schemes[] = { "http", "https", NULL };
+
+static void
+soup_request_http_class_init (SoupRequestHTTPClass *request_http_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (request_http_class);
+ SoupRequestClass *request_class =
+ SOUP_REQUEST_CLASS (request_http_class);
+
+ g_type_class_add_private (request_http_class, sizeof (SoupRequestHTTPPrivate));
+
+ request_class->schemes = http_schemes;
+
+ object_class->finalize = soup_request_http_finalize;
+
+ request_class->check_uri = soup_request_http_check_uri;
+ request_class->send = soup_request_http_send;
+ request_class->send_async = soup_request_http_send_async;
+ request_class->send_finish = soup_request_http_send_finish;
+ request_class->get_content_length = soup_request_http_get_content_length;
+ request_class->get_content_type = soup_request_http_get_content_type;
+}
+
+/**
+ * soup_request_http_get_message:
+ * @http: a #SoupRequestHTTP object
+ *
+ * Gets a new reference to the #SoupMessage associated to this SoupRequest
+ *
+ * Returns: a new reference to the #SoupMessage
+ *
+ * Since: 2.34
+ */
+SoupMessage *
+soup_request_http_get_message (SoupRequestHTTP *http)
+{
+ g_return_val_if_fail (SOUP_IS_REQUEST_HTTP (http), NULL);
+
+ return g_object_ref (http->priv->msg);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SOUP_REQUEST_HTTP_H
+#define SOUP_REQUEST_HTTP_H 1
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "soup-request.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_REQUEST_HTTP (soup_request_http_get_type ())
+#define SOUP_REQUEST_HTTP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTP))
+#define SOUP_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTPClass))
+#define SOUP_IS_REQUEST_HTTP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_REQUEST_HTTP))
+#define SOUP_IS_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST_HTTP))
+#define SOUP_REQUEST_HTTP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST_HTTP, SoupRequestHTTPClass))
+
+typedef struct _SoupRequestHTTPPrivate SoupRequestHTTPPrivate;
+
+typedef struct {
+ SoupRequest parent;
+
+ SoupRequestHTTPPrivate *priv;
+} SoupRequestHTTP;
+
+typedef struct {
+ SoupRequestClass parent;
+} SoupRequestHTTPClass;
+
+GType soup_request_http_get_type (void);
+
+SoupMessage *soup_request_http_get_message (SoupRequestHTTP *http);
+
+G_END_DECLS
+
+#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
+
+#endif /* SOUP_REQUEST_HTTP_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-request.c: Protocol-independent streaming request interface
+ *
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010, Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "soup-request.h"
+#include "soup-requester.h"
+#include "soup-session.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-request
+ * @short_description: Protocol-independent streaming request interface
+ *
+ * FIXME
+ */
+
+/**
+ * SoupRequest:
+ *
+ * FIXME
+ *
+ * Since: 2.34
+ */
+
+static void soup_request_initable_interface_init (GInitableIface *initable_interface);
+
+G_DEFINE_TYPE_WITH_CODE (SoupRequest, soup_request, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ soup_request_initable_interface_init))
+
+enum {
+ PROP_0,
+ PROP_URI,
+ PROP_SESSION
+};
+
+struct _SoupRequestPrivate {
+ SoupURI *uri;
+ SoupSession *session;
+};
+
+static void
+soup_request_init (SoupRequest *request)
+{
+ request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, SOUP_TYPE_REQUEST, SoupRequestPrivate);
+}
+
+static void
+soup_request_finalize (GObject *object)
+{
+ SoupRequest *request = SOUP_REQUEST (object);
+
+ if (request->priv->uri)
+ soup_uri_free (request->priv->uri);
+ if (request->priv->session)
+ g_object_unref (request->priv->session);
+
+ G_OBJECT_CLASS (soup_request_parent_class)->finalize (object);
+}
+
+static void
+soup_request_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SoupRequest *request = SOUP_REQUEST (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ if (request->priv->uri)
+ soup_uri_free (request->priv->uri);
+ request->priv->uri = g_value_dup_boxed (value);
+ break;
+ case PROP_SESSION:
+ if (request->priv->session)
+ g_object_unref (request->priv->session);
+ request->priv->session = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_request_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SoupRequest *request = SOUP_REQUEST (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ g_value_set_boxed (value, request->priv->uri);
+ break;
+ case PROP_SESSION:
+ g_value_set_object (value, request->priv->session);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+soup_request_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupRequest *request = SOUP_REQUEST (initable);
+ gboolean ok;
+
+ if (!request->priv->uri) {
+ g_set_error (error, SOUP_REQUESTER_ERROR, SOUP_REQUESTER_ERROR_BAD_URI,
+ _("No URI provided"));
+ return FALSE;
+ }
+
+ ok = SOUP_REQUEST_GET_CLASS (initable)->
+ check_uri (request, request->priv->uri, error);
+
+ if (!ok && error) {
+ char *uri_string = soup_uri_to_string (request->priv->uri, FALSE);
+ g_set_error (error, SOUP_REQUESTER_ERROR, SOUP_REQUESTER_ERROR_BAD_URI,
+ _("Invalid '%s' URI: %s"),
+ request->priv->uri->scheme,
+ uri_string);
+ g_free (uri_string);
+ }
+
+ return ok;
+}
+
+static gboolean
+soup_request_default_check_uri (SoupRequest *request,
+ SoupURI *uri,
+ GError **error)
+{
+ return TRUE;
+}
+
+/* Default implementation: assume the sync implementation doesn't block */
+static void
+soup_request_default_send_async (SoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (G_OBJECT (request),
+ callback, user_data,
+ soup_request_default_send_async);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+}
+
+static GInputStream *
+soup_request_default_send_finish (SoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), soup_request_default_send_async), NULL);
+
+ return soup_request_send (request, NULL, error);
+}
+
+GInputStream *
+soup_request_send (SoupRequest *request,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return SOUP_REQUEST_GET_CLASS (request)->
+ send (request, cancellable, error);
+}
+
+void
+soup_request_send_async (SoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SOUP_REQUEST_GET_CLASS (request)->
+ send_async (request, cancellable, callback, user_data);
+}
+
+GInputStream *
+soup_request_send_finish (SoupRequest *request,
+ GAsyncResult *result,
+ GError **error)
+{
+ return SOUP_REQUEST_GET_CLASS (request)->
+ send_finish (request, result, error);
+}
+
+static void
+soup_request_class_init (SoupRequestClass *request_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (request_class);
+
+ g_type_class_add_private (request_class, sizeof (SoupRequestPrivate));
+
+ request_class->check_uri = soup_request_default_check_uri;
+ request_class->send_async = soup_request_default_send_async;
+ request_class->send_finish = soup_request_default_send_finish;
+
+ object_class->finalize = soup_request_finalize;
+ object_class->set_property = soup_request_set_property;
+ object_class->get_property = soup_request_get_property;
+
+ g_object_class_install_property (
+ object_class, PROP_URI,
+ g_param_spec_boxed (SOUP_REQUEST_URI,
+ "URI",
+ "The request URI",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_SESSION,
+ g_param_spec_object (SOUP_REQUEST_SESSION,
+ "Session",
+ "The request's session",
+ SOUP_TYPE_SESSION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+soup_request_initable_interface_init (GInitableIface *initable_interface)
+{
+ initable_interface->init = soup_request_initable_init;
+}
+
+SoupURI *
+soup_request_get_uri (SoupRequest *request)
+{
+ return request->priv->uri;
+}
+
+SoupSession *
+soup_request_get_session (SoupRequest *request)
+{
+ return request->priv->session;
+}
+
+goffset
+soup_request_get_content_length (SoupRequest *request)
+{
+ return SOUP_REQUEST_GET_CLASS (request)->get_content_length (request);
+}
+
+const char *
+soup_request_get_content_type (SoupRequest *request)
+{
+ return SOUP_REQUEST_GET_CLASS (request)->get_content_type (request);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SOUP_REQUEST_H
+#define SOUP_REQUEST_H 1
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include <gio/gio.h>
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_REQUEST (soup_request_get_type ())
+#define SOUP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_REQUEST, SoupRequest))
+#define SOUP_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUEST, SoupRequestClass))
+#define SOUP_IS_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_REQUEST))
+#define SOUP_IS_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST))
+#define SOUP_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST, SoupRequestClass))
+
+typedef struct _SoupRequest SoupRequest;
+typedef struct _SoupRequestPrivate SoupRequestPrivate;
+typedef struct _SoupRequestClass SoupRequestClass;
+
+struct _SoupRequest {
+ GObject parent;
+
+ SoupRequestPrivate *priv;
+};
+
+struct _SoupRequestClass {
+ GObjectClass parent;
+
+ const char **schemes;
+
+ gboolean (*check_uri) (SoupRequest *req_base,
+ SoupURI *uri,
+ GError **error);
+
+ GInputStream * (*send) (SoupRequest *request,
+ GCancellable *cancellable,
+ GError **error);
+ void (*send_async) (SoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GInputStream * (*send_finish) (SoupRequest *request,
+ GAsyncResult *result,
+ GError **error);
+
+ goffset (*get_content_length) (SoupRequest *request);
+ const char * (*get_content_type) (SoupRequest *request);
+};
+
+GType soup_request_get_type (void);
+
+#define SOUP_REQUEST_URI "uri"
+#define SOUP_REQUEST_SESSION "session"
+
+GInputStream *soup_request_send (SoupRequest *request,
+ GCancellable *cancellable,
+ GError **error);
+void soup_request_send_async (SoupRequest *request,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GInputStream *soup_request_send_finish (SoupRequest *request,
+ GAsyncResult *result,
+ GError **error);
+
+SoupURI *soup_request_get_uri (SoupRequest *request);
+SoupSession *soup_request_get_session (SoupRequest *request);
+
+goffset soup_request_get_content_length (SoupRequest *request);
+const char *soup_request_get_content_type (SoupRequest *request);
+
+G_END_DECLS
+
+#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
+
+#endif /* SOUP_REQUEST_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-requester.c:
+ *
+ * Copyright (C) 2010, Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include "soup-requester.h"
+#include "soup-request-data.h"
+#include "soup-request-file.h"
+#include "soup-request-http.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+static SoupSessionFeatureInterface *soup_requester_default_feature_interface;
+static void soup_requester_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+struct _SoupRequesterPrivate {
+ SoupSession *session;
+ GHashTable *request_types;
+};
+
+G_DEFINE_TYPE_WITH_CODE (SoupRequester, soup_requester, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_requester_session_feature_init))
+
+static void
+soup_requester_init (SoupRequester *requester)
+{
+ SoupSessionFeature *feature;
+
+ requester->priv = G_TYPE_INSTANCE_GET_PRIVATE (requester,
+ SOUP_TYPE_REQUESTER,
+ SoupRequesterPrivate);
+
+ requester->priv->request_types = g_hash_table_new (soup_str_case_hash,
+ soup_str_case_equal);
+
+ feature = SOUP_SESSION_FEATURE (requester);
+ soup_session_feature_add_feature (feature, SOUP_TYPE_REQUEST_HTTP);
+ soup_session_feature_add_feature (feature, SOUP_TYPE_REQUEST_FILE);
+ soup_session_feature_add_feature (feature, SOUP_TYPE_REQUEST_DATA);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupRequester *requester = SOUP_REQUESTER (object);
+
+ g_hash_table_destroy (requester->priv->request_types);
+
+ G_OBJECT_CLASS (soup_requester_parent_class)->finalize (object);
+}
+
+static void
+soup_requester_class_init (SoupRequesterClass *requester_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (requester_class);
+
+ g_type_class_add_private (requester_class, sizeof (SoupRequesterPrivate));
+
+ /* virtual method override */
+ object_class->finalize = finalize;
+}
+
+static void
+attach (SoupSessionFeature *feature, SoupSession *session)
+{
+ SoupRequester *requester = SOUP_REQUESTER (feature);
+
+ requester->priv->session = session;
+
+ soup_requester_default_feature_interface->attach (feature, session);
+}
+
+static void
+detach (SoupSessionFeature *feature, SoupSession *session)
+{
+ SoupRequester *requester = SOUP_REQUESTER (feature);
+
+ requester->priv->session = NULL;
+
+ soup_requester_default_feature_interface->detach (feature, session);
+}
+
+static gboolean
+add_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupRequester *requester = SOUP_REQUESTER (feature);
+ SoupRequestClass *request_class;
+ int i;
+
+ if (!g_type_is_a (type, SOUP_TYPE_REQUEST))
+ return FALSE;
+
+ request_class = g_type_class_ref (type);
+ for (i = 0; request_class->schemes[i]; i++) {
+ g_hash_table_insert (requester->priv->request_types,
+ (char *)request_class->schemes[i],
+ GSIZE_TO_POINTER (type));
+ }
+ return TRUE;
+}
+
+static gboolean
+remove_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupRequester *requester = SOUP_REQUESTER (feature);
+ SoupRequestClass *request_class;
+ int i, orig_size;
+
+ if (!g_type_is_a (type, SOUP_TYPE_REQUEST))
+ return FALSE;
+
+ request_class = g_type_class_peek (type);
+ if (!request_class)
+ return FALSE;
+
+ orig_size = g_hash_table_size (requester->priv->request_types);
+ for (i = 0; request_class->schemes[i]; i++) {
+ g_hash_table_remove (requester->priv->request_types,
+ request_class->schemes[i]);
+ }
+
+ return g_hash_table_size (requester->priv->request_types) != orig_size;
+}
+
+static gboolean
+has_feature (SoupSessionFeature *feature, GType type)
+{
+ SoupRequester *requester = SOUP_REQUESTER (feature);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if (!g_type_is_a (type, SOUP_TYPE_REQUEST))
+ return FALSE;
+
+ g_hash_table_iter_init (&iter, requester->priv->request_types);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (value == GSIZE_TO_POINTER (type))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+soup_requester_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ soup_requester_default_feature_interface =
+ g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
+
+ feature_interface->attach = attach;
+ feature_interface->detach = detach;
+ feature_interface->add_feature = add_feature;
+ feature_interface->remove_feature = remove_feature;
+ feature_interface->has_feature = has_feature;
+}
+
+SoupRequester *
+soup_requester_new (void)
+{
+ return g_object_new (SOUP_TYPE_REQUESTER, NULL);
+}
+
+SoupRequest *
+soup_requester_request (SoupRequester *requester, const char *uri_string,
+ GError **error)
+{
+ SoupURI *uri;
+ SoupRequest *req;
+
+ uri = soup_uri_new (uri_string);
+ if (!uri) {
+ g_set_error (error, SOUP_REQUESTER_ERROR, SOUP_REQUESTER_ERROR_BAD_URI,
+ _("Could not parse URI '%s'"), uri_string);
+ return NULL;
+ }
+
+ req = soup_requester_request_uri (requester, uri, error);
+ soup_uri_free (uri);
+ return req;
+}
+
+SoupRequest *
+soup_requester_request_uri (SoupRequester *requester, SoupURI *uri,
+ GError **error)
+{
+ GType request_type;
+
+ g_return_val_if_fail (SOUP_IS_REQUESTER (requester), NULL);
+
+ request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (requester->priv->request_types, uri->scheme));
+ if (!request_type) {
+ g_set_error (error, SOUP_REQUESTER_ERROR,
+ SOUP_REQUESTER_ERROR_UNSUPPORTED_URI_SCHEME,
+ _("Unsupported URI scheme '%s'"), uri->scheme);
+ return NULL;
+ }
+
+ return g_initable_new (request_type, NULL, error,
+ "uri", uri,
+ "session", requester->priv->session,
+ NULL);
+}
+
+GQuark
+soup_requester_error_quark (void)
+{
+ static GQuark error;
+ if (!error)
+ error = g_quark_from_static_string ("soup_requester_error_quark");
+ return error;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SOUP_REQUESTER_H
+#define SOUP_REQUESTER_H 1
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-request.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_REQUESTER (soup_requester_get_type ())
+#define SOUP_REQUESTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_REQUESTER, SoupRequester))
+#define SOUP_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_REQUESTER, SoupRequesterClass))
+#define SOUP_IS_REQUESTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_REQUESTER))
+#define SOUP_IS_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_REQUESTER))
+#define SOUP_REQUESTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUESTER, SoupRequesterClass))
+
+typedef struct _SoupRequester SoupRequester;
+typedef struct _SoupRequesterPrivate SoupRequesterPrivate;
+
+struct _SoupRequester {
+ GObject parent;
+
+ SoupRequesterPrivate *priv;
+};
+
+typedef struct {
+ GObjectClass parent_class;
+} SoupRequesterClass;
+
+GType soup_requester_get_type (void);
+
+SoupRequester *soup_requester_new (void);
+
+SoupRequest *soup_requester_request (SoupRequester *requester,
+ const char *uri_string,
+ GError **error);
+
+SoupRequest *soup_requester_request_uri (SoupRequester *requester,
+ SoupURI *uri,
+ GError **error);
+
+GQuark soup_requester_error_quark (void);
+#define SOUP_REQUESTER_ERROR soup_requester_error_quark ()
+
+typedef enum {
+ SOUP_REQUESTER_ERROR_BAD_URI,
+ SOUP_REQUESTER_ERROR_UNSUPPORTED_URI_SCHEME
+} SoupRequesterError;
+
+G_END_DECLS
+
+#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
+
+#endif /* SOUP_REQUESTER_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-server.c: Asynchronous HTTP server
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "soup-server.h"
+#include "soup-address.h"
+#include "soup-auth-domain.h"
+#include "soup-date.h"
+#include "soup-form.h"
+#include "soup-headers.h"
+#include "soup-message-private.h"
+#include "soup-marshal.h"
+#include "soup-path-map.h"
+#include "soup-socket.h"
+#include "soup-ssl.h"
+
+/**
+ * SECTION:soup-server
+ * @short_description: HTTP server
+ * @see_also: #SoupAuthDomain
+ *
+ * #SoupServer implements a simple HTTP server.
+ *
+ * To begin, create a server using soup_server_new(). Add at least one
+ * handler by calling soup_server_add_handler(); the handler will be
+ * called to process any requests underneath the path passed to
+ * soup_server_add_handler(). (If you want all requests to go to the
+ * same handler, just pass "/" (or %NULL) for the path.) Any request
+ * that does not match any handler will automatically be returned to
+ * the client with a 404 (Not Found) status.
+ *
+ * If you want to handle the special "*" URI (eg, "OPTIONS *"), you
+ * must explicitly register a handler for "*"; the default handler
+ * will not be used for that case.
+ *
+ * To add authentication to some or all paths, create an appropriate
+ * #SoupAuthDomain (qv), and add it to the server via
+ * soup_server_add_auth_domain(). (As with handlers, you must
+ * explicitly add "*" to an auth domain if you want it to be covered.)
+ *
+ * Additional processing options are available via #SoupServer's
+ * signals; Connect to #SoupServer::request-started to be notified
+ * every time a new request is being processed. (This gives you a
+ * chance to connect to the #SoupMessage "got-" signals in case you
+ * want to do processing before the body has been fully read.)
+ *
+ * Once the server is set up, start it processing connections by
+ * calling soup_server_run_async() or soup_server_run(). #SoupServer
+ * runs via the glib main loop; if you need to have a server that runs
+ * in another thread (or merely isn't bound to the default main loop),
+ * create a #GMainContext for it to use, and set that via the
+ * #SOUP_SERVER_ASYNC_CONTEXT property.
+ **/
+
+G_DEFINE_TYPE (SoupServer, soup_server, G_TYPE_OBJECT)
+
+enum {
+ REQUEST_STARTED,
+ REQUEST_READ,
+ REQUEST_FINISHED,
+ REQUEST_ABORTED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct SoupClientContext {
+ SoupServer *server;
+ SoupSocket *sock;
+ SoupMessage *msg;
+ SoupAuthDomain *auth_domain;
+ char *auth_user;
+
+ int ref_count;
+};
+
+typedef struct {
+ char *path;
+
+ SoupServerCallback callback;
+ GDestroyNotify destroy;
+ gpointer user_data;
+} SoupServerHandler;
+
+typedef struct {
+ SoupAddress *iface;
+ guint port;
+
+ char *ssl_cert_file, *ssl_key_file;
+ SoupSSLCredentials *ssl_creds;
+
+ char *server_header;
+
+ GMainLoop *loop;
+
+ SoupSocket *listen_sock;
+ GSList *clients;
+
+ gboolean raw_paths;
+ SoupPathMap *handlers;
+ SoupServerHandler *default_handler;
+
+ GSList *auth_domains;
+
+ GMainContext *async_context;
+} SoupServerPrivate;
+#define SOUP_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SERVER, SoupServerPrivate))
+
+#define SOUP_SERVER_SERVER_HEADER_BASE "libsoup/" PACKAGE_VERSION
+
+enum {
+ PROP_0,
+
+ PROP_PORT,
+ PROP_INTERFACE,
+ PROP_SSL_CERT_FILE,
+ PROP_SSL_KEY_FILE,
+ PROP_ASYNC_CONTEXT,
+ PROP_RAW_PATHS,
+ PROP_SERVER_HEADER,
+
+ LAST_PROP
+};
+
+static GObject *constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties);
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+free_handler (SoupServerHandler *hand)
+{
+ g_free (hand->path);
+ g_slice_free (SoupServerHandler, hand);
+}
+
+static void
+soup_server_init (SoupServer *server)
+{
+ SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ priv->handlers = soup_path_map_new ((GDestroyNotify)free_handler);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupServer *server = SOUP_SERVER (object);
+ SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+ GSList *iter;
+
+ if (priv->iface)
+ g_object_unref (priv->iface);
+
+ g_free (priv->ssl_cert_file);
+ g_free (priv->ssl_key_file);
+ if (priv->ssl_creds)
+ soup_ssl_free_server_credentials (priv->ssl_creds);
+
+ g_free (priv->server_header);
+
+ if (priv->listen_sock)
+ g_object_unref (priv->listen_sock);
+
+ while (priv->clients) {
+ SoupClientContext *client = priv->clients->data;
+ SoupSocket *sock = g_object_ref (client->sock);
+
+ priv->clients = g_slist_remove (priv->clients, client);
+
+ if (client->msg) {
+ soup_message_set_status (client->msg, SOUP_STATUS_IO_ERROR);
+ soup_message_io_finished (client->msg);
+ }
+
+ soup_socket_disconnect (sock);
+ g_object_unref (sock);
+ }
+
+ if (priv->default_handler)
+ free_handler (priv->default_handler);
+ soup_path_map_free (priv->handlers);
+
+ for (iter = priv->auth_domains; iter; iter = iter->next)
+ g_object_unref (iter->data);
+ g_slist_free (priv->auth_domains);
+
+ if (priv->loop)
+ g_main_loop_unref (priv->loop);
+ if (priv->async_context)
+ g_main_context_unref (priv->async_context);
+
+ G_OBJECT_CLASS (soup_server_parent_class)->finalize (object);
+}
+
+static void
+soup_server_class_init (SoupServerClass *server_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (server_class);
+
+ g_type_class_add_private (server_class, sizeof (SoupServerPrivate));
+
+ /* virtual method override */
+ object_class->constructor = constructor;
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /* signals */
+
+ /**
+ * SoupServer::request-started
+ * @server: the server
+ * @message: the new message
+ * @client: the client context
+ *
+ * Emitted when the server has started reading a new request.
+ * @message will be completely blank; not even the
+ * Request-Line will have been read yet. About the only thing
+ * you can usefully do with it is connect to its signals.
+ *
+ * If the request is read successfully, this will eventually
+ * be followed by a #SoupServer::request_read signal. If a
+ * response is then sent, the request processing will end with
+ * a #SoupServer::request_finished signal. If a network error
+ * occurs, the processing will instead end with
+ * #SoupServer::request_aborted.
+ **/
+ signals[REQUEST_STARTED] =
+ g_signal_new ("request-started",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupServerClass, request_started),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_POINTER,
+ G_TYPE_NONE, 2,
+ SOUP_TYPE_MESSAGE,
+ SOUP_TYPE_CLIENT_CONTEXT);
+
+ /**
+ * SoupServer::request-read
+ * @server: the server
+ * @message: the message
+ * @client: the client context
+ *
+ * Emitted when the server has successfully read a request.
+ * @message will have all of its request-side information
+ * filled in, and if the message was authenticated, @client
+ * will have information about that. This signal is emitted
+ * before any handlers are called for the message, and if it
+ * sets the message's #status_code, then normal handler
+ * processing will be skipped.
+ **/
+ signals[REQUEST_READ] =
+ g_signal_new ("request-read",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupServerClass, request_read),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_POINTER,
+ G_TYPE_NONE, 2,
+ SOUP_TYPE_MESSAGE,
+ SOUP_TYPE_CLIENT_CONTEXT);
+
+ /**
+ * SoupServer::request-finished
+ * @server: the server
+ * @message: the message
+ * @client: the client context
+ *
+ * Emitted when the server has finished writing a response to
+ * a request.
+ **/
+ signals[REQUEST_FINISHED] =
+ g_signal_new ("request-finished",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupServerClass, request_finished),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_POINTER,
+ G_TYPE_NONE, 2,
+ SOUP_TYPE_MESSAGE,
+ SOUP_TYPE_CLIENT_CONTEXT);
+
+ /**
+ * SoupServer::request-aborted
+ * @server: the server
+ * @message: the message
+ * @client: the client context
+ *
+ * Emitted when processing has failed for a message; this
+ * could mean either that it could not be read (if
+ * #SoupServer::request_read has not been emitted for it yet),
+ * or that the response could not be written back (if
+ * #SoupServer::request_read has been emitted but
+ * #SoupServer::request_finished has not been).
+ *
+ * @message is in an undefined state when this signal is
+ * emitted; the signal exists primarily to allow the server to
+ * free any state that it may have allocated in
+ * #SoupServer::request_started.
+ **/
+ signals[REQUEST_ABORTED] =
+ g_signal_new ("request-aborted",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupServerClass, request_aborted),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_POINTER,
+ G_TYPE_NONE, 2,
+ SOUP_TYPE_MESSAGE,
+ SOUP_TYPE_CLIENT_CONTEXT);
+
+ /* properties */
+ /**
+ * SOUP_SERVER_PORT:
+ *
+ * Alias for the #SoupServer:port property. (The port the
+ * server listens on.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_PORT,
+ g_param_spec_uint (SOUP_SERVER_PORT,
+ "Port",
+ "Port to listen on",
+ 0, 65536, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SERVER_INTERFACE:
+ *
+ * Alias for the #SoupServer:interface property. (The address
+ * of the network interface the server listens on.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_INTERFACE,
+ g_param_spec_object (SOUP_SERVER_INTERFACE,
+ "Interface",
+ "Address of interface to listen on",
+ SOUP_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SERVER_SSL_CERT_FILE:
+ *
+ * Alias for the #SoupServer:ssl-cert-file property. (The file
+ * containing the SSL certificate for the server.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SSL_CERT_FILE,
+ g_param_spec_string (SOUP_SERVER_SSL_CERT_FILE,
+ "SSL certificate file",
+ "File containing server SSL certificate",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SERVER_SSL_KEY_FILE:
+ *
+ * Alias for the #SoupServer:ssl-key-file property. (The file
+ * containing the SSL certificate key for the server.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SSL_KEY_FILE,
+ g_param_spec_string (SOUP_SERVER_SSL_KEY_FILE,
+ "SSL key file",
+ "File containing server SSL key",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SERVER_ASYNC_CONTEXT:
+ *
+ * Alias for the #SoupServer:async-context property. (The
+ * server's #GMainContext.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_ASYNC_CONTEXT,
+ g_param_spec_pointer (SOUP_SERVER_ASYNC_CONTEXT,
+ "Async GMainContext",
+ "The GMainContext to dispatch async I/O in",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SERVER_RAW_PATHS:
+ *
+ * Alias for the #SoupServer:raw-paths property. (If %TRUE,
+ * percent-encoding in the Request-URI path will not be
+ * automatically decoded.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_RAW_PATHS,
+ g_param_spec_boolean (SOUP_SERVER_RAW_PATHS,
+ "Raw paths",
+ "If %TRUE, percent-encoding in the Request-URI path will not be automatically decoded.",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * SoupServer:server-header:
+ *
+ * If non-%NULL, the value to use for the "Server" header on
+ * #SoupMessage<!-- -->s processed by this server.
+ *
+ * The Server header is the server equivalent of the
+ * User-Agent header, and provides information about the
+ * server and its components. It contains a list of one or
+ * more product tokens, separated by whitespace, with the most
+ * significant product token coming first. The tokens must be
+ * brief, ASCII, and mostly alphanumeric (although "-", "_",
+ * and "." are also allowed), and may optionally include a "/"
+ * followed by a version string. You may also put comments,
+ * enclosed in parentheses, between or after the tokens.
+ *
+ * Some HTTP server implementations intentionally do not use
+ * version numbers in their Server header, so that
+ * installations running older versions of the server don't
+ * end up advertising their vulnerability to specific security
+ * holes.
+ *
+ * As with #SoupSession:user_agent, if you set a
+ * %server_header property that has trailing whitespace,
+ * #SoupServer will append its own product token (eg,
+ * "<literal>libsoup/2.3.2</literal>") to the end of the
+ * header for you.
+ **/
+ /**
+ * SOUP_SERVER_SERVER_HEADER:
+ *
+ * Alias for the #SoupServer:server-header property, qv.
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SERVER_HEADER,
+ g_param_spec_string (SOUP_SERVER_SERVER_HEADER,
+ "Server header",
+ "Server header",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static GObject *
+constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GObject *server;
+ SoupServerPrivate *priv;
+
+ server = G_OBJECT_CLASS (soup_server_parent_class)->constructor (
+ type, n_construct_properties, construct_properties);
+ if (!server)
+ return NULL;
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ if (!priv->iface) {
+ priv->iface =
+ soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4,
+ priv->port);
+ }
+
+ if (priv->ssl_cert_file && priv->ssl_key_file) {
+ priv->ssl_creds = soup_ssl_get_server_credentials (
+ priv->ssl_cert_file,
+ priv->ssl_key_file);
+ if (!priv->ssl_creds) {
+ g_object_unref (server);
+ return NULL;
+ }
+ }
+
+ priv->listen_sock =
+ soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, priv->iface,
+ SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+ SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
+ NULL);
+ if (!soup_socket_listen (priv->listen_sock)) {
+ g_object_unref (server);
+ return NULL;
+ }
+
+ /* Re-resolve the interface address, in particular in case
+ * the passed-in address had SOUP_ADDRESS_ANY_PORT.
+ */
+ g_object_unref (priv->iface);
+ priv->iface = soup_socket_get_local_address (priv->listen_sock);
+ g_object_ref (priv->iface);
+ priv->port = soup_address_get_port (priv->iface);
+
+ return server;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (object);
+ const char *header;
+
+ switch (prop_id) {
+ case PROP_PORT:
+ priv->port = g_value_get_uint (value);
+ break;
+ case PROP_INTERFACE:
+ if (priv->iface)
+ g_object_unref (priv->iface);
+ priv->iface = g_value_get_object (value);
+ if (priv->iface)
+ g_object_ref (priv->iface);
+ break;
+ case PROP_SSL_CERT_FILE:
+ priv->ssl_cert_file =
+ g_strdup (g_value_get_string (value));
+ break;
+ case PROP_SSL_KEY_FILE:
+ priv->ssl_key_file =
+ g_strdup (g_value_get_string (value));
+ break;
+ case PROP_ASYNC_CONTEXT:
+ priv->async_context = g_value_get_pointer (value);
+ if (priv->async_context)
+ g_main_context_ref (priv->async_context);
+ break;
+ case PROP_RAW_PATHS:
+ priv->raw_paths = g_value_get_boolean (value);
+ break;
+ case PROP_SERVER_HEADER:
+ g_free (priv->server_header);
+ header = g_value_get_string (value);
+ if (!header)
+ priv->server_header = NULL;
+ else if (!*header) {
+ priv->server_header =
+ g_strdup (SOUP_SERVER_SERVER_HEADER_BASE);
+ } else if (g_str_has_suffix (header, " ")) {
+ priv->server_header =
+ g_strdup_printf ("%s%s", header,
+ SOUP_SERVER_SERVER_HEADER_BASE);
+ } else
+ priv->server_header = g_strdup (header);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_PORT:
+ g_value_set_uint (value, priv->port);
+ break;
+ case PROP_INTERFACE:
+ g_value_set_object (value, priv->iface);
+ break;
+ case PROP_SSL_CERT_FILE:
+ g_value_set_string (value, priv->ssl_cert_file);
+ break;
+ case PROP_SSL_KEY_FILE:
+ g_value_set_string (value, priv->ssl_key_file);
+ break;
+ case PROP_ASYNC_CONTEXT:
+ g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
+ break;
+ case PROP_RAW_PATHS:
+ g_value_set_boolean (value, priv->raw_paths);
+ break;
+ case PROP_SERVER_HEADER:
+ g_value_set_string (value, priv->server_header);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_server_new:
+ * @optname1: name of first property to set
+ * @...: value of @optname1, followed by additional property/value pairs
+ *
+ * Creates a new #SoupServer.
+ *
+ * Return value: a new #SoupServer
+ **/
+SoupServer *
+soup_server_new (const char *optname1, ...)
+{
+ SoupServer *server;
+ va_list ap;
+
+ va_start (ap, optname1);
+ server = (SoupServer *)g_object_new_valist (SOUP_TYPE_SERVER,
+ optname1, ap);
+ va_end (ap);
+
+ return server;
+}
+
+/**
+ * soup_server_get_port:
+ * @server: a #SoupServer
+ *
+ * Gets the TCP port that @server is listening on. This is most useful
+ * when you did not request a specific port (or explicitly requested
+ * %SOUP_ADDRESS_ANY_PORT).
+ *
+ * Return value: the port @server is listening on.
+ **/
+guint
+soup_server_get_port (SoupServer *server)
+{
+ g_return_val_if_fail (SOUP_IS_SERVER (server), 0);
+
+ return SOUP_SERVER_GET_PRIVATE (server)->port;
+}
+
+/**
+ * soup_server_is_https:
+ * @server: a #SoupServer
+ *
+ * Checks whether @server is running plain http or https.
+ *
+ * In order for a server to run https, you must set the
+ * %SOUP_SERVER_SSL_CERT_FILE and %SOUP_SERVER_SSL_KEY_FILE properties
+ * to provide it with an SSL certificate to use.
+ *
+ * Return value: %TRUE if @server is serving https.
+ **/
+gboolean
+soup_server_is_https (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SERVER (server), 0);
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ return (priv->ssl_cert_file && priv->ssl_key_file);
+}
+
+/**
+ * soup_server_get_listener:
+ * @server: a #SoupServer
+ *
+ * Gets @server's listening socket. You should treat this as
+ * read-only; writing to it or modifiying it may cause @server to
+ * malfunction.
+ *
+ * Return value: (transfer none): the listening socket.
+ **/
+SoupSocket *
+soup_server_get_listener (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ return priv->listen_sock;
+}
+
+static void start_request (SoupServer *, SoupClientContext *);
+
+static SoupClientContext *
+soup_client_context_new (SoupServer *server, SoupSocket *sock)
+{
+ SoupClientContext *client = g_slice_new0 (SoupClientContext);
+
+ client->server = server;
+ client->sock = sock;
+ client->ref_count = 1;
+
+ return client;
+}
+
+static void
+soup_client_context_cleanup (SoupClientContext *client)
+{
+ if (client->auth_domain) {
+ g_object_unref (client->auth_domain);
+ client->auth_domain = NULL;
+ }
+ if (client->auth_user) {
+ g_free (client->auth_user);
+ client->auth_user = NULL;
+ }
+ client->msg = NULL;
+}
+
+static SoupClientContext *
+soup_client_context_ref (SoupClientContext *client)
+{
+ client->ref_count++;
+ return client;
+}
+
+static void
+soup_client_context_unref (SoupClientContext *client)
+{
+ if (--client->ref_count == 0) {
+ soup_client_context_cleanup (client);
+ g_slice_free (SoupClientContext, client);
+ }
+}
+
+static void
+request_finished (SoupMessage *msg, gpointer user_data)
+{
+ SoupClientContext *client = user_data;
+ SoupServer *server = client->server;
+ SoupSocket *sock = client->sock;
+
+ soup_message_finished (msg);
+ g_signal_emit (server,
+ msg->status_code == SOUP_STATUS_IO_ERROR ?
+ signals[REQUEST_ABORTED] : signals[REQUEST_FINISHED],
+ 0, msg, client);
+
+ soup_client_context_cleanup (client);
+ if (soup_socket_is_connected (sock) && soup_message_is_keepalive (msg)) {
+ /* Start a new request */
+ start_request (server, client);
+ } else {
+ soup_socket_disconnect (sock);
+ soup_client_context_unref (client);
+ }
+ g_object_unref (msg);
+ g_object_unref (sock);
+}
+
+static SoupServerHandler *
+soup_server_get_handler (SoupServer *server, const char *path)
+{
+ SoupServerPrivate *priv;
+ SoupServerHandler *hand;
+
+ g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ if (path) {
+ hand = soup_path_map_lookup (priv->handlers, path);
+ if (hand)
+ return hand;
+ if (!strcmp (path, "*"))
+ return NULL;
+ }
+ return priv->default_handler;
+}
+
+static void
+got_headers (SoupMessage *req, SoupClientContext *client)
+{
+ SoupServer *server = client->server;
+ SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+ SoupURI *uri;
+ SoupDate *date;
+ char *date_string;
+ SoupAuthDomain *domain;
+ GSList *iter;
+ gboolean rejected = FALSE;
+ char *auth_user;
+
+ if (!priv->raw_paths) {
+ char *decoded_path;
+
+ uri = soup_message_get_uri (req);
+ decoded_path = soup_uri_decode (uri->path);
+
+ if (strstr (decoded_path, "/../") ||
+ g_str_has_suffix (decoded_path, "/..")) {
+ /* Introducing new ".." segments is not allowed */
+ g_free (decoded_path);
+ soup_message_set_status (req, SOUP_STATUS_BAD_REQUEST);
+ return;
+ }
+
+ soup_uri_set_path (uri, decoded_path);
+ g_free (decoded_path);
+ }
+
+ /* Add required response headers */
+ date = soup_date_new_from_now (0);
+ date_string = soup_date_to_string (date, SOUP_DATE_HTTP);
+ soup_message_headers_replace (req->response_headers, "Date",
+ date_string);
+ g_free (date_string);
+ soup_date_free (date);
+
+ /* Now handle authentication. (We do this here so that if
+ * the request uses "Expect: 100-continue", we can reject it
+ * immediately rather than waiting for the request body to
+ * be sent.
+ */
+ for (iter = priv->auth_domains; iter; iter = iter->next) {
+ domain = iter->data;
+
+ if (soup_auth_domain_covers (domain, req)) {
+ auth_user = soup_auth_domain_accepts (domain, req);
+ if (auth_user) {
+ client->auth_domain = g_object_ref (domain);
+ client->auth_user = auth_user;
+ return;
+ }
+
+ rejected = TRUE;
+ }
+ }
+
+ /* If no auth domain rejected it, then it's ok. */
+ if (!rejected)
+ return;
+
+ for (iter = priv->auth_domains; iter; iter = iter->next) {
+ domain = iter->data;
+
+ if (soup_auth_domain_covers (domain, req))
+ soup_auth_domain_challenge (domain, req);
+ }
+}
+
+static void
+call_handler (SoupMessage *req, SoupClientContext *client)
+{
+ SoupServer *server = client->server;
+ SoupServerHandler *hand;
+ SoupURI *uri;
+
+ g_signal_emit (server, signals[REQUEST_READ], 0, req, client);
+
+ if (req->status_code != 0)
+ return;
+
+ uri = soup_message_get_uri (req);
+ hand = soup_server_get_handler (server, uri->path);
+ if (!hand) {
+ soup_message_set_status (req, SOUP_STATUS_NOT_FOUND);
+ return;
+ }
+
+ if (hand->callback) {
+ GHashTable *form_data_set;
+
+ if (uri->query)
+ form_data_set = soup_form_decode (uri->query);
+ else
+ form_data_set = NULL;
+
+ /* Call method handler */
+ (*hand->callback) (server, req,
+ uri->path, form_data_set,
+ client, hand->user_data);
+
+ if (form_data_set)
+ g_hash_table_destroy (form_data_set);
+ }
+}
+
+static void
+start_request (SoupServer *server, SoupClientContext *client)
+{
+ SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+ SoupMessage *msg;
+
+ soup_client_context_cleanup (client);
+
+ /* Listen for another request on this connection */
+ msg = g_object_new (SOUP_TYPE_MESSAGE,
+ SOUP_MESSAGE_SERVER_SIDE, TRUE,
+ NULL);
+ client->msg = msg;
+
+ if (priv->server_header) {
+ soup_message_headers_append (msg->response_headers, "Server",
+ priv->server_header);
+ }
+
+ g_signal_connect (msg, "got_headers", G_CALLBACK (got_headers), client);
+ g_signal_connect (msg, "got_body", G_CALLBACK (call_handler), client);
+
+ g_signal_emit (server, signals[REQUEST_STARTED], 0,
+ msg, client);
+
+ g_object_ref (client->sock);
+ soup_message_read_request (msg, client->sock,
+ request_finished, client);
+}
+
+static void
+socket_disconnected (SoupSocket *sock, SoupClientContext *client)
+{
+ SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (client->server);
+
+ priv->clients = g_slist_remove (priv->clients, client);
+ g_signal_handlers_disconnect_by_func (sock, socket_disconnected, client);
+ g_object_unref (sock);
+}
+
+static void
+new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data)
+{
+ SoupServer *server = user_data;
+ SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+ SoupClientContext *client;
+
+ client = soup_client_context_new (server, g_object_ref (sock));
+ priv->clients = g_slist_prepend (priv->clients, client);
+ g_signal_connect (sock, "disconnected",
+ G_CALLBACK (socket_disconnected), client);
+ start_request (server, client);
+}
+
+/**
+ * soup_server_run_async:
+ * @server: a #SoupServer
+ *
+ * Starts @server, causing it to listen for and process incoming
+ * connections.
+ *
+ * The server actually runs in @server's #GMainContext. It will not
+ * actually perform any processing unless the appropriate main loop is
+ * running. In the simple case where you did not set the server's
+ * %SOUP_SERVER_ASYNC_CONTEXT property, this means the server will run
+ * whenever the glib main loop is running.
+ **/
+void
+soup_server_run_async (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ if (!priv->listen_sock) {
+ if (priv->loop) {
+ g_main_loop_unref (priv->loop);
+ priv->loop = NULL;
+ }
+ return;
+ }
+
+ g_signal_connect (priv->listen_sock, "new_connection",
+ G_CALLBACK (new_connection), server);
+
+ return;
+
+}
+
+/**
+ * soup_server_run:
+ * @server: a #SoupServer
+ *
+ * Starts @server, causing it to listen for and process incoming
+ * connections. Unlike soup_server_run_async(), this creates a
+ * #GMainLoop and runs it, and it will not return until someone calls
+ * soup_server_quit() to stop the server.
+ **/
+void
+soup_server_run (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ if (!priv->loop) {
+ priv->loop = g_main_loop_new (priv->async_context, TRUE);
+ soup_server_run_async (server);
+ }
+
+ if (priv->loop)
+ g_main_loop_run (priv->loop);
+}
+
+/**
+ * soup_server_quit:
+ * @server: a #SoupServer
+ *
+ * Stops processing for @server. Call this to clean up after
+ * soup_server_run_async(), or to terminate a call to soup_server_run().
+ *
+ * @server is still in a working state after this call; you can start
+ * and stop a server as many times as you want.
+ **/
+void
+soup_server_quit (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ g_signal_handlers_disconnect_by_func (priv->listen_sock,
+ G_CALLBACK (new_connection),
+ server);
+ if (priv->loop)
+ g_main_loop_quit (priv->loop);
+}
+
+/**
+ * soup_server_disconnect:
+ * @server: a #SoupServer
+ *
+ * Stops processing for @server and closes its socket. This implies
+ * the effects of soup_server_quit(), but additionally closes the
+ * listening socket. Note that messages currently in progress will
+ * continue to be handled, if the main loop associated with the
+ * server is resumed or kept running.
+ *
+ * After calling this function, @server is no longer functional, so it
+ * has nearly the same effect as destroying @server entirely. The
+ * function is thus useful mainly for language bindings without
+ * explicit control over object lifetime.
+ **/
+void
+soup_server_disconnect (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ soup_server_quit (server);
+
+ if (priv->listen_sock) {
+ soup_socket_disconnect (priv->listen_sock);
+ g_object_unref (priv->listen_sock);
+ priv->listen_sock = NULL;
+ }
+}
+
+/**
+ * soup_server_get_async_context:
+ * @server: a #SoupServer
+ *
+ * Gets @server's async_context. This does not add a ref to the
+ * context, so you will need to ref it yourself if you want it to
+ * outlive its server.
+ *
+ * Return value: (transfer none): @server's #GMainContext, which may be %NULL
+ **/
+GMainContext *
+soup_server_get_async_context (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ return priv->async_context;
+}
+
+/**
+ * SoupClientContext:
+ *
+ * A #SoupClientContext provides additional information about the
+ * client making a particular request. In particular, you can use
+ * soup_client_context_get_auth_domain() and
+ * soup_client_context_get_auth_user() to determine if HTTP
+ * authentication was used successfully.
+ *
+ * soup_client_context_get_address() and/or
+ * soup_client_context_get_host() can be used to get information for
+ * logging or debugging purposes. soup_client_context_get_socket() may
+ * also be of use in some situations (eg, tracking when multiple
+ * requests are made on the same connection).
+ **/
+GType
+soup_client_context_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupClientContext"),
+ (GBoxedCopyFunc) soup_client_context_ref,
+ (GBoxedFreeFunc) soup_client_context_unref);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
+
+/**
+ * soup_client_context_get_socket:
+ * @client: a #SoupClientContext
+ *
+ * Retrieves the #SoupSocket that @client is associated with.
+ *
+ * If you are using this method to observe when multiple requests are
+ * made on the same persistent HTTP connection (eg, as the ntlm-test
+ * test program does), you will need to pay attention to socket
+ * destruction as well (either by using weak references, or by
+ * connecting to the #SoupSocket::disconnected signal), so that you do
+ * not get fooled when the allocator reuses the memory address of a
+ * previously-destroyed socket to represent a new socket.
+ *
+ * Return value: (transfer none): the #SoupSocket that @client is
+ * associated with.
+ **/
+SoupSocket *
+soup_client_context_get_socket (SoupClientContext *client)
+{
+ g_return_val_if_fail (client != NULL, NULL);
+
+ return client->sock;
+}
+
+/**
+ * soup_client_context_get_address:
+ * @client: a #SoupClientContext
+ *
+ * Retrieves the #SoupAddress associated with the remote end
+ * of a connection.
+ *
+ * Return value: (transfer none): the #SoupAddress associated with the
+ * remote end of a connection.
+ **/
+SoupAddress *
+soup_client_context_get_address (SoupClientContext *client)
+{
+ g_return_val_if_fail (client != NULL, NULL);
+
+ return soup_socket_get_remote_address (client->sock);
+}
+
+/**
+ * soup_client_context_get_host:
+ * @client: a #SoupClientContext
+ *
+ * Retrieves the IP address associated with the remote end of a
+ * connection. (If you want the actual hostname, you'll have to call
+ * soup_client_context_get_address() and then call the appropriate
+ * #SoupAddress method to resolve it.)
+ *
+ * Return value: the IP address associated with the remote end of a
+ * connection.
+ **/
+const char *
+soup_client_context_get_host (SoupClientContext *client)
+{
+ SoupAddress *address;
+
+ address = soup_client_context_get_address (client);
+ return soup_address_get_physical (address);
+}
+
+/**
+ * soup_client_context_get_auth_domain:
+ * @client: a #SoupClientContext
+ *
+ * Checks whether the request associated with @client has been
+ * authenticated, and if so returns the #SoupAuthDomain that
+ * authenticated it.
+ *
+ * Return value: (transfer none) (allow-none): a #SoupAuthDomain, or
+ * %NULL if the request was not authenticated.
+ **/
+SoupAuthDomain *
+soup_client_context_get_auth_domain (SoupClientContext *client)
+{
+ g_return_val_if_fail (client != NULL, NULL);
+
+ return client->auth_domain;
+}
+
+/**
+ * soup_client_context_get_auth_user:
+ * @client: a #SoupClientContext
+ *
+ * Checks whether the request associated with @client has been
+ * authenticated, and if so returns the username that the client
+ * authenticated as.
+ *
+ * Return value: the authenticated-as user, or %NULL if the request
+ * was not authenticated.
+ **/
+const char *
+soup_client_context_get_auth_user (SoupClientContext *client)
+{
+ g_return_val_if_fail (client != NULL, NULL);
+
+ return client->auth_user;
+}
+
+/**
+ * SoupServerCallback:
+ * @server: the #SoupServer
+ * @msg: the message being processed
+ * @path: the path component of @msg's Request-URI
+ * @query: (element-type utf8 utf8) (allow-none): the parsed query
+ * component of @msg's Request-URI
+ * @client: additional contextual information about the client
+ * @user_data: the data passed to @soup_server_add_handler
+ *
+ * A callback used to handle requests to a #SoupServer. The callback
+ * will be invoked after receiving the request body; @msg's %method,
+ * %request_headers, and %request_body fields will be filled in.
+ *
+ * @path and @query contain the likewise-named components of the
+ * Request-URI, subject to certain assumptions. By default,
+ * #SoupServer decodes all percent-encoding in the URI path, such that
+ * "/foo%<!-- -->2Fbar" is treated the same as "/foo/bar". If your
+ * server is serving resources in some non-POSIX-filesystem namespace,
+ * you may want to distinguish those as two distinct paths. In that
+ * case, you can set the %SOUP_SERVER_RAW_PATHS property when creating
+ * the #SoupServer, and it will leave those characters undecoded. (You
+ * may want to call soup_uri_normalize() to decode any percent-encoded
+ * characters that you aren't handling specially.)
+ *
+ * @query contains the query component of the Request-URI parsed
+ * according to the rules for HTML form handling. Although this is the
+ * only commonly-used query string format in HTTP, there is nothing
+ * that actually requires that HTTP URIs use that format; if your
+ * server needs to use some other format, you can just ignore @query,
+ * and call soup_message_get_uri() and parse the URI's query field
+ * yourself.
+ *
+ * After determining what to do with the request, the callback must at
+ * a minimum call soup_message_set_status() (or
+ * soup_message_set_status_full()) on @msg to set the response status
+ * code. Additionally, it may set response headers and/or fill in the
+ * response body.
+ *
+ * If the callback cannot fully fill in the response before returning
+ * (eg, if it needs to wait for information from a database, or
+ * another network server), it should call soup_server_pause_message()
+ * to tell #SoupServer to not send the response right away. When the
+ * response is ready, call soup_server_unpause_message() to cause it
+ * to be sent.
+ *
+ * To send the response body a bit at a time using "chunked" encoding,
+ * first call soup_message_headers_set_encoding() to set
+ * %SOUP_ENCODING_CHUNKED on the %response_headers. Then call
+ * soup_message_body_append() (or soup_message_body_append_buffer())
+ * to append each chunk as it becomes ready, and
+ * soup_server_unpause_message() to make sure it's running. (The
+ * server will automatically pause the message if it is using chunked
+ * encoding but no more chunks are available.) When you are done, call
+ * soup_message_body_complete() to indicate that no more chunks are
+ * coming.
+ **/
+
+/**
+ * soup_server_add_handler:
+ * @server: a #SoupServer
+ * @path: (allow-none): the toplevel path for the handler
+ * @callback: callback to invoke for requests under @path
+ * @user_data: data for @callback
+ * @destroy: destroy notifier to free @user_data
+ *
+ * Adds a handler to @server for requests under @path. See the
+ * documentation for #SoupServerCallback for information about
+ * how callbacks should behave.
+ *
+ * If @path is %NULL or "/", then this will be the default handler for
+ * all requests that don't have a more specific handler. Note though
+ * that if you want to handle requests to the special "*" URI, you
+ * must explicitly register a handler for "*"; the default handler
+ * will not be used for that case.
+ **/
+void
+soup_server_add_handler (SoupServer *server,
+ const char *path,
+ SoupServerCallback callback,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ SoupServerPrivate *priv;
+ SoupServerHandler *hand;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ g_return_if_fail (callback != NULL);
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ /* "" was never documented as meaning the same this as "/",
+ * but it effectively was. We have to special case it now or
+ * otherwise it would match "*" too.
+ */
+ if (path && (!*path || !strcmp (path, "/")))
+ path = NULL;
+
+ hand = g_slice_new0 (SoupServerHandler);
+ hand->path = g_strdup (path);
+ hand->callback = callback;
+ hand->destroy = destroy;
+ hand->user_data = user_data;
+
+ soup_server_remove_handler (server, path);
+ if (path)
+ soup_path_map_add (priv->handlers, path, hand);
+ else
+ priv->default_handler = hand;
+}
+
+static void
+unregister_handler (SoupServerHandler *handler)
+{
+ if (handler->destroy)
+ handler->destroy (handler->user_data);
+}
+
+/**
+ * soup_server_remove_handler:
+ * @server: a #SoupServer
+ * @path: the toplevel path for the handler
+ *
+ * Removes the handler registered at @path.
+ **/
+void
+soup_server_remove_handler (SoupServer *server, const char *path)
+{
+ SoupServerPrivate *priv;
+ SoupServerHandler *hand;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ if (!path || !*path || !strcmp (path, "/")) {
+ if (priv->default_handler) {
+ unregister_handler (priv->default_handler);
+ free_handler (priv->default_handler);
+ priv->default_handler = NULL;
+ }
+ return;
+ }
+
+ hand = soup_path_map_lookup (priv->handlers, path);
+ if (hand && !strcmp (path, hand->path)) {
+ unregister_handler (hand);
+ soup_path_map_remove (priv->handlers, path);
+ }
+}
+
+/**
+ * soup_server_add_auth_domain:
+ * @server: a #SoupServer
+ * @auth_domain: a #SoupAuthDomain
+ *
+ * Adds an authentication domain to @server. Each auth domain will
+ * have the chance to require authentication for each request that
+ * comes in; normally auth domains will require authentication for
+ * requests on certain paths that they have been set up to watch, or
+ * that meet other criteria set by the caller. If an auth domain
+ * determines that a request requires authentication (and the request
+ * doesn't contain authentication), @server will automatically reject
+ * the request with an appropriate status (401 Unauthorized or 407
+ * Proxy Authentication Required). If the request used the
+ * "100-continue" Expectation, @server will reject it before the
+ * request body is sent.
+ **/
+void
+soup_server_add_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain)
+{
+ SoupServerPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ priv->auth_domains = g_slist_append (priv->auth_domains, auth_domain);
+ g_object_ref (auth_domain);
+}
+
+/**
+ * soup_server_remove_auth_domain:
+ * @server: a #SoupServer
+ * @auth_domain: a #SoupAuthDomain
+ *
+ * Removes @auth_domain from @server.
+ **/
+void
+soup_server_remove_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain)
+{
+ SoupServerPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ priv = SOUP_SERVER_GET_PRIVATE (server);
+
+ priv->auth_domains = g_slist_remove (priv->auth_domains, auth_domain);
+ g_object_unref (auth_domain);
+}
+
+/**
+ * soup_server_pause_message:
+ * @server: a #SoupServer
+ * @msg: a #SoupMessage associated with @server.
+ *
+ * Pauses I/O on @msg. This can be used when you need to return from
+ * the server handler without having the full response ready yet. Use
+ * soup_server_unpause_message() to resume I/O.
+ **/
+void
+soup_server_pause_message (SoupServer *server,
+ SoupMessage *msg)
+{
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ soup_message_io_pause (msg);
+}
+
+/**
+ * soup_server_unpause_message:
+ * @server: a #SoupServer
+ * @msg: a #SoupMessage associated with @server.
+ *
+ * Resumes I/O on @msg. Use this to resume after calling
+ * soup_server_pause_message(), or after adding a new chunk to a
+ * chunked response.
+ *
+ * I/O won't actually resume until you return to the main loop.
+ **/
+void
+soup_server_unpause_message (SoupServer *server,
+ SoupMessage *msg)
+{
+ g_return_if_fail (SOUP_IS_SERVER (server));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ soup_message_io_unpause (msg);
+}
+
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SERVER_H
+#define SOUP_SERVER_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-uri.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_SERVER (soup_server_get_type ())
+#define SOUP_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SERVER, SoupServer))
+#define SOUP_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SERVER, SoupServerClass))
+#define SOUP_IS_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SERVER))
+#define SOUP_IS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SERVER))
+#define SOUP_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SERVER, SoupServerClass))
+
+typedef struct SoupClientContext SoupClientContext;
+GType soup_client_context_get_type (void);
+#define SOUP_TYPE_CLIENT_CONTEXT (soup_client_context_get_type ())
+
+struct _SoupServer {
+ GObject parent;
+
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*request_started) (SoupServer *server, SoupMessage *msg,
+ SoupClientContext *client);
+ void (*request_read) (SoupServer *server, SoupMessage *msg,
+ SoupClientContext *client);
+ void (*request_finished) (SoupServer *server, SoupMessage *msg,
+ SoupClientContext *client);
+ void (*request_aborted) (SoupServer *server, SoupMessage *msg,
+ SoupClientContext *client);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupServerClass;
+
+GType soup_server_get_type (void);
+
+typedef void (*SoupServerCallback) (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data);
+
+#define SOUP_SERVER_PORT "port"
+#define SOUP_SERVER_INTERFACE "interface"
+#define SOUP_SERVER_SSL_CERT_FILE "ssl-cert-file"
+#define SOUP_SERVER_SSL_KEY_FILE "ssl-key-file"
+#define SOUP_SERVER_ASYNC_CONTEXT "async-context"
+#define SOUP_SERVER_RAW_PATHS "raw-paths"
+#define SOUP_SERVER_SERVER_HEADER "server-header"
+
+SoupServer *soup_server_new (const char *optname1,
+ ...) G_GNUC_NULL_TERMINATED;
+
+gboolean soup_server_is_https (SoupServer *server);
+guint soup_server_get_port (SoupServer *server);
+
+SoupSocket *soup_server_get_listener (SoupServer *server);
+
+void soup_server_run (SoupServer *server);
+void soup_server_run_async (SoupServer *server);
+void soup_server_quit (SoupServer *server);
+void soup_server_disconnect (SoupServer *server);
+
+GMainContext *soup_server_get_async_context (SoupServer *server);
+
+/* Handlers and auth */
+
+void soup_server_add_handler (SoupServer *server,
+ const char *path,
+ SoupServerCallback callback,
+ gpointer user_data,
+ GDestroyNotify destroy);
+void soup_server_remove_handler (SoupServer *server,
+ const char *path);
+
+void soup_server_add_auth_domain (SoupServer *server,
+ SoupAuthDomain *auth_domain);
+void soup_server_remove_auth_domain (SoupServer *server,
+ SoupAuthDomain *auth_domain);
+
+/* I/O */
+
+void soup_server_pause_message (SoupServer *server,
+ SoupMessage *msg);
+void soup_server_unpause_message (SoupServer *server,
+ SoupMessage *msg);
+
+/* Client context */
+
+SoupSocket *soup_client_context_get_socket (SoupClientContext *client);
+SoupAddress *soup_client_context_get_address (SoupClientContext *client);
+const char *soup_client_context_get_host (SoupClientContext *client);
+SoupAuthDomain *soup_client_context_get_auth_domain (SoupClientContext *client);
+const char *soup_client_context_get_auth_user (SoupClientContext *client);
+
+G_END_DECLS
+
+#endif /* SOUP_SERVER_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-session-async.c
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+
+#include "soup-address.h"
+#include "soup-session-async.h"
+#include "soup-session-private.h"
+#include "soup-address.h"
+#include "soup-message-private.h"
+#include "soup-message-queue.h"
+#include "soup-misc.h"
+#include "soup-password-manager.h"
+#include "soup-proxy-uri-resolver.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-session-async
+ * @short_description: Soup session for asynchronous (main-loop-based) I/O.
+ *
+ * #SoupSessionAsync is an implementation of #SoupSession that uses
+ * non-blocking I/O via the glib main loop. It is intended for use in
+ * single-threaded programs.
+ **/
+
+static void run_queue (SoupSessionAsync *sa);
+static void do_idle_run_queue (SoupSession *session);
+
+static void queue_message (SoupSession *session, SoupMessage *req,
+ SoupSessionCallback callback, gpointer user_data);
+static guint send_message (SoupSession *session, SoupMessage *req);
+static void cancel_message (SoupSession *session, SoupMessage *msg,
+ guint status_code);
+
+static void auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
+
+G_DEFINE_TYPE (SoupSessionAsync, soup_session_async, SOUP_TYPE_SESSION)
+
+typedef struct {
+ GSource *idle_run_queue_source;
+} SoupSessionAsyncPrivate;
+#define SOUP_SESSION_ASYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncPrivate))
+
+static void
+soup_session_async_init (SoupSessionAsync *sa)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (object);
+
+ if (priv->idle_run_queue_source)
+ g_source_destroy (priv->idle_run_queue_source);
+
+ G_OBJECT_CLASS (soup_session_async_parent_class)->finalize (object);
+}
+
+static void
+soup_session_async_class_init (SoupSessionAsyncClass *soup_session_async_class)
+{
+ SoupSessionClass *session_class = SOUP_SESSION_CLASS (soup_session_async_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (session_class);
+
+ g_type_class_add_private (soup_session_async_class,
+ sizeof (SoupSessionAsyncPrivate));
+
+ /* virtual method override */
+ session_class->queue_message = queue_message;
+ session_class->send_message = send_message;
+ session_class->cancel_message = cancel_message;
+ session_class->auth_required = auth_required;
+
+ object_class->finalize = finalize;
+}
+
+
+/**
+ * soup_session_async_new:
+ *
+ * Creates an asynchronous #SoupSession with the default options.
+ *
+ * Return value: the new session.
+ **/
+SoupSession *
+soup_session_async_new (void)
+{
+ return g_object_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+}
+
+/**
+ * soup_session_async_new_with_options:
+ * @optname1: name of first property to set
+ * @...: value of @optname1, followed by additional property/value pairs
+ *
+ * Creates an asynchronous #SoupSession with the specified options.
+ *
+ * Return value: the new session.
+ **/
+SoupSession *
+soup_session_async_new_with_options (const char *optname1, ...)
+{
+ SoupSession *session;
+ va_list ap;
+
+ va_start (ap, optname1);
+ session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_ASYNC,
+ optname1, ap);
+ va_end (ap);
+
+ return session;
+}
+
+static gboolean
+item_failed (SoupMessageQueueItem *item, guint status)
+{
+ if (item->removed) {
+ soup_message_queue_item_unref (item);
+ return TRUE;
+ }
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ item->state = SOUP_MESSAGE_FINISHING;
+ if (!item->msg->status_code)
+ soup_session_set_item_status (item->session, item, status);
+ do_idle_run_queue (item->session);
+ soup_message_queue_item_unref (item);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+resolved_proxy_addr (SoupAddress *addr, guint status, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+ SoupSession *session = item->session;
+
+ if (item_failed (item, soup_status_proxify (status)))
+ return;
+
+ item->proxy_addr = g_object_ref (addr);
+ item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+
+ soup_message_queue_item_unref (item);
+
+ /* If we got here we know session still exists */
+ run_queue ((SoupSessionAsync *)session);
+}
+
+static void
+resolved_proxy_uri (SoupProxyURIResolver *proxy_resolver,
+ guint status, SoupURI *proxy_uri, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+ SoupSession *session = item->session;
+
+ if (item_failed (item, status))
+ return;
+
+ if (proxy_uri) {
+ SoupAddress *proxy_addr;
+
+ item->state = SOUP_MESSAGE_RESOLVING_PROXY_ADDRESS;
+
+ item->proxy_uri = soup_uri_copy (proxy_uri);
+ proxy_addr = soup_address_new (proxy_uri->host,
+ proxy_uri->port);
+ soup_address_resolve_async (proxy_addr,
+ soup_session_get_async_context (session),
+ item->cancellable,
+ resolved_proxy_addr, item);
+ g_object_unref (proxy_addr);
+ return;
+ }
+
+ item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+ soup_message_queue_item_unref (item);
+
+ /* If we got here we know session still exists */
+ run_queue ((SoupSessionAsync *)session);
+}
+
+static void
+resolve_proxy_addr (SoupMessageQueueItem *item,
+ SoupProxyURIResolver *proxy_resolver)
+{
+ item->state = SOUP_MESSAGE_RESOLVING_PROXY_URI;
+
+ soup_message_queue_item_ref (item);
+ soup_proxy_uri_resolver_get_proxy_uri_async (
+ proxy_resolver, soup_message_get_uri (item->msg),
+ soup_session_get_async_context (item->session),
+ item->cancellable, resolved_proxy_uri, item);
+}
+
+static void
+connection_closed (SoupConnection *conn, gpointer session)
+{
+ /* Run the queue in case anyone was waiting for a connection
+ * to be closed.
+ */
+ do_idle_run_queue (session);
+}
+
+static void
+message_completed (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ if (item->state != SOUP_MESSAGE_RESTARTING)
+ item->state = SOUP_MESSAGE_FINISHING;
+ do_idle_run_queue (item->session);
+}
+
+static void
+tunnel_complete (SoupMessageQueueItem *item)
+{
+ SoupSession *session = item->session;
+
+ soup_message_finished (item->msg);
+ if (item->related->msg->status_code)
+ item->related->state = SOUP_MESSAGE_FINISHING;
+
+ do_idle_run_queue (session);
+ soup_message_queue_item_unref (item->related);
+ soup_session_unqueue_item (session, item);
+ soup_message_queue_item_unref (item);
+ g_object_unref (session);
+}
+
+static void
+ssl_tunnel_completed (SoupConnection *conn, guint status, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ g_signal_connect (item->conn, "disconnected",
+ G_CALLBACK (connection_closed), item->session);
+ soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
+ soup_connection_set_state (item->conn, SOUP_CONNECTION_IN_USE);
+
+ item->related->state = SOUP_MESSAGE_READY;
+ } else {
+ if (item->conn)
+ soup_connection_disconnect (item->conn);
+ soup_message_set_status (item->related->msg, SOUP_STATUS_SSL_FAILED);
+ }
+
+ tunnel_complete (item);
+}
+
+static void
+tunnel_message_completed (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+ SoupSession *session = item->session;
+
+ if (item->state == SOUP_MESSAGE_RESTARTING) {
+ soup_message_restarted (msg);
+ if (item->conn) {
+ soup_session_send_queue_item (session, item, tunnel_message_completed);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
+ }
+
+ item->state = SOUP_MESSAGE_FINISHED;
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ if (item->conn)
+ soup_connection_disconnect (item->conn);
+ if (msg->status_code == SOUP_STATUS_TRY_AGAIN) {
+ item->related->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+ g_object_unref (item->related->conn);
+ item->related->conn = NULL;
+ } else
+ soup_message_set_status (item->related->msg, msg->status_code);
+
+ tunnel_complete (item);
+ return;
+ }
+
+ soup_connection_start_ssl_async (item->conn, item->cancellable,
+ ssl_tunnel_completed, item);
+}
+
+static void
+got_connection (SoupConnection *conn, guint status, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+ SoupSession *session = item->session;
+ SoupAddress *tunnel_addr;
+
+ if (item->state != SOUP_MESSAGE_CONNECTING) {
+ soup_connection_disconnect (conn);
+ do_idle_run_queue (session);
+ soup_message_queue_item_unref (item);
+ g_object_unref (session);
+ return;
+ }
+
+ if (status != SOUP_STATUS_OK) {
+ soup_connection_disconnect (conn);
+
+ if (status == SOUP_STATUS_TRY_AGAIN) {
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+ } else {
+ soup_session_set_item_status (session, item, status);
+ item->state = SOUP_MESSAGE_FINISHING;
+ }
+
+ do_idle_run_queue (session);
+ soup_message_queue_item_unref (item);
+ g_object_unref (session);
+ return;
+ }
+
+ tunnel_addr = soup_connection_get_tunnel_addr (conn);
+ if (tunnel_addr) {
+ SoupMessageQueueItem *tunnel_item;
+
+ item->state = SOUP_MESSAGE_TUNNELING;
+
+ tunnel_item = soup_session_make_connect_message (session, conn);
+ tunnel_item->related = item;
+ soup_session_send_queue_item (session, tunnel_item, tunnel_message_completed);
+ return;
+ }
+
+ item->state = SOUP_MESSAGE_READY;
+ g_signal_connect (conn, "disconnected",
+ G_CALLBACK (connection_closed), session);
+ run_queue ((SoupSessionAsync *)session);
+ soup_message_queue_item_unref (item);
+ g_object_unref (session);
+}
+
+static void
+process_queue_item (SoupMessageQueueItem *item,
+ gboolean *should_prune,
+ gboolean loop)
+{
+ SoupSession *session = item->session;
+ SoupProxyURIResolver *proxy_resolver;
+
+ do {
+ switch (item->state) {
+ case SOUP_MESSAGE_STARTING:
+ proxy_resolver = (SoupProxyURIResolver *)soup_session_get_feature_for_message (session, SOUP_TYPE_PROXY_URI_RESOLVER, item->msg);
+ if (!proxy_resolver) {
+ item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+ break;
+ }
+ resolve_proxy_addr (item, proxy_resolver);
+ return;
+
+ case SOUP_MESSAGE_AWAITING_CONNECTION:
+ if (!soup_session_get_connection (session, item, should_prune))
+ return;
+
+ if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
+ item->state = SOUP_MESSAGE_READY;
+ break;
+ }
+
+ item->state = SOUP_MESSAGE_CONNECTING;
+ soup_message_queue_item_ref (item);
+ g_object_ref (session);
+ soup_connection_connect_async (item->conn, item->cancellable,
+ got_connection, item);
+ return;
+
+ case SOUP_MESSAGE_READY:
+ item->state = SOUP_MESSAGE_RUNNING;
+ soup_session_send_queue_item (session, item, message_completed);
+ break;
+
+ case SOUP_MESSAGE_RESTARTING:
+ item->state = SOUP_MESSAGE_STARTING;
+ soup_message_restarted (item->msg);
+ break;
+
+ case SOUP_MESSAGE_FINISHING:
+ item->state = SOUP_MESSAGE_FINISHED;
+ soup_message_finished (item->msg);
+ if (item->state != SOUP_MESSAGE_FINISHED)
+ break;
+
+ g_object_ref (session);
+ soup_session_unqueue_item (session, item);
+ if (item->callback)
+ item->callback (session, item->msg, item->callback_data);
+ g_object_unref (item->msg);
+ do_idle_run_queue (session);
+ g_object_unref (session);
+ return;
+
+ default:
+ /* Nothing to do with this message in any
+ * other state.
+ */
+ return;
+ }
+ } while (loop && item->state != SOUP_MESSAGE_FINISHED);
+}
+
+static void
+run_queue (SoupSessionAsync *sa)
+{
+ SoupSession *session = SOUP_SESSION (sa);
+ SoupMessageQueue *queue = soup_session_get_queue (session);
+ SoupMessageQueueItem *item;
+ SoupMessage *msg;
+ gboolean try_pruning = TRUE, should_prune = FALSE;
+
+ g_object_ref (session);
+ soup_session_cleanup_connections (session, FALSE);
+
+ try_again:
+ for (item = soup_message_queue_first (queue);
+ item;
+ item = soup_message_queue_next (queue, item)) {
+ msg = item->msg;
+
+ /* CONNECT messages are handled specially */
+ if (msg->method != SOUP_METHOD_CONNECT)
+ process_queue_item (item, &should_prune, TRUE);
+ }
+
+ if (try_pruning && should_prune) {
+ /* There is at least one message in the queue that
+ * could be sent if we pruned an idle connection from
+ * some other server.
+ */
+ if (soup_session_cleanup_connections (session, TRUE)) {
+ try_pruning = should_prune = FALSE;
+ goto try_again;
+ }
+ }
+
+ g_object_unref (session);
+}
+
+static gboolean
+idle_run_queue (gpointer sa)
+{
+ SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (sa);
+
+ priv->idle_run_queue_source = NULL;
+ run_queue (sa);
+ return FALSE;
+}
+
+static void
+do_idle_run_queue (SoupSession *session)
+{
+ SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (session);
+
+ if (!priv->idle_run_queue_source) {
+ priv->idle_run_queue_source = soup_add_completion (
+ soup_session_get_async_context (session),
+ idle_run_queue, session);
+ }
+}
+
+static void
+queue_message (SoupSession *session, SoupMessage *req,
+ SoupSessionCallback callback, gpointer user_data)
+{
+ SOUP_SESSION_CLASS (soup_session_async_parent_class)->queue_message (session, req, callback, user_data);
+
+ do_idle_run_queue (session);
+}
+
+static guint
+send_message (SoupSession *session, SoupMessage *req)
+{
+ SoupMessageQueueItem *item;
+ GMainContext *async_context =
+ soup_session_get_async_context (session);
+
+ /* Balance out the unref that queuing will eventually do */
+ g_object_ref (req);
+
+ queue_message (session, req, NULL, NULL);
+
+ item = soup_message_queue_lookup (soup_session_get_queue (session), req);
+ g_return_val_if_fail (item != NULL, SOUP_STATUS_MALFORMED);
+
+ while (item->state != SOUP_MESSAGE_FINISHED)
+ g_main_context_iteration (async_context, TRUE);
+
+ soup_message_queue_item_unref (item);
+
+ return req->status_code;
+}
+
+static void
+cancel_message (SoupSession *session, SoupMessage *msg,
+ guint status_code)
+{
+ SoupMessageQueue *queue;
+ SoupMessageQueueItem *item;
+ gboolean dummy;
+
+ SOUP_SESSION_CLASS (soup_session_async_parent_class)->
+ cancel_message (session, msg, status_code);
+
+ queue = soup_session_get_queue (session);
+ item = soup_message_queue_lookup (queue, msg);
+ if (!item)
+ return;
+
+ /* Force it to finish immediately, so that
+ * soup_session_abort (session); g_object_unref (session);
+ * will work. (The soup_session_cancel_message() docs
+ * point out that the callback will be invoked from
+ * within the cancel call.)
+ */
+ if (soup_message_io_in_progress (msg))
+ soup_message_io_finished (msg);
+ else if (item->state != SOUP_MESSAGE_FINISHED)
+ item->state = SOUP_MESSAGE_FINISHING;
+
+ if (item->state != SOUP_MESSAGE_FINISHED)
+ process_queue_item (item, &dummy, FALSE);
+
+ soup_message_queue_item_unref (item);
+}
+
+static void
+got_passwords (SoupPasswordManager *password_manager, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer session)
+{
+ soup_session_unpause_message (session, msg);
+ SOUP_SESSION_CLASS (soup_session_async_parent_class)->
+ auth_required (session, msg, auth, retrying);
+ g_object_unref (auth);
+}
+
+static void
+auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying)
+{
+ SoupSessionFeature *password_manager;
+
+ password_manager = soup_session_get_feature_for_message (
+ session, SOUP_TYPE_PASSWORD_MANAGER, msg);
+ if (password_manager) {
+ soup_session_pause_message (session, msg);
+ g_object_ref (auth);
+ soup_password_manager_get_passwords_async (
+ SOUP_PASSWORD_MANAGER (password_manager),
+ msg, auth, retrying,
+ soup_session_get_async_context (session),
+ NULL, /* FIXME cancellable */
+ got_passwords, session);
+ } else {
+ SOUP_SESSION_CLASS (soup_session_async_parent_class)->
+ auth_required (session, msg, auth, retrying);
+ }
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SESSION_ASYNC_H
+#define SOUP_SESSION_ASYNC_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-session.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_SESSION_ASYNC (soup_session_async_get_type ())
+#define SOUP_SESSION_ASYNC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsync))
+#define SOUP_SESSION_ASYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncClass))
+#define SOUP_IS_SESSION_ASYNC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SESSION_ASYNC))
+#define SOUP_IS_SESSION_ASYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SESSION_ASYNC))
+#define SOUP_SESSION_ASYNC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncClass))
+
+struct _SoupSessionAsync {
+ SoupSession parent;
+
+};
+
+typedef struct {
+ SoupSessionClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupSessionAsyncClass;
+
+GType soup_session_async_get_type (void);
+
+SoupSession *soup_session_async_new (void);
+SoupSession *soup_session_async_new_with_options (const char *optname1,
+ ...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* SOUP_SESSION_ASYNC_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-session-feature.c: Miscellaneous session feature-provider interface
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-session-feature.h"
+#include "soup-message-private.h"
+
+/**
+ * SECTION:soup-session-feature
+ * @short_description: Interface for miscellaneous session features
+ *
+ * #SoupSessionFeature is the interface used by classes that extend
+ * the functionality of a #SoupSession. Some features like HTTP
+ * authentication handling are implemented internally via
+ * #SoupSessionFeature<!-- -->s. Other features can be added to the session
+ * by the application. (Eg, #SoupLogger, #SoupCookieJar.)
+ *
+ * See soup_session_add_feature(), etc, to add a feature to a session.
+ **/
+
+/**
+ * SoupSessionFeature:
+ *
+ * An object that implement some sort of optional feature for
+ * #SoupSession.
+ *
+ * Since: 2.24
+ **/
+
+/**
+ * SoupSessionFeatureInterface:
+ * @parent: The parent interface.
+ * @attach: Perform setup when a feature is added to a session
+ * @detach: Perform cleanup when a feature is removed from a session
+ * @request_queued: Proxies the session's #SoupSession::request_queued signal
+ * @request_started: Proxies the session's #SoupSession::request_started signal
+ * @request_unqueued: Proxies the session's #SoupSession::request_unqueued signal
+ *
+ * The interface implemented by #SoupSessionFeature<!-- -->s.
+ *
+ * Since: 2.24
+ **/
+
+static void soup_session_feature_interface_init (SoupSessionFeatureInterface *interface);
+
+static void attach (SoupSessionFeature *feature, SoupSession *session);
+static void detach (SoupSessionFeature *feature, SoupSession *session);
+
+GType
+soup_session_feature_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ GType g_define_type_id =
+ g_type_register_static_simple (G_TYPE_INTERFACE,
+ g_intern_static_string ("SoupSessionFeature"),
+ sizeof (SoupSessionFeatureInterface),
+ (GClassInitFunc)soup_session_feature_interface_init,
+ 0,
+ (GInstanceInitFunc)NULL,
+ (GTypeFlags) 0);
+ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+static void
+soup_session_feature_interface_init (SoupSessionFeatureInterface *interface)
+{
+ interface->attach = attach;
+ interface->detach = detach;
+}
+
+static void
+weak_notify_unref (gpointer feature, GObject *ex_object)
+{
+ g_object_unref (feature);
+}
+
+static void
+request_queued (SoupSession *session, SoupMessage *msg, gpointer feature)
+{
+ if (soup_message_disables_feature (msg, feature))
+ return;
+
+ SOUP_SESSION_FEATURE_GET_CLASS (feature)->
+ request_queued (feature, session, msg);
+}
+
+static void
+request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket, gpointer feature)
+{
+ if (soup_message_disables_feature (msg, feature))
+ return;
+
+ SOUP_SESSION_FEATURE_GET_CLASS (feature)->
+ request_started (feature, session, msg, socket);
+}
+
+static void
+request_unqueued (SoupSession *session, SoupMessage *msg, gpointer feature)
+{
+ if (soup_message_disables_feature (msg, feature))
+ return;
+
+ SOUP_SESSION_FEATURE_GET_CLASS (feature)->
+ request_unqueued (feature, session, msg);
+}
+
+static void
+attach (SoupSessionFeature *feature, SoupSession *session)
+{
+ g_object_weak_ref (G_OBJECT (session),
+ weak_notify_unref, g_object_ref (feature));
+
+ if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_queued) {
+ g_signal_connect (session, "request_queued",
+ G_CALLBACK (request_queued), feature);
+ }
+
+ if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_started) {
+ g_signal_connect (session, "request_started",
+ G_CALLBACK (request_started), feature);
+ }
+
+ if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_unqueued) {
+ g_signal_connect (session, "request_unqueued",
+ G_CALLBACK (request_unqueued), feature);
+ }
+}
+
+void
+soup_session_feature_attach (SoupSessionFeature *feature,
+ SoupSession *session)
+{
+ SOUP_SESSION_FEATURE_GET_CLASS (feature)->attach (feature, session);
+}
+
+static void
+detach (SoupSessionFeature *feature, SoupSession *session)
+{
+ g_object_weak_unref (G_OBJECT (session), weak_notify_unref, feature);
+
+ g_signal_handlers_disconnect_by_func (session, request_queued, feature);
+ g_signal_handlers_disconnect_by_func (session, request_started, feature);
+ g_signal_handlers_disconnect_by_func (session, request_unqueued, feature);
+
+ g_object_unref (feature);
+}
+
+void
+soup_session_feature_detach (SoupSessionFeature *feature,
+ SoupSession *session)
+{
+ SOUP_SESSION_FEATURE_GET_CLASS (feature)->detach (feature, session);
+}
+
+/**
+ * soup_session_feature_add_feature:
+ * @feature: the "base" feature
+ * @type: the #GType of a "sub-feature"
+ *
+ * Adds a "sub-feature" of type @type to the base feature @feature.
+ * This is used for features that can be extended with multiple
+ * different types. Eg, the authentication manager can be extended
+ * with subtypes of #SoupAuth.
+ *
+ * Return value: %TRUE if @feature accepted @type as a subfeature.
+ *
+ * Since: 2.34
+ */
+gboolean
+soup_session_feature_add_feature (SoupSessionFeature *feature,
+ GType type)
+{
+ SoupSessionFeatureInterface *feature_iface =
+ SOUP_SESSION_FEATURE_GET_CLASS (feature);
+
+ if (feature_iface->add_feature)
+ return feature_iface->add_feature (feature, type);
+ else
+ return FALSE;
+}
+
+/**
+ * soup_session_feature_remove_feature:
+ * @feature: the "base" feature
+ * @type: the #GType of a "sub-feature"
+ *
+ * Removes the "sub-feature" of type @type from the base feature
+ * @feature. See soup_session_feature_add_feature().
+ *
+ * Return value: %TRUE if @type was removed from @feature
+ *
+ * Since: 2.34
+ */
+gboolean
+soup_session_feature_remove_feature (SoupSessionFeature *feature,
+ GType type)
+{
+ SoupSessionFeatureInterface *feature_iface =
+ SOUP_SESSION_FEATURE_GET_CLASS (feature);
+
+ if (feature_iface->remove_feature)
+ return feature_iface->remove_feature (feature, type);
+ else
+ return FALSE;
+}
+
+/**
+ * soup_session_feature_has_feature:
+ * @feature: the "base" feature
+ * @type: the #GType of a "sub-feature"
+ *
+ * Tests if @feature has a "sub-feature" of type @type. See
+ * soup_session_feature_add_feature().
+ *
+ * Return value: %TRUE if @feature has a subfeature of type @type
+ *
+ * Since: 2.34
+ */
+gboolean
+soup_session_feature_has_feature (SoupSessionFeature *feature,
+ GType type)
+{
+ SoupSessionFeatureInterface *feature_iface =
+ SOUP_SESSION_FEATURE_GET_CLASS (feature);
+
+ if (feature_iface->has_feature)
+ return feature_iface->has_feature (feature, type);
+ else
+ return FALSE;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_SESSION_FEATURE_H
+#define SOUP_SESSION_FEATURE_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_SESSION_FEATURE (soup_session_feature_get_type ())
+#define SOUP_SESSION_FEATURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION_FEATURE, SoupSessionFeature))
+#define SOUP_SESSION_FEATURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SESSION_FEATURE, SoupSessionFeatureInterface))
+#define SOUP_IS_SESSION_FEATURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SESSION_FEATURE))
+#define SOUP_IS_SESSION_FEATURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_SESSION_FEATURE))
+#define SOUP_SESSION_FEATURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SOUP_TYPE_SESSION_FEATURE, SoupSessionFeatureInterface))
+
+typedef struct {
+ GTypeInterface parent;
+
+ /* methods */
+ void (*attach) (SoupSessionFeature *feature,
+ SoupSession *session);
+ void (*detach) (SoupSessionFeature *feature,
+ SoupSession *session);
+
+ void (*request_queued) (SoupSessionFeature *feature,
+ SoupSession *session,
+ SoupMessage *msg);
+ void (*request_started) (SoupSessionFeature *feature,
+ SoupSession *session,
+ SoupMessage *msg,
+ SoupSocket *socket);
+ void (*request_unqueued) (SoupSessionFeature *feature,
+ SoupSession *session,
+ SoupMessage *msg);
+
+ gboolean (*add_feature) (SoupSessionFeature *feature,
+ GType type);
+ gboolean (*remove_feature) (SoupSessionFeature *feature,
+ GType type);
+ gboolean (*has_feature) (SoupSessionFeature *feature,
+ GType type);
+
+} SoupSessionFeatureInterface;
+
+GType soup_session_feature_get_type (void);
+
+void soup_session_feature_attach (SoupSessionFeature *feature,
+ SoupSession *session);
+void soup_session_feature_detach (SoupSessionFeature *feature,
+ SoupSession *session);
+
+gboolean soup_session_feature_add_feature (SoupSessionFeature *feature,
+ GType type);
+gboolean soup_session_feature_remove_feature (SoupSessionFeature *feature,
+ GType type);
+gboolean soup_session_feature_has_feature (SoupSessionFeature *feature,
+ GType type);
+
+G_END_DECLS
+
+#endif /* SOUP_SESSION_FEATURE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SESSION_PRIVATE_H
+#define SOUP_SESSION_PRIVATE_H 1
+
+#include "soup-session.h"
+#include "soup-message-private.h"
+#include "soup-proxy-uri-resolver.h"
+
+G_BEGIN_DECLS
+
+/* "protected" methods for subclasses */
+SoupMessageQueue *soup_session_get_queue (SoupSession *session);
+
+SoupMessageQueueItem *soup_session_make_connect_message (SoupSession *session,
+ SoupConnection *conn);
+gboolean soup_session_get_connection (SoupSession *session,
+ SoupMessageQueueItem *item,
+ gboolean *try_pruning);
+gboolean soup_session_cleanup_connections (SoupSession *session,
+ gboolean prune_idle);
+void soup_session_send_queue_item (SoupSession *session,
+ SoupMessageQueueItem *item,
+ SoupMessageCompletionFn completion_cb);
+void soup_session_unqueue_item (SoupSession *session,
+ SoupMessageQueueItem *item);
+void soup_session_set_item_status (SoupSession *session,
+ SoupMessageQueueItem *item,
+ guint status_code);
+
+G_END_DECLS
+
+#endif /* SOUP_SESSION_PRIVATE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-session-sync.c
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+
+#include "soup-address.h"
+#include "soup-session-sync.h"
+#include "soup-session-private.h"
+#include "soup-address.h"
+#include "soup-message-private.h"
+#include "soup-message-queue.h"
+#include "soup-misc.h"
+#include "soup-password-manager.h"
+#include "soup-proxy-uri-resolver.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-session-sync
+ * @short_description: Soup session for blocking I/O in multithreaded
+ * programs.
+ *
+ * #SoupSessionSync is an implementation of #SoupSession that uses
+ * synchronous I/O, intended for use in multi-threaded programs.
+ *
+ * You can use #SoupSessionSync from multiple threads concurrently.
+ * Eg, you can send a #SoupMessage in one thread, and then while
+ * waiting for the response, send another #SoupMessage from another
+ * thread. You can also send a message from one thread and then call
+ * soup_session_cancel_message() on it from any other thread (although
+ * you need to be careful to avoid race conditions, where the message
+ * finishes and is then unreffed by the sending thread just before you
+ * cancel it).
+ *
+ * However, the majority of other types and methods in libsoup are not
+ * MT-safe. In particular, you <emphasis>cannot</emphasis> modify or
+ * examine a #SoupMessage while it is being transmitted by
+ * #SoupSessionSync in another thread. Once a message has been handed
+ * off to #SoupSessionSync, it can only be manipulated from its signal
+ * handler callbacks, until I/O is complete.
+ **/
+
+typedef struct {
+ GMutex *lock;
+ GCond *cond;
+} SoupSessionSyncPrivate;
+#define SOUP_SESSION_SYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncPrivate))
+
+static void queue_message (SoupSession *session, SoupMessage *msg,
+ SoupSessionCallback callback, gpointer user_data);
+static guint send_message (SoupSession *session, SoupMessage *msg);
+static void cancel_message (SoupSession *session, SoupMessage *msg,
+ guint status_code);
+static void auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
+static void flush_queue (SoupSession *session);
+
+G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
+
+static void
+soup_session_sync_init (SoupSessionSync *ss)
+{
+ SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (ss);
+
+ priv->lock = g_mutex_new ();
+ priv->cond = g_cond_new ();
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (object);
+
+ g_mutex_free (priv->lock);
+ g_cond_free (priv->cond);
+
+ G_OBJECT_CLASS (soup_session_sync_parent_class)->finalize (object);
+}
+
+static void
+soup_session_sync_class_init (SoupSessionSyncClass *session_sync_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (session_sync_class);
+ SoupSessionClass *session_class = SOUP_SESSION_CLASS (session_sync_class);
+
+ g_type_class_add_private (session_sync_class, sizeof (SoupSessionSyncPrivate));
+
+ /* virtual method override */
+ session_class->queue_message = queue_message;
+ session_class->send_message = send_message;
+ session_class->cancel_message = cancel_message;
+ session_class->auth_required = auth_required;
+ session_class->flush_queue = flush_queue;
+
+ object_class->finalize = finalize;
+}
+
+
+/**
+ * soup_session_sync_new:
+ *
+ * Creates an synchronous #SoupSession with the default options.
+ *
+ * Return value: the new session.
+ **/
+SoupSession *
+soup_session_sync_new (void)
+{
+ return g_object_new (SOUP_TYPE_SESSION_SYNC, NULL);
+}
+
+/**
+ * soup_session_sync_new_with_options:
+ * @optname1: name of first property to set
+ * @...: value of @optname1, followed by additional property/value pairs
+ *
+ * Creates an synchronous #SoupSession with the specified options.
+ *
+ * Return value: the new session.
+ **/
+SoupSession *
+soup_session_sync_new_with_options (const char *optname1, ...)
+{
+ SoupSession *session;
+ va_list ap;
+
+ va_start (ap, optname1);
+ session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_SYNC,
+ optname1, ap);
+ va_end (ap);
+
+ return session;
+}
+
+static guint
+tunnel_connect (SoupSession *session, SoupMessageQueueItem *related)
+{
+ SoupConnection *conn = related->conn;
+ SoupMessageQueueItem *item;
+ guint status;
+
+ g_object_ref (conn);
+
+ item = soup_session_make_connect_message (session, conn);
+ do {
+ soup_session_send_queue_item (session, item, NULL);
+ status = item->msg->status_code;
+ if (item->state == SOUP_MESSAGE_RESTARTING &&
+ soup_connection_get_state (conn) != SOUP_CONNECTION_DISCONNECTED) {
+ item->state = SOUP_MESSAGE_STARTING;
+ soup_message_restarted (item->msg);
+ } else {
+ if (item->state == SOUP_MESSAGE_RESTARTING)
+ status = SOUP_STATUS_TRY_AGAIN;
+ item->state = SOUP_MESSAGE_FINISHED;
+ soup_message_finished (item->msg);
+ }
+ } while (item->state == SOUP_MESSAGE_STARTING);
+ soup_session_unqueue_item (session, item);
+ soup_message_queue_item_unref (item);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ if (!soup_connection_start_ssl_sync (conn, related->cancellable))
+ status = SOUP_STATUS_SSL_FAILED;
+ }
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ soup_connection_disconnect (conn);
+
+ g_object_unref (conn);
+ return status;
+}
+
+static void
+get_connection (SoupMessageQueueItem *item)
+{
+ SoupSession *session = item->session;
+ SoupMessage *msg = item->msg;
+ gboolean try_pruning = FALSE;
+ guint status;
+
+try_again:
+ soup_session_cleanup_connections (session, FALSE);
+
+ if (!soup_session_get_connection (session, item, &try_pruning)) {
+ if (!try_pruning)
+ return;
+ soup_session_cleanup_connections (session, TRUE);
+ if (!soup_session_get_connection (session, item, &try_pruning))
+ return;
+ try_pruning = FALSE;
+ }
+
+ if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
+ item->state = SOUP_MESSAGE_READY;
+ return;
+ }
+
+ status = soup_connection_connect_sync (item->conn, item->cancellable);
+ if (status == SOUP_STATUS_TRY_AGAIN) {
+ soup_connection_disconnect (item->conn);
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ goto try_again;
+ }
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ if (!msg->status_code)
+ soup_session_set_item_status (session, item, status);
+ item->state = SOUP_MESSAGE_FINISHING;
+ soup_connection_disconnect (item->conn);
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ return;
+ }
+
+ if (soup_connection_get_tunnel_addr (item->conn)) {
+ status = tunnel_connect (session, item);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ soup_connection_disconnect (item->conn);
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ if (status == SOUP_STATUS_TRY_AGAIN)
+ goto try_again;
+ soup_session_set_item_status (session, item, status);
+ item->state = SOUP_MESSAGE_FINISHING;
+ return;
+ }
+ }
+
+ item->state = SOUP_MESSAGE_READY;
+}
+
+static void
+process_queue_item (SoupMessageQueueItem *item)
+{
+ SoupSession *session = item->session;
+ SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
+ SoupMessage *msg = item->msg;
+ SoupProxyURIResolver *proxy_resolver;
+ guint status;
+
+ item->state = SOUP_MESSAGE_STARTING;
+ do {
+ switch (item->state) {
+ case SOUP_MESSAGE_STARTING:
+ proxy_resolver = (SoupProxyURIResolver *)soup_session_get_feature_for_message (session, SOUP_TYPE_PROXY_URI_RESOLVER, msg);
+ if (!proxy_resolver) {
+ item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+ break;
+ }
+
+ status = soup_proxy_uri_resolver_get_proxy_uri_sync (
+ proxy_resolver, soup_message_get_uri (msg),
+ item->cancellable, &item->proxy_uri);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ soup_session_set_item_status (session, item, status);
+ item->state = SOUP_MESSAGE_FINISHING;
+ break;
+ }
+ if (!item->proxy_uri) {
+ item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+ break;
+ }
+
+ item->proxy_addr = soup_address_new (
+ item->proxy_uri->host, item->proxy_uri->port);
+ status = soup_address_resolve_sync (item->proxy_addr,
+ item->cancellable);
+ if (SOUP_STATUS_IS_SUCCESSFUL (status))
+ item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+ else {
+ soup_session_set_item_status (session, item, soup_status_proxify (status));
+ item->state = SOUP_MESSAGE_FINISHING;
+ }
+ break;
+
+ case SOUP_MESSAGE_AWAITING_CONNECTION:
+ g_mutex_lock (priv->lock);
+ do {
+ get_connection (item);
+ if (item->state == SOUP_MESSAGE_AWAITING_CONNECTION)
+ g_cond_wait (priv->cond, priv->lock);
+ } while (item->state == SOUP_MESSAGE_AWAITING_CONNECTION);
+ g_mutex_unlock (priv->lock);
+ break;
+
+ case SOUP_MESSAGE_READY:
+ item->state = SOUP_MESSAGE_RUNNING;
+ soup_session_send_queue_item (item->session, item, NULL);
+ if (item->state != SOUP_MESSAGE_RESTARTING)
+ item->state = SOUP_MESSAGE_FINISHING;
+ break;
+
+ case SOUP_MESSAGE_RESTARTING:
+ item->state = SOUP_MESSAGE_STARTING;
+ soup_message_restarted (item->msg);
+ break;
+
+ case SOUP_MESSAGE_FINISHING:
+ item->state = SOUP_MESSAGE_FINISHED;
+ soup_message_finished (item->msg);
+ soup_session_unqueue_item (session, item);
+ g_cond_broadcast (priv->cond);
+ break;
+
+ default:
+ g_warn_if_reached ();
+ item->state = SOUP_MESSAGE_FINISHING;
+ break;
+ }
+ } while (item->state != SOUP_MESSAGE_FINISHED);
+}
+
+static gboolean
+queue_message_callback (gpointer data)
+{
+ SoupMessageQueueItem *item = data;
+
+ item->callback (item->session, item->msg, item->callback_data);
+ g_object_unref (item->session);
+ g_object_unref (item->msg);
+ soup_message_queue_item_unref (item);
+ return FALSE;
+}
+
+static gpointer
+queue_message_thread (gpointer data)
+{
+ SoupMessageQueueItem *item = data;
+
+ process_queue_item (item);
+ if (item->callback) {
+ soup_add_completion (soup_session_get_async_context (item->session),
+ queue_message_callback, item);
+ } else {
+ g_object_unref (item->session);
+ g_object_unref (item->msg);
+ soup_message_queue_item_unref (item);
+ }
+
+ return NULL;
+}
+
+static void
+queue_message (SoupSession *session, SoupMessage *msg,
+ SoupSessionCallback callback, gpointer user_data)
+{
+ SoupMessageQueueItem *item;
+
+ SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
+ queue_message (g_object_ref (session), msg, callback, user_data);
+
+ item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
+ g_return_if_fail (item != NULL);
+
+ g_thread_create (queue_message_thread, item, FALSE, NULL);
+}
+
+static guint
+send_message (SoupSession *session, SoupMessage *msg)
+{
+ SoupMessageQueueItem *item;
+ guint status;
+
+ SOUP_SESSION_CLASS (soup_session_sync_parent_class)->queue_message (session, msg, NULL, NULL);
+
+ item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
+ g_return_val_if_fail (item != NULL, SOUP_STATUS_MALFORMED);
+
+ process_queue_item (item);
+ status = msg->status_code;
+ soup_message_queue_item_unref (item);
+ return status;
+}
+
+static void
+cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
+{
+ SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
+
+ g_mutex_lock (priv->lock);
+ SOUP_SESSION_CLASS (soup_session_sync_parent_class)->cancel_message (session, msg, status_code);
+ g_cond_broadcast (priv->cond);
+ g_mutex_unlock (priv->lock);
+}
+
+static void
+auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying)
+{
+ SoupSessionFeature *password_manager;
+
+ password_manager = soup_session_get_feature_for_message (
+ session, SOUP_TYPE_PASSWORD_MANAGER, msg);
+ if (password_manager) {
+ soup_password_manager_get_passwords_sync (
+ SOUP_PASSWORD_MANAGER (password_manager),
+ msg, auth, NULL); /* FIXME cancellable */
+ }
+
+ SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
+ auth_required (session, msg, auth, retrying);
+}
+
+static void
+flush_queue (SoupSession *session)
+{
+ SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
+ SoupMessageQueue *queue;
+ SoupMessageQueueItem *item;
+ GHashTable *current;
+ gboolean done = FALSE;
+
+ /* Record the current contents of the queue */
+ current = g_hash_table_new (NULL, NULL);
+ queue = soup_session_get_queue (session);
+ for (item = soup_message_queue_first (queue);
+ item;
+ item = soup_message_queue_next (queue, item))
+ g_hash_table_insert (current, item, item);
+
+ /* Cancel everything */
+ SOUP_SESSION_CLASS (soup_session_sync_parent_class)->flush_queue (session);
+
+ /* Wait until all of the items in @current have been removed
+ * from the queue. (This is not the same as "wait for the
+ * queue to be empty", because the app may queue new requests
+ * in response to the cancellation of the old ones. We don't
+ * try to cancel those requests as well, since we'd likely
+ * just end up looping forever.)
+ */
+ g_mutex_lock (priv->lock);
+ do {
+ done = TRUE;
+ for (item = soup_message_queue_first (queue);
+ item;
+ item = soup_message_queue_next (queue, item)) {
+ if (g_hash_table_lookup (current, item))
+ done = FALSE;
+ }
+
+ if (!done)
+ g_cond_wait (priv->cond, priv->lock);
+ } while (!done);
+ g_mutex_unlock (priv->lock);
+
+ g_hash_table_destroy (current);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SESSION_SYNC_H
+#define SOUP_SESSION_SYNC_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-session.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_SESSION_SYNC (soup_session_sync_get_type ())
+#define SOUP_SESSION_SYNC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION_SYNC, SoupSessionSync))
+#define SOUP_SESSION_SYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncClass))
+#define SOUP_IS_SESSION_SYNC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SESSION_SYNC))
+#define SOUP_IS_SESSION_SYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SESSION_SYNC))
+#define SOUP_SESSION_SYNC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncClass))
+
+struct _SoupSessionSync {
+ SoupSession parent;
+
+};
+
+typedef struct {
+ SoupSessionClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupSessionSyncClass;
+
+GType soup_session_sync_get_type (void);
+
+SoupSession *soup_session_sync_new (void);
+SoupSession *soup_session_sync_new_with_options (const char *optname1,
+ ...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* SOUP_SESSION_SYNC_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-session.c
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "soup-address.h"
+#include "soup-auth.h"
+#include "soup-auth-basic.h"
+#include "soup-auth-digest.h"
+#include "soup-auth-manager-ntlm.h"
+#include "soup-connection.h"
+#include "soup-marshal.h"
+#include "soup-message-private.h"
+#include "soup-message-queue.h"
+#include "soup-misc.h"
+#include "soup-proxy-resolver-static.h"
+#include "soup-proxy-uri-resolver.h"
+#include "soup-session.h"
+#include "soup-session-feature.h"
+#include "soup-session-private.h"
+#include "soup-socket.h"
+#include "soup-ssl.h"
+#include "soup-uri.h"
+/*TIZEN patch*/
+#include "TIZEN.h"
+
+/**
+ * SECTION:soup-session
+ * @short_description: Soup session state object
+ *
+ * #SoupSession is the object that controls client-side HTTP. A
+ * #SoupSession encapsulates all of the state that libsoup is keeping
+ * on behalf of your program; cached HTTP connections, authentication
+ * information, etc.
+ *
+ * Most applications will only need a single #SoupSession; the primary
+ * reason you might need multiple sessions is if you need to have
+ * multiple independent authentication contexts. (Eg, you are
+ * connecting to a server and authenticating as two different users at
+ * different times; the easiest way to ensure that each #SoupMessage
+ * is sent with the authentication information you intended is to use
+ * one session for the first user, and a second session for the other
+ * user.)
+ *
+ * #SoupSession itself is an abstract class, with two subclasses. If
+ * you are using the glib main loop, you will generally want to use
+ * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
+ * the other hand, if your application is threaded and you want to do
+ * synchronous I/O in a separate thread from the UI, use
+ * #SoupSessionSync.
+ **/
+
+typedef struct {
+ SoupURI *uri;
+ SoupAddress *addr;
+
+ GSList *connections; /* CONTAINS: SoupConnection */
+ guint num_conns;
+
+ guint num_messages;
+
+ gboolean ssl_fallback;
+} SoupSessionHost;
+
+typedef struct {
+ char *ssl_ca_file;
+ SoupSSLCredentials *ssl_creds;
+ gboolean ssl_strict;
+
+ SoupMessageQueue *queue;
+
+ char *user_agent;
+ char *accept_language;
+ gboolean accept_language_auto;
+
+ GSList *features;
+ GHashTable *features_cache;
+
+ GHashTable *hosts; /* char* -> SoupSessionHost */
+ GHashTable *conns; /* SoupConnection -> SoupSessionHost */
+ guint num_conns;
+ guint max_conns, max_conns_per_host;
+ guint io_timeout, idle_timeout;
+
+ /* Must hold the host_lock before potentially creating a
+ * new SoupSessionHost, or adding/removing a connection.
+ * Must not emit signals or destroy objects while holding it.
+ */
+ GMutex *host_lock;
+
+ GMainContext *async_context;
+
+ GResolver *resolver;
+} SoupSessionPrivate;
+#define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
+
+static void free_host (SoupSessionHost *host);
+
+static void queue_message (SoupSession *session, SoupMessage *msg,
+ SoupSessionCallback callback, gpointer user_data);
+static void requeue_message (SoupSession *session, SoupMessage *msg);
+static void cancel_message (SoupSession *session, SoupMessage *msg,
+ guint status_code);
+static void auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
+static void flush_queue (SoupSession *session);
+
+static void auth_manager_authenticate (SoupAuthManager *manager,
+ SoupMessage *msg, SoupAuth *auth,
+ gboolean retrying, gpointer user_data);
+
+#define SOUP_SESSION_MAX_CONNS_DEFAULT 10
+#define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
+
+#define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
+
+#define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
+
+G_DEFINE_ABSTRACT_TYPE (SoupSession, soup_session, G_TYPE_OBJECT)
+
+enum {
+ REQUEST_QUEUED,
+ REQUEST_STARTED,
+ REQUEST_UNQUEUED,
+ AUTHENTICATE,
+ CONNECTION_CREATED,
+ TUNNELING,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+
+ PROP_PROXY_URI,
+ PROP_MAX_CONNS,
+ PROP_MAX_CONNS_PER_HOST,
+ PROP_USE_NTLM,
+ PROP_SSL_CA_FILE,
+ PROP_SSL_STRICT,
+ PROP_ASYNC_CONTEXT,
+ PROP_TIMEOUT,
+ PROP_USER_AGENT,
+ PROP_ACCEPT_LANGUAGE,
+ PROP_ACCEPT_LANGUAGE_AUTO,
+ PROP_IDLE_TIMEOUT,
+ PROP_ADD_FEATURE,
+ PROP_ADD_FEATURE_BY_TYPE,
+ PROP_REMOVE_FEATURE_BY_TYPE,
+
+ LAST_PROP
+};
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void
+soup_session_init (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupAuthManager *auth_manager;
+
+ priv->queue = soup_message_queue_new (session);
+
+ priv->host_lock = g_mutex_new ();
+ priv->hosts = g_hash_table_new_full (soup_uri_host_hash,
+ soup_uri_host_equal,
+ NULL, (GDestroyNotify)free_host);
+ priv->conns = g_hash_table_new (NULL, NULL);
+
+ priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
+ priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
+
+ priv->features_cache = g_hash_table_new (NULL, NULL);
+
+ auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
+ g_signal_connect (auth_manager, "authenticate",
+ G_CALLBACK (auth_manager_authenticate), session);
+ soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
+ SOUP_TYPE_AUTH_BASIC);
+ soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
+ SOUP_TYPE_AUTH_DIGEST);
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
+ g_object_unref (auth_manager);
+
+ /* We'll be doing DNS continuously-ish while the session is active,
+ * so hold a ref on the default GResolver.
+ */
+ priv->resolver = g_resolver_get_default ();
+
+ priv->ssl_strict = TRUE;
+}
+
+static void
+dispose (GObject *object)
+{
+ SoupSession *session = SOUP_SESSION (object);
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ soup_session_abort (session);
+
+ while (priv->features)
+ soup_session_remove_feature (session, priv->features->data);
+
+ G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupSession *session = SOUP_SESSION (object);
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ soup_message_queue_destroy (priv->queue);
+
+ g_mutex_free (priv->host_lock);
+ g_hash_table_destroy (priv->hosts);
+ g_hash_table_destroy (priv->conns);
+
+ g_free (priv->user_agent);
+ g_free (priv->accept_language);
+
+ if (priv->ssl_ca_file)
+ g_free (priv->ssl_ca_file);
+ if (priv->ssl_creds)
+ soup_ssl_free_client_credentials (priv->ssl_creds);
+
+ if (priv->async_context)
+ g_main_context_unref (priv->async_context);
+
+ g_hash_table_destroy (priv->features_cache);
+
+ g_object_unref (priv->resolver);
+
+ G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
+}
+
+static void
+soup_session_class_init (SoupSessionClass *session_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (session_class);
+
+ g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
+
+ /* virtual method definition */
+ session_class->queue_message = queue_message;
+ session_class->requeue_message = requeue_message;
+ session_class->cancel_message = cancel_message;
+ session_class->auth_required = auth_required;
+ session_class->flush_queue = flush_queue;
+
+ /* virtual method override */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /* signals */
+
+ /**
+ * SoupSession::request-queued:
+ * @session: the session
+ * @msg: the request that was queued
+ *
+ * Emitted when a request is queued on @session. (Note that
+ * "queued" doesn't just mean soup_session_queue_message();
+ * soup_session_send_message() implicitly queues the message
+ * as well.)
+ *
+ * When sending a request, first #SoupSession::request_queued
+ * is emitted, indicating that the session has become aware of
+ * the request.
+ *
+ * Once a connection is available to send the request on, the
+ * session emits #SoupSession::request_started. Then, various
+ * #SoupMessage signals are emitted as the message is
+ * processed. If the message is requeued, it will emit
+ * #SoupMessage::restarted, which will then be followed by
+ * another #SoupSession::request_started and another set of
+ * #SoupMessage signals when the message is re-sent.
+ *
+ * Eventually, the message will emit #SoupMessage::finished.
+ * Normally, this signals the completion of message
+ * processing. However, it is possible that the application
+ * will requeue the message from the "finished" handler (or
+ * equivalently, from the soup_session_queue_message()
+ * callback). In that case, the process will loop back to
+ * #SoupSession::request_started.
+ *
+ * Eventually, a message will reach "finished" and not be
+ * requeued. At that point, the session will emit
+ * #SoupSession::request_unqueued to indicate that it is done
+ * with the message.
+ *
+ * To sum up: #SoupSession::request_queued and
+ * #SoupSession::request_unqueued are guaranteed to be emitted
+ * exactly once, but #SoupSession::request_started and
+ * #SoupMessage::finished (and all of the other #SoupMessage
+ * signals) may be invoked multiple times for a given message.
+ *
+ * Since: 2.4.1
+ **/
+ signals[REQUEST_QUEUED] =
+ g_signal_new ("request-queued",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* FIXME? */
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ SOUP_TYPE_MESSAGE);
+
+ /**
+ * SoupSession::request-started:
+ * @session: the session
+ * @msg: the request being sent
+ * @socket: the socket the request is being sent on
+ *
+ * Emitted just before a request is sent. See
+ * #SoupSession::request_queued for a detailed description of
+ * the message lifecycle within a session.
+ **/
+ signals[REQUEST_STARTED] =
+ g_signal_new ("request-started",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupSessionClass, request_started),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_OBJECT,
+ G_TYPE_NONE, 2,
+ SOUP_TYPE_MESSAGE,
+ SOUP_TYPE_SOCKET);
+
+ /**
+ * SoupSession::request-unqueued:
+ * @session: the session
+ * @msg: the request that was unqueued
+ *
+ * Emitted when a request is removed from @session's queue,
+ * indicating that @session is done with it. See
+ * #SoupSession::request_queued for a detailed description of the
+ * message lifecycle within a session.
+ *
+ * Since: 2.4.1
+ **/
+ signals[REQUEST_UNQUEUED] =
+ g_signal_new ("request-unqueued",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* FIXME? */
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ SOUP_TYPE_MESSAGE);
+
+ /**
+ * SoupSession::authenticate:
+ * @session: the session
+ * @msg: the #SoupMessage being sent
+ * @auth: the #SoupAuth to authenticate
+ * @retrying: %TRUE if this is the second (or later) attempt
+ *
+ * Emitted when the session requires authentication. If
+ * credentials are available call soup_auth_authenticate() on
+ * @auth. If these credentials fail, the signal will be
+ * emitted again, with @retrying set to %TRUE, which will
+ * continue until you return without calling
+ * soup_auth_authenticate() on @auth.
+ *
+ * Note that this may be emitted before @msg's body has been
+ * fully read.
+ *
+ * If you call soup_session_pause_message() on @msg before
+ * returning, then you can authenticate @auth asynchronously
+ * (as long as you g_object_ref() it to make sure it doesn't
+ * get destroyed), and then unpause @msg when you are ready
+ * for it to continue.
+ **/
+ signals[AUTHENTICATE] =
+ g_signal_new ("authenticate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupSessionClass, authenticate),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
+ G_TYPE_NONE, 3,
+ SOUP_TYPE_MESSAGE,
+ SOUP_TYPE_AUTH,
+ G_TYPE_BOOLEAN);
+
+ signals[CONNECTION_CREATED] =
+ g_signal_new ("connection-created",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ /* SoupConnection is private, so we can't use
+ * SOUP_TYPE_CONNECTION here.
+ */
+ G_TYPE_OBJECT);
+
+ signals[TUNNELING] =
+ g_signal_new ("tunneling",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ /* SoupConnection is private, so we can't use
+ * SOUP_TYPE_CONNECTION here.
+ */
+ G_TYPE_OBJECT);
+
+
+ /* properties */
+ /**
+ * SOUP_SESSION_PROXY_URI:
+ *
+ * Alias for the #SoupSession:proxy-uri property. (The HTTP
+ * proxy to use for this session.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_PROXY_URI,
+ g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
+ "Proxy URI",
+ "The HTTP Proxy to use for this session",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_SESSION_MAX_CONNS:
+ *
+ * Alias for the #SoupSession:max-conns property. (The maximum
+ * number of connections that the session can open at once.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_MAX_CONNS,
+ g_param_spec_int (SOUP_SESSION_MAX_CONNS,
+ "Max Connection Count",
+ "The maximum number of connections that the session can open at once",
+ 1,
+ G_MAXINT,
+ SOUP_SESSION_MAX_CONNS_DEFAULT,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_SESSION_MAX_CONNS_PER_HOST:
+ *
+ * Alias for the #SoupSession:max-conns-per-host property.
+ * (The maximum number of connections that the session can
+ * open at once to a given host.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_MAX_CONNS_PER_HOST,
+ g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
+ "Max Per-Host Connection Count",
+ "The maximum number of connections that the session can open at once to a given host",
+ 1,
+ G_MAXINT,
+ SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
+ G_PARAM_READWRITE));
+ /**
+ * SoupSession:idle-timeout:
+ *
+ * Connection lifetime when idle
+ *
+ * Since: 2.4.1
+ **/
+ /**
+ * SOUP_SESSION_IDLE_TIMEOUT:
+ *
+ * Alias for the #SoupSession:idle-timeout property. (The idle
+ * connection lifetime.)
+ *
+ * Since: 2.4.1
+ **/
+ g_object_class_install_property (
+ object_class, PROP_IDLE_TIMEOUT,
+ g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
+ "Idle Timeout",
+ "Connection lifetime when idle",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE));
+ /**
+ * SoupSession:use-ntlm:
+ *
+ * Whether or not to use NTLM authentication.
+ *
+ * Deprecated: use soup_session_add_feature_by_type() with
+ * #SOUP_TYPE_AUTH_NTLM.
+ **/
+ /**
+ * SOUP_SESSION_USE_NTLM:
+ *
+ * Alias for the #SoupSession:use-ntlm property. (Whether or
+ * not to use NTLM authentication.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_USE_NTLM,
+ g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
+ "Use NTLM",
+ "Whether or not to use NTLM authentication",
+ FALSE,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_SESSION_SSL_CA_FILE:
+ *
+ * Alias for the #SoupSession:ssl-ca-file property. (File
+ * containing SSL CA certificates.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SSL_CA_FILE,
+ g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
+ "SSL CA file",
+ "File containing SSL CA certificates",
+ NULL,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_SESSION_SSL_STRICT:
+ *
+ * Alias for the #SoupSession:ignore-ssl-cert-errors
+ * property. By default, when validating certificates against
+ * a CA file, Soup will consider invalid certificates as a
+ * connection error. Setting this property to %TRUE makes soup
+ * ignore the errors, and make the connection.
+ *
+ * Since: 2.30
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SSL_STRICT,
+ g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
+ "Strictly validate SSL certificates",
+ "Whether certificate errors should be considered a connection error",
+ TRUE,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_SESSION_ASYNC_CONTEXT:
+ *
+ * Alias for the #SoupSession:async-context property. (The
+ * session's #GMainContext.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_ASYNC_CONTEXT,
+ g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
+ "Async GMainContext",
+ "The GMainContext to dispatch async I/O in",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SESSION_TIMEOUT:
+ *
+ * Alias for the #SoupSession:timeout property. (The timeout
+ * in seconds for blocking socket I/O operations.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_TIMEOUT,
+ g_param_spec_uint (SOUP_SESSION_TIMEOUT,
+ "Timeout value",
+ "Value in seconds to timeout a blocking I/O",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE));
+
+ /**
+ * SoupSession:user-agent:
+ *
+ * If non-%NULL, the value to use for the "User-Agent" header
+ * on #SoupMessage<!-- -->s sent from this session.
+ *
+ * RFC 2616 says: "The User-Agent request-header field
+ * contains information about the user agent originating the
+ * request. This is for statistical purposes, the tracing of
+ * protocol violations, and automated recognition of user
+ * agents for the sake of tailoring responses to avoid
+ * particular user agent limitations. User agents SHOULD
+ * include this field with requests."
+ *
+ * The User-Agent header contains a list of one or more
+ * product tokens, separated by whitespace, with the most
+ * significant product token coming first. The tokens must be
+ * brief, ASCII, and mostly alphanumeric (although "-", "_",
+ * and "." are also allowed), and may optionally include a "/"
+ * followed by a version string. You may also put comments,
+ * enclosed in parentheses, between or after the tokens.
+ *
+ * If you set a %user_agent property that has trailing
+ * whitespace, #SoupSession will append its own product token
+ * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
+ * header for you.
+ **/
+ /**
+ * SOUP_SESSION_USER_AGENT:
+ *
+ * Alias for the #SoupSession:user-agent property, qv.
+ **/
+ g_object_class_install_property (
+ object_class, PROP_USER_AGENT,
+ g_param_spec_string (SOUP_SESSION_USER_AGENT,
+ "User-Agent string",
+ "User-Agent string",
+ NULL,
+ G_PARAM_READWRITE));
+
+ /**
+ * SoupSession:accept-language:
+ *
+ * If non-%NULL, the value to use for the "Accept-Language" header
+ * on #SoupMessage<!-- -->s sent from this session.
+ *
+ * Setting this will disable
+ * #SoupSession:accept-language-auto.
+ *
+ * Since: 2.30
+ **/
+ /**
+ * SOUP_SESSION_ACCEPT_LANGUAGE:
+ *
+ * Alias for the #SoupSession:accept-language property, qv.
+ *
+ * Since: 2.30
+ **/
+ g_object_class_install_property (
+ object_class, PROP_ACCEPT_LANGUAGE,
+ g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
+ "Accept-Language string",
+ "Accept-Language string",
+ NULL,
+ G_PARAM_READWRITE));
+
+ /**
+ * SoupSession:accept-language-auto:
+ *
+ * If %TRUE, #SoupSession will automatically set the string
+ * for the "Accept-Language" header on every #SoupMessage
+ * sent, based on the return value of g_get_language_names().
+ *
+ * Setting this will override any previous value of
+ * #SoupSession:accept-language.
+ *
+ * Since: 2.30
+ **/
+ /**
+ * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
+ *
+ * Alias for the #SoupSession:accept-language-auto property, qv.
+ *
+ * Since: 2.30
+ **/
+ g_object_class_install_property (
+ object_class, PROP_ACCEPT_LANGUAGE_AUTO,
+ g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
+ "Accept-Language automatic mode",
+ "Accept-Language automatic mode",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ /**
+ * SoupSession:add-feature: (skip)
+ *
+ * Add a feature object to the session. (Shortcut for calling
+ * soup_session_add_feature().)
+ *
+ * Since: 2.24
+ **/
+ /**
+ * SOUP_SESSION_ADD_FEATURE: (skip)
+ *
+ * Alias for the #SoupSession:add-feature property. (Shortcut
+ * for calling soup_session_add_feature().
+ *
+ * Since: 2.24
+ **/
+ g_object_class_install_property (
+ object_class, PROP_ADD_FEATURE,
+ g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
+ "Add Feature",
+ "Add a feature object to the session",
+ SOUP_TYPE_SESSION_FEATURE,
+ G_PARAM_READWRITE));
+ /**
+ * SoupSession:add-feature-by-type: (skip)
+ *
+ * Add a feature object of the given type to the session.
+ * (Shortcut for calling soup_session_add_feature_by_type().)
+ *
+ * Since: 2.24
+ **/
+ /**
+ * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
+ *
+ * Alias for the #SoupSession:add-feature-by-type property.
+ * (Shortcut for calling soup_session_add_feature_by_type().
+ *
+ * Since: 2.24
+ **/
+ g_object_class_install_property (
+ object_class, PROP_ADD_FEATURE_BY_TYPE,
+ g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
+ "Add Feature By Type",
+ "Add a feature object of the given type to the session",
+ SOUP_TYPE_SESSION_FEATURE,
+ G_PARAM_READWRITE));
+ /**
+ * SoupSession:remove-feature-by-type: (skip)
+ *
+ * Remove feature objects from the session. (Shortcut for
+ * calling soup_session_remove_feature_by_type().)
+ *
+ * Since: 2.24
+ **/
+ /**
+ * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
+ *
+ * Alias for the #SoupSession:remove-feature-by-type
+ * property. (Shortcut for calling
+ * soup_session_remove_feature_by_type().
+ *
+ * Since: 2.24
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REMOVE_FEATURE_BY_TYPE,
+ g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
+ "Remove Feature By Type",
+ "Remove features of the given type from the session",
+ SOUP_TYPE_SESSION_FEATURE,
+ G_PARAM_READWRITE));
+}
+
+static gboolean
+safe_str_equal (const char *a, const char *b)
+{
+ if (!a && !b)
+ return TRUE;
+
+ if ((a && !b) || (b && !a))
+ return FALSE;
+
+ return strcmp (a, b) == 0;
+}
+
+/* Converts a language in POSIX format and to be RFC2616 compliant */
+/* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
+static gchar *
+posix_lang_to_rfc2616 (const gchar *language)
+{
+ /* Don't include charset variants, etc */
+ if (strchr (language, '.') || strchr (language, '@'))
+ return NULL;
+
+ /* Ignore "C" locale, which g_get_language_names() always
+ * includes as a fallback.
+ */
+ if (!strcmp (language, "C"))
+ return NULL;
+
+ return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
+}
+
+/* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
+static gchar *
+add_quality_value (const gchar *str, int quality)
+{
+ g_return_val_if_fail (str != NULL, NULL);
+
+ if (quality >= 0 && quality < 100) {
+ /* We don't use %.02g because of "." vs "," locale issues */
+ if (quality % 10)
+ return g_strdup_printf ("%s;q=0.%02d", str, quality);
+ else
+ return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
+ } else
+ return g_strdup (str);
+}
+
+/* Returns a RFC2616 compliant languages list from system locales */
+static gchar *
+accept_languages_from_system (void)
+{
+ const char * const * lang_names;
+ GPtrArray *langs = NULL;
+ char *lang, **langs_array, *langs_str;
+ int delta;
+ int i;
+
+ lang_names = g_get_language_names ();
+ g_return_val_if_fail (lang_names != NULL, NULL);
+
+ /* Build the array of languages */
+ langs = g_ptr_array_new ();
+ for (i = 0; lang_names[i] != NULL; i++) {
+ lang = posix_lang_to_rfc2616 (lang_names[i]);
+ if (lang)
+ g_ptr_array_add (langs, lang);
+ }
+
+ /* Add quality values */
+ if (langs->len < 10)
+ delta = 10;
+ else if (langs->len < 20)
+ delta = 5;
+ else
+ delta = 1;
+
+ for (i = 0; i < langs->len; i++) {
+ lang = langs->pdata[i];
+ langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
+ g_free (lang);
+ }
+
+ /* Fallback: add "en" if list is empty */
+ if (langs->len == 0)
+ g_ptr_array_add (langs, g_strdup ("en"));
+
+ g_ptr_array_add (langs, NULL);
+ langs_array = (char **)langs->pdata;
+ langs_str = g_strjoinv (", ", langs_array);
+
+ g_strfreev (langs_array);
+ g_ptr_array_free (langs, FALSE);
+
+ return langs_str;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupSession *session = SOUP_SESSION (object);
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupURI *uri;
+ gboolean ca_file_changed = FALSE;
+ const char *new_ca_file, *user_agent;
+ SoupSessionFeature *feature;
+
+ switch (prop_id) {
+ case PROP_PROXY_URI:
+ uri = g_value_get_boxed (value);
+
+ if (uri) {
+ soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
+ feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
+ soup_session_add_feature (session, feature);
+ g_object_unref (feature);
+ } else
+ soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
+
+ soup_session_abort (session);
+ break;
+ case PROP_MAX_CONNS:
+ priv->max_conns = g_value_get_int (value);
+ break;
+ case PROP_MAX_CONNS_PER_HOST:
+ priv->max_conns_per_host = g_value_get_int (value);
+ break;
+ case PROP_USE_NTLM:
+ feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
+ if (feature) {
+ if (g_value_get_boolean (value))
+ soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
+ else
+ soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
+ } else
+ g_warning ("Trying to set use-ntlm on session with no auth-manager");
+ break;
+ case PROP_SSL_CA_FILE:
+ new_ca_file = g_value_get_string (value);
+
+ if (!safe_str_equal (priv->ssl_ca_file, new_ca_file))
+ ca_file_changed = TRUE;
+
+ g_free (priv->ssl_ca_file);
+ priv->ssl_ca_file = g_strdup (new_ca_file);
+
+ if (ca_file_changed && priv->ssl_creds) {
+ soup_ssl_free_client_credentials (priv->ssl_creds);
+ priv->ssl_creds = NULL;
+ }
+
+ break;
+ case PROP_SSL_STRICT:
+ priv->ssl_strict = g_value_get_boolean (value);
+ break;
+ case PROP_ASYNC_CONTEXT:
+ priv->async_context = g_value_get_pointer (value);
+ if (priv->async_context)
+ g_main_context_ref (priv->async_context);
+ break;
+ case PROP_TIMEOUT:
+ priv->io_timeout = g_value_get_uint (value);
+ break;
+ case PROP_USER_AGENT:
+ g_free (priv->user_agent);
+ user_agent = g_value_get_string (value);
+ if (!user_agent)
+ priv->user_agent = NULL;
+ else if (!*user_agent) {
+ priv->user_agent =
+ g_strdup (SOUP_SESSION_USER_AGENT_BASE);
+ } else if (g_str_has_suffix (user_agent, " ")) {
+ priv->user_agent =
+ g_strdup_printf ("%s%s", user_agent,
+ SOUP_SESSION_USER_AGENT_BASE);
+ } else
+ priv->user_agent = g_strdup (user_agent);
+ break;
+ case PROP_ACCEPT_LANGUAGE:
+ g_free (priv->accept_language);
+ priv->accept_language = g_strdup (g_value_get_string (value));
+ priv->accept_language_auto = FALSE;
+ break;
+ case PROP_ACCEPT_LANGUAGE_AUTO:
+ priv->accept_language_auto = g_value_get_boolean (value);
+ if (priv->accept_language) {
+ g_free (priv->accept_language);
+ priv->accept_language = NULL;
+ }
+
+ /* Get languages from system if needed */
+ if (priv->accept_language_auto)
+ priv->accept_language = accept_languages_from_system ();
+ break;
+ case PROP_IDLE_TIMEOUT:
+ priv->idle_timeout = g_value_get_uint (value);
+ break;
+ case PROP_ADD_FEATURE:
+ soup_session_add_feature (session, g_value_get_object (value));
+ break;
+ case PROP_ADD_FEATURE_BY_TYPE:
+ soup_session_add_feature_by_type (session, g_value_get_gtype (value));
+ break;
+ case PROP_REMOVE_FEATURE_BY_TYPE:
+ soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupSession *session = SOUP_SESSION (object);
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupSessionFeature *feature;
+
+ switch (prop_id) {
+ case PROP_PROXY_URI:
+ feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
+ if (feature) {
+ g_object_get_property (G_OBJECT (feature),
+ SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
+ value);
+ } else
+ g_value_set_boxed (value, NULL);
+ break;
+ case PROP_MAX_CONNS:
+ g_value_set_int (value, priv->max_conns);
+ break;
+ case PROP_MAX_CONNS_PER_HOST:
+ g_value_set_int (value, priv->max_conns_per_host);
+ break;
+ case PROP_USE_NTLM:
+ feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
+ if (feature)
+ g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
+ else
+ g_value_set_boolean (value, FALSE);
+ break;
+ case PROP_SSL_CA_FILE:
+ g_value_set_string (value, priv->ssl_ca_file);
+ break;
+ case PROP_SSL_STRICT:
+ g_value_set_boolean (value, priv->ssl_strict);
+ break;
+ case PROP_ASYNC_CONTEXT:
+ g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
+ break;
+ case PROP_TIMEOUT:
+ g_value_set_uint (value, priv->io_timeout);
+ break;
+ case PROP_USER_AGENT:
+ g_value_set_string (value, priv->user_agent);
+ break;
+ case PROP_ACCEPT_LANGUAGE:
+ g_value_set_string (value, priv->accept_language);
+ break;
+ case PROP_ACCEPT_LANGUAGE_AUTO:
+ g_value_set_boolean (value, priv->accept_language_auto);
+ break;
+ case PROP_IDLE_TIMEOUT:
+ g_value_set_uint (value, priv->idle_timeout);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+/**
+ * soup_session_get_async_context:
+ * @session: a #SoupSession
+ *
+ * Gets @session's async_context. This does not add a ref to the
+ * context, so you will need to ref it yourself if you want it to
+ * outlive its session.
+ *
+ * Return value: (transfer none): @session's #GMainContext, which may
+ * be %NULL
+ **/
+GMainContext *
+soup_session_get_async_context (SoupSession *session)
+{
+ SoupSessionPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ return priv->async_context;
+}
+
+/* Hosts */
+
+static SoupSessionHost *
+soup_session_host_new (SoupSession *session, SoupURI *uri)
+{
+ SoupSessionHost *host;
+
+ host = g_slice_new0 (SoupSessionHost);
+ host->uri = soup_uri_copy_host (uri);
+ host->addr = soup_address_new (host->uri->host, host->uri->port);
+
+ return host;
+}
+
+/* Requires host_lock to be locked */
+static SoupSessionHost *
+get_host_for_uri (SoupSession *session, SoupURI *uri)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupSessionHost *host;
+
+ host = g_hash_table_lookup (priv->hosts, uri);
+ if (host)
+ return host;
+
+ host = soup_session_host_new (session, uri);
+ g_hash_table_insert (priv->hosts, host->uri, host);
+
+ return host;
+}
+
+/* Note: get_host_for_message doesn't lock the host_lock. The caller
+ * must do it itself if there's a chance the host doesn't already
+ * exist.
+ */
+static SoupSessionHost *
+get_host_for_message (SoupSession *session, SoupMessage *msg)
+{
+ return get_host_for_uri (session, soup_message_get_uri (msg));
+}
+
+static void
+free_host (SoupSessionHost *host)
+{
+ while (host->connections) {
+ SoupConnection *conn = host->connections->data;
+
+ host->connections = g_slist_remove (host->connections, conn);
+ soup_connection_disconnect (conn);
+ }
+
+ soup_uri_free (host->uri);
+ g_object_unref (host->addr);
+ g_slice_free (SoupSessionHost, host);
+}
+
+static void
+auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying)
+{
+ g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
+}
+
+static void
+auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying,
+ gpointer session)
+{
+ SOUP_SESSION_GET_CLASS (session)->auth_required (
+ session, msg, auth, retrying);
+}
+
+#define SOUP_METHOD_IS_SAFE(method) (method == SOUP_METHOD_GET || \
+ method == SOUP_METHOD_HEAD || \
+ method == SOUP_METHOD_OPTIONS || \
+ method == SOUP_METHOD_PROPFIND)
+
+static void
+redirect_handler (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+ SoupSession *session = item->session;
+ const char *new_loc;
+ SoupURI *new_uri;
+
+ new_loc = soup_message_headers_get_one (msg->response_headers,
+ "Location");
+ g_return_if_fail (new_loc != NULL);
+
+ if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
+ soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
+ return;
+ }
+ item->redirection_count++;
+
+ if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
+ (msg->status_code == SOUP_STATUS_FOUND &&
+ !SOUP_METHOD_IS_SAFE (msg->method)) ||
+ (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY &&
+ msg->method == SOUP_METHOD_POST)) {
+ if (msg->method != SOUP_METHOD_HEAD) {
+ /* Redirect using a GET */
+ g_object_set (msg,
+ SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
+ NULL);
+ }
+ soup_message_set_request (msg, NULL,
+ SOUP_MEMORY_STATIC, NULL, 0);
+ soup_message_headers_set_encoding (msg->request_headers,
+ SOUP_ENCODING_NONE);
+ } else if (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY ||
+ msg->status_code == SOUP_STATUS_TEMPORARY_REDIRECT ||
+ msg->status_code == SOUP_STATUS_FOUND) {
+ /* Don't redirect non-safe methods */
+ if (!SOUP_METHOD_IS_SAFE (msg->method))
+ return;
+ } else {
+ /* Three possibilities:
+ *
+ * 1) This was a non-3xx response that happened to
+ * have a "Location" header
+ * 2) It's a non-redirecty 3xx response (300, 304,
+ * 305, 306)
+ * 3) It's some newly-defined 3xx response (308+)
+ *
+ * We ignore all of these cases. In the first two,
+ * redirecting would be explicitly wrong, and in the
+ * last case, we have no clue if the 3xx response is
+ * supposed to be redirecty or non-redirecty. Plus,
+ * 2616 says unrecognized status codes should be
+ * treated as the equivalent to the x00 code, and we
+ * don't redirect on 300, so therefore we shouldn't
+ * redirect on 308+ either.
+ */
+ return;
+ }
+
+ /* Location is supposed to be an absolute URI, but some sites
+ * are lame, so we use soup_uri_new_with_base().
+ */
+ new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
+ if (!new_uri || !new_uri->host) {
+ if (new_uri)
+ soup_uri_free (new_uri);
+ soup_message_set_status_full (msg,
+ SOUP_STATUS_MALFORMED,
+ "Invalid Redirect URL");
+ return;
+ }
+
+ soup_message_set_uri (msg, new_uri);
+ soup_uri_free (new_uri);
+
+ soup_session_requeue_message (session, msg);
+}
+
+void
+soup_session_send_queue_item (SoupSession *session,
+ SoupMessageQueueItem *item,
+ SoupMessageCompletionFn completion_cb)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ const char *conn_header;
+
+ if (priv->user_agent) {
+ soup_message_headers_replace (item->msg->request_headers,
+ "User-Agent", priv->user_agent);
+ }
+
+ if (priv->accept_language &&
+ !soup_message_headers_get_list (item->msg->request_headers,
+ "Accept-Language")) {
+ soup_message_headers_append (item->msg->request_headers,
+ "Accept-Language",
+ priv->accept_language);
+ }
+
+ /* Force keep alive connections for HTTP 1.0. Performance will
+ * improve when issuing multiple requests to the same host in
+ * a short period of time, as we wouldn't need to establish
+ * new connections. Keep alive is implicit for HTTP 1.1.
+ */
+ conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
+ if (!conn_header ||
+ (!soup_header_contains (conn_header, "Keep-Alive") &&
+ !soup_header_contains (conn_header, "close")))
+ soup_message_headers_append (item->msg->request_headers,
+ "Connection", "Keep-Alive");
+
+ g_signal_emit (session, signals[REQUEST_STARTED], 0,
+ item->msg, soup_connection_get_socket (item->conn));
+ soup_connection_send_request (item->conn, item, completion_cb, item);
+}
+
+gboolean
+soup_session_cleanup_connections (SoupSession *session,
+ gboolean prune_idle)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ GSList *conns = NULL, *c;
+ GHashTableIter iter;
+ gpointer conn, host;
+ SoupConnectionState state;
+
+ g_mutex_lock (priv->host_lock);
+ g_hash_table_iter_init (&iter, priv->conns);
+ while (g_hash_table_iter_next (&iter, &conn, &host)) {
+ state = soup_connection_get_state (conn);
+ if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
+ (prune_idle && state == SOUP_CONNECTION_IDLE))
+ conns = g_slist_prepend (conns, g_object_ref (conn));
+ }
+ g_mutex_unlock (priv->host_lock);
+
+ if (!conns)
+ return FALSE;
+
+ for (c = conns; c; c = c->next) {
+ conn = c->data;
+ soup_connection_disconnect (conn);
+ g_object_unref (conn);
+ }
+ g_slist_free (conns);
+
+ return TRUE;
+}
+
+static void
+connection_disconnected (SoupConnection *conn, gpointer user_data)
+{
+ SoupSession *session = user_data;
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupSessionHost *host;
+
+ g_mutex_lock (priv->host_lock);
+
+ host = g_hash_table_lookup (priv->conns, conn);
+ if (host) {
+ g_hash_table_remove (priv->conns, conn);
+ host->connections = g_slist_remove (host->connections, conn);
+ host->num_conns--;
+
+ if (soup_connection_get_ssl_fallback (conn))
+ host->ssl_fallback = TRUE;
+ }
+
+ g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
+ priv->num_conns--;
+
+ g_mutex_unlock (priv->host_lock);
+ g_object_unref (conn);
+}
+
+SoupMessageQueueItem *
+soup_session_make_connect_message (SoupSession *session,
+ SoupConnection *conn)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupAddress *server_addr = soup_connection_get_tunnel_addr (conn);
+ SoupURI *uri;
+ SoupMessage *msg;
+ SoupMessageQueueItem *item;
+
+ uri = soup_uri_new (NULL);
+ soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
+ soup_uri_set_host (uri, soup_address_get_name (server_addr));
+ soup_uri_set_port (uri, soup_address_get_port (server_addr));
+ soup_uri_set_path (uri, "");
+ msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
+ soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
+ soup_uri_free (uri);
+
+ /* Call the base implementation of soup_session_queue_message
+ * directly, to add msg to the SoupMessageQueue and cause all
+ * the right signals to be emitted.
+ */
+ queue_message (session, msg, NULL, NULL);
+ item = soup_message_queue_lookup (priv->queue, msg);
+ item->conn = g_object_ref (conn);
+ g_object_unref (msg);
+
+ g_signal_emit (session, signals[TUNNELING], 0, conn);
+ return item;
+}
+
+gboolean
+soup_session_get_connection (SoupSession *session,
+ SoupMessageQueueItem *item,
+ gboolean *try_pruning)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupConnection *conn;
+ SoupSessionHost *host;
+ SoupAddress *remote_addr, *tunnel_addr;
+ SoupSSLCredentials *ssl_creds;
+ GSList *conns;
+ int num_pending = 0;
+ SoupURI *uri;
+
+ if (item->conn) {
+ g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
+ return TRUE;
+ }
+
+ g_mutex_lock (priv->host_lock);
+
+ host = get_host_for_message (session, item->msg);
+ for (conns = host->connections; conns; conns = conns->next) {
+ if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
+ soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
+ g_mutex_unlock (priv->host_lock);
+ item->conn = g_object_ref (conns->data);
+ return TRUE;
+ } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
+ num_pending++;
+ }
+
+ /* Limit the number of pending connections; num_messages / 2
+ * is somewhat arbitrary...
+ */
+ if (num_pending > host->num_messages / 2) {
+ g_mutex_unlock (priv->host_lock);
+ return FALSE;
+ }
+
+ if (host->num_conns >= priv->max_conns_per_host) {
+ g_mutex_unlock (priv->host_lock);
+ return FALSE;
+ }
+
+ if (priv->num_conns >= priv->max_conns) {
+ *try_pruning = TRUE;
+ g_mutex_unlock (priv->host_lock);
+ return FALSE;
+ }
+
+ if (item->proxy_addr) {
+ remote_addr = item->proxy_addr;
+ tunnel_addr = NULL;
+ } else {
+ remote_addr = host->addr;
+ tunnel_addr = NULL;
+ }
+
+ uri = soup_message_get_uri (item->msg);
+ if (uri->scheme == SOUP_URI_SCHEME_HTTPS) {
+ if (!priv->ssl_creds)
+ priv->ssl_creds = soup_ssl_get_client_credentials (priv->ssl_ca_file);
+ ssl_creds = priv->ssl_creds;
+
+ if (item->proxy_addr)
+ tunnel_addr = host->addr;
+ } else
+ ssl_creds = NULL;
+
+ conn = soup_connection_new (
+ SOUP_CONNECTION_REMOTE_ADDRESS, remote_addr,
+ SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr,
+ SOUP_CONNECTION_PROXY_URI, item->proxy_uri,
+ SOUP_CONNECTION_SSL_CREDENTIALS, ssl_creds,
+ SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict,
+ SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
+ SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
+ SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
+ SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
+ NULL);
+ g_signal_connect (conn, "disconnected",
+ G_CALLBACK (connection_disconnected),
+ session);
+
+ g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
+
+ g_hash_table_insert (priv->conns, conn, host);
+
+ priv->num_conns++;
+ host->num_conns++;
+ host->connections = g_slist_prepend (host->connections, conn);
+
+ g_mutex_unlock (priv->host_lock);
+ item->conn = g_object_ref (conn);
+ return TRUE;
+}
+
+SoupMessageQueue *
+soup_session_get_queue (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ return priv->queue;
+}
+
+void
+soup_session_unqueue_item (SoupSession *session,
+ SoupMessageQueueItem *item)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupSessionHost *host;
+
+ if (item->conn) {
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ }
+
+ if (item->state != SOUP_MESSAGE_FINISHED) {
+ g_warning ("finished an item with state %d", item->state);
+ return;
+ }
+
+ soup_message_queue_remove (priv->queue, item);
+
+ g_mutex_lock (priv->host_lock);
+ host = get_host_for_message (session, item->msg);
+ host->num_messages--;
+ g_mutex_unlock (priv->host_lock);
+
+ /* g_signal_handlers_disconnect_by_func doesn't work if you
+ * have a metamarshal, meaning it doesn't work with
+ * soup_message_add_header_handler()
+ */
+ g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, item);
+ g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
+ soup_message_queue_item_unref (item);
+}
+
+void
+soup_session_set_item_status (SoupSession *session,
+ SoupMessageQueueItem *item,
+ guint status_code)
+{
+ SoupURI *uri;
+ char *msg;
+
+ switch (status_code) {
+ case SOUP_STATUS_CANT_RESOLVE:
+ case SOUP_STATUS_CANT_CONNECT:
+ uri = soup_message_get_uri (item->msg);
+ msg = g_strdup_printf ("%s (%s)",
+ soup_status_get_phrase (status_code),
+ uri->host);
+ soup_message_set_status_full (item->msg, status_code, msg);
+ g_free (msg);
+ break;
+
+ case SOUP_STATUS_CANT_RESOLVE_PROXY:
+ case SOUP_STATUS_CANT_CONNECT_PROXY:
+ if (item->proxy_uri && item->proxy_uri->host) {
+ msg = g_strdup_printf ("%s (%s)",
+ soup_status_get_phrase (status_code),
+ item->proxy_uri->host);
+ soup_message_set_status_full (item->msg, status_code, msg);
+ g_free (msg);
+ break;
+ }
+ soup_message_set_status (item->msg, status_code);
+ break;
+
+ case SOUP_STATUS_SSL_FAILED:
+ if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
+ soup_message_set_status_full (item->msg, status_code,
+ "TLS/SSL support not available; install glib-networking");
+ } else
+ soup_message_set_status (item->msg, status_code);
+ break;
+
+ default:
+ soup_message_set_status (item->msg, status_code);
+ break;
+ }
+}
+
+static void
+queue_message (SoupSession *session, SoupMessage *msg,
+ SoupSessionCallback callback, gpointer user_data)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupMessageQueueItem *item;
+ SoupSessionHost *host;
+
+ item = soup_message_queue_append (priv->queue, msg, callback, user_data);
+
+ g_mutex_lock (priv->host_lock);
+ host = get_host_for_message (session, item->msg);
+ host->num_messages++;
+ g_mutex_unlock (priv->host_lock);
+
+ if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
+ soup_message_add_header_handler (
+ msg, "got_body", "Location",
+ G_CALLBACK (redirect_handler), item);
+ }
+
+ g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
+}
+
+/**
+ * SoupSessionCallback:
+ * @session: the session
+ * @msg: the message that has finished
+ * @user_data: the data passed to soup_session_queue_message
+ *
+ * Prototype for the callback passed to soup_session_queue_message(),
+ * qv.
+ **/
+
+/**
+ * soup_session_queue_message:
+ * @session: a #SoupSession
+ * @msg: (transfer full): the message to queue
+ * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
+ * be called after the message completes or when an unrecoverable error occurs.
+ * @user_data: (allow-none): a pointer passed to @callback.
+ *
+ * Queues the message @msg for sending. All messages are processed
+ * while the glib main loop runs. If @msg has been processed before,
+ * any resources related to the time it was last sent are freed.
+ *
+ * Upon message completion, the callback specified in @callback will
+ * be invoked (in the thread associated with @session's async
+ * context). If after returning from this callback the message has not
+ * been requeued, @msg will be unreffed.
+ */
+void
+soup_session_queue_message (SoupSession *session, SoupMessage *msg,
+ SoupSessionCallback callback, gpointer user_data)
+{
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
+ callback, user_data);
+}
+
+static void
+requeue_message (SoupSession *session, SoupMessage *msg)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupMessageQueueItem *item;
+
+ item = soup_message_queue_lookup (priv->queue, msg);
+ g_return_if_fail (item != NULL);
+ item->state = SOUP_MESSAGE_RESTARTING;
+ soup_message_queue_item_unref (item);
+}
+
+/**
+ * soup_session_requeue_message:
+ * @session: a #SoupSession
+ * @msg: the message to requeue
+ *
+ * This causes @msg to be placed back on the queue to be attempted
+ * again.
+ **/
+void
+soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
+{
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
+}
+
+
+/**
+ * soup_session_send_message:
+ * @session: a #SoupSession
+ * @msg: the message to send
+ *
+ * Synchronously send @msg. This call will not return until the
+ * transfer is finished successfully or there is an unrecoverable
+ * error.
+ *
+ * @msg is not freed upon return.
+ *
+ * Return value: the HTTP status code of the response
+ */
+guint
+soup_session_send_message (SoupSession *session, SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
+
+ return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
+}
+
+
+/**
+ * soup_session_pause_message:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage currently running on @session
+ *
+ * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
+ * resume I/O.
+ **/
+void
+soup_session_pause_message (SoupSession *session,
+ SoupMessage *msg)
+{
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+#if ENABLE(TIZEN_FIX_PAUSE_MESSAGE)
+ if(soup_message_io_in_progress (msg))
+ soup_message_io_pause (msg);
+#else
+ soup_message_io_pause (msg);
+#endif
+}
+
+/**
+ * soup_session_unpause_message:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage currently running on @session
+ *
+ * Resumes HTTP I/O on @msg. Use this to resume after calling
+ * soup_session_pause_message().
+ *
+ * If @msg is being sent via blocking I/O, this will resume reading or
+ * writing immediately. If @msg is using non-blocking I/O, then
+ * reading or writing won't resume until you return to the main loop.
+ **/
+void
+soup_session_unpause_message (SoupSession *session,
+ SoupMessage *msg)
+{
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+#if ENABLE(TIZEN_FIX_PAUSE_MESSAGE)
+ if(soup_message_io_in_progress (msg))
+ soup_message_io_unpause (msg);
+#else
+ soup_message_io_unpause (msg);
+#endif
+}
+
+
+static void
+cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupMessageQueueItem *item;
+
+ item = soup_message_queue_lookup (priv->queue, msg);
+ g_return_if_fail (item != NULL);
+
+ soup_message_set_status (msg, status_code);
+ g_cancellable_cancel (item->cancellable);
+
+ soup_message_queue_item_unref (item);
+}
+
+/**
+ * soup_session_cancel_message:
+ * @session: a #SoupSession
+ * @msg: the message to cancel
+ * @status_code: status code to set on @msg (generally
+ * %SOUP_STATUS_CANCELLED)
+ *
+ * Causes @session to immediately finish processing @msg (regardless
+ * of its current state) with a final status_code of @status_code. You
+ * may call this at any time after handing @msg off to @session; if
+ * @session has started sending the request but has not yet received
+ * the complete response, then it will close the request's connection.
+ * Note that with non-idempotent requests (eg, %POST, %PUT, %DELETE)
+ * it is possible that you might cancel the request after the server
+ * acts on it, but before it returns a response, leaving the remote
+ * resource in an unknown state.
+ *
+ * If the message is cancelled while its response body is being read,
+ * then the response body in @msg will be left partially-filled-in.
+ * The response headers, on the other hand, will always be either
+ * empty or complete.
+ *
+ * For messages queued with soup_session_queue_message() (and
+ * cancelled from the same thread), the callback will be invoked
+ * before soup_session_cancel_message() returns.
+ **/
+void
+soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
+ guint status_code)
+{
+ SoupSessionPrivate *priv;
+ SoupMessageQueueItem *item;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+ item = soup_message_queue_lookup (priv->queue, msg);
+ /* If the message is already ending, don't do anything */
+ if (!item)
+ return;
+ if (item->state == SOUP_MESSAGE_FINISHED) {
+ soup_message_queue_item_unref (item);
+ return;
+ }
+
+ SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
+ soup_message_queue_item_unref (item);
+}
+
+static void
+gather_conns (gpointer key, gpointer host, gpointer data)
+{
+ SoupConnection *conn = key;
+ GSList **conns = data;
+
+ *conns = g_slist_prepend (*conns, g_object_ref (conn));
+}
+
+static void
+flush_queue (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupMessageQueueItem *item;
+
+ for (item = soup_message_queue_first (priv->queue);
+ item;
+ item = soup_message_queue_next (priv->queue, item)) {
+ soup_session_cancel_message (session, item->msg,
+ SOUP_STATUS_CANCELLED);
+ }
+}
+
+/**
+ * soup_session_abort:
+ * @session: the session
+ *
+ * Cancels all pending requests in @session.
+ **/
+void
+soup_session_abort (SoupSession *session)
+{
+ SoupSessionPrivate *priv;
+ GSList *conns, *c;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
+
+ /* Close all connections */
+ g_mutex_lock (priv->host_lock);
+ conns = NULL;
+ g_hash_table_foreach (priv->conns, gather_conns, &conns);
+
+ g_mutex_unlock (priv->host_lock);
+ for (c = conns; c; c = c->next) {
+ soup_connection_disconnect (c->data);
+ g_object_unref (c->data);
+ }
+
+ g_slist_free (conns);
+}
+
+/**
+* soup_session_prepare_for_uri:
+* @session: a #SoupSession
+* @uri: a #SoupURI which may be required
+*
+* Tells @session that @uri may be requested shortly, and so the
+* session can try to prepare (resolving the domain name, obtaining
+* proxy address, etc.) in order to work more quickly once the URI is
+* actually requested.
+*
+* This method acts asynchronously, in @session's %async_context.
+* If you are using #SoupSessionSync and do not have a main loop running,
+* then you can't use this method.
+*
+* Since: 2.30
+**/
+void
+soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
+{
+ SoupSessionPrivate *priv;
+ SoupSessionHost *host;
+ SoupAddress *addr;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (uri != NULL);
+
+ if (!uri->host)
+ return;
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ g_mutex_lock (priv->host_lock);
+ host = get_host_for_uri (session, uri);
+ addr = g_object_ref (host->addr);
+ g_mutex_unlock (priv->host_lock);
+
+ soup_address_resolve_async (addr, priv->async_context,
+ NULL, NULL, NULL);
+ g_object_unref (addr);
+}
+
+/**
+ * soup_session_add_feature:
+ * @session: a #SoupSession
+ * @feature: an object that implements #SoupSessionFeature
+ *
+ * Adds @feature's functionality to @session. You can also add a
+ * feature to the session at construct time by using the
+ * %SOUP_SESSION_ADD_FEATURE property.
+ *
+ * Since: 2.24
+ **/
+void
+soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
+{
+ SoupSessionPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+ priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
+ g_hash_table_remove_all (priv->features_cache);
+ soup_session_feature_attach (feature, session);
+}
+
+/**
+ * soup_session_add_feature_by_type:
+ * @session: a #SoupSession
+ * @feature_type: a #GType
+ *
+ * If @feature_type is the type of a class that implements
+ * #SoupSessionFeature, this creates a new feature of that type and
+ * adds it to @session as with soup_session_add_feature(). You can use
+ * this when you don't need to customize the new feature in any way.
+ *
+ * If @feature_type is not a #SoupSessionFeature type, this gives
+ * each existing feature on @session the chance to accept @feature_type
+ * as a "subfeature". This can be used to add new #SoupAuth types,
+ * for instance.
+ *
+ * You can also add a feature to the session at construct time by
+ * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
+ *
+ * Since: 2.24
+ **/
+void
+soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
+{
+ g_return_if_fail (SOUP_IS_SESSION (session));
+
+ if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
+ SoupSessionFeature *feature;
+
+ feature = g_object_new (feature_type, NULL);
+ soup_session_add_feature (session, feature);
+ g_object_unref (feature);
+ } else {
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ GSList *f;
+
+ for (f = priv->features; f; f = f->next) {
+ if (soup_session_feature_add_feature (f->data, feature_type))
+ return;
+ }
+ g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
+ }
+}
+
+/**
+ * soup_session_remove_feature:
+ * @session: a #SoupSession
+ * @feature: a feature that has previously been added to @session
+ *
+ * Removes @feature's functionality from @session.
+ *
+ * Since: 2.24
+ **/
+void
+soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
+{
+ SoupSessionPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+ if (g_slist_find (priv->features, feature)) {
+ priv->features = g_slist_remove (priv->features, feature);
+ g_hash_table_remove_all (priv->features_cache);
+ soup_session_feature_detach (feature, session);
+ g_object_unref (feature);
+ }
+}
+
+/**
+ * soup_session_remove_feature_by_type:
+ * @session: a #SoupSession
+ * @feature_type: a #GType
+ *
+ * Removes all features of type @feature_type (or any subclass of
+ * @feature_type) from @session. You can also remove standard features
+ * from the session at construct time by using the
+ * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
+ *
+ * Since: 2.24
+ **/
+void
+soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
+{
+ SoupSessionPrivate *priv;
+ GSList *f;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
+ restart:
+ for (f = priv->features; f; f = f->next) {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
+ soup_session_remove_feature (session, f->data);
+ goto restart;
+ }
+ }
+ } else {
+ for (f = priv->features; f; f = f->next) {
+ if (soup_session_feature_remove_feature (f->data, feature_type))
+ return;
+ }
+ g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
+ }
+}
+
+/**
+ * soup_session_get_features:
+ * @session: a #SoupSession
+ * @feature_type: the #GType of the class of features to get
+ *
+ * Generates a list of @session's features of type @feature_type. (If
+ * you want to see all features, you can pass %G_TYPE_SESSION_FEATURE
+ * for @feature_type.)
+ *
+ * Return value: (transfer container) (element-type Soup.SessionFeature):
+ * a list of features. You must free the list, but not its contents
+ *
+ * Since: 2.26
+ **/
+GSList *
+soup_session_get_features (SoupSession *session, GType feature_type)
+{
+ SoupSessionPrivate *priv;
+ GSList *f, *ret;
+
+ g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+ for (f = priv->features, ret = NULL; f; f = f->next) {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
+ ret = g_slist_prepend (ret, f->data);
+ }
+ return g_slist_reverse (ret);
+}
+
+/**
+ * soup_session_get_feature:
+ * @session: a #SoupSession
+ * @feature_type: the #GType of the feature to get
+ *
+ * Gets the first feature in @session of type @feature_type. For
+ * features where there may be more than one feature of a given type,
+ * use soup_session_get_features().
+ *
+ * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
+ * feature is owned by @session.
+ *
+ * Since: 2.26
+ **/
+SoupSessionFeature *
+soup_session_get_feature (SoupSession *session, GType feature_type)
+{
+ SoupSessionPrivate *priv;
+ SoupSessionFeature *feature;
+ GSList *f;
+
+ g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ feature = g_hash_table_lookup (priv->features_cache,
+ GSIZE_TO_POINTER (feature_type));
+ if (feature)
+ return feature;
+
+ for (f = priv->features; f; f = f->next) {
+ feature = f->data;
+ if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
+ g_hash_table_insert (priv->features_cache,
+ GSIZE_TO_POINTER (feature_type),
+ feature);
+ return feature;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * soup_session_get_feature_for_message:
+ * @session: a #SoupSession
+ * @feature_type: the #GType of the feature to get
+ * @msg: a #SoupMessage
+ *
+ * Gets the first feature in @session of type @feature_type, provided
+ * that it is not disabled for @msg. As with
+ * soup_session_get_feature(), this should only be used for features
+ * where @feature_type is only expected to match a single feature. In
+ * particular, if there are two matching features, and the first is
+ * disabled on @msg, and the second is not, then this will return
+ * %NULL, not the second feature.
+ *
+ * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
+ * feature is owned by @session.
+ *
+ * Since: 2.28
+ **/
+SoupSessionFeature *
+soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
+ SoupMessage *msg)
+{
+ SoupSessionFeature *feature;
+
+ feature = soup_session_get_feature (session, feature_type);
+ if (feature && soup_message_disables_feature (msg, feature))
+ return NULL;
+ return feature;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SESSION_H
+#define SOUP_SESSION_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_SESSION (soup_session_get_type ())
+#define SOUP_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION, SoupSession))
+#define SOUP_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SESSION, SoupSessionClass))
+#define SOUP_IS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SESSION))
+#define SOUP_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SESSION))
+#define SOUP_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SESSION, SoupSessionClass))
+
+typedef void (*SoupSessionCallback) (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data);
+
+struct _SoupSession {
+ GObject parent;
+
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*request_started) (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket);
+ void (*authenticate) (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
+
+ /* methods */
+ void (*queue_message) (SoupSession *session, SoupMessage *msg,
+ SoupSessionCallback callback,
+ gpointer user_data);
+ void (*requeue_message) (SoupSession *session, SoupMessage *msg);
+ guint (*send_message) (SoupSession *session, SoupMessage *msg);
+
+ void (*cancel_message) (SoupSession *session, SoupMessage *msg,
+ guint status_code);
+
+ void (*auth_required) (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
+
+ void (*flush_queue) (SoupSession *session);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupSessionClass;
+
+GType soup_session_get_type (void);
+
+#define SOUP_SESSION_PROXY_URI "proxy-uri"
+#define SOUP_SESSION_MAX_CONNS "max-conns"
+#define SOUP_SESSION_MAX_CONNS_PER_HOST "max-conns-per-host"
+#define SOUP_SESSION_USE_NTLM "use-ntlm"
+#define SOUP_SESSION_SSL_CA_FILE "ssl-ca-file"
+#define SOUP_SESSION_SSL_STRICT "ssl-strict"
+#define SOUP_SESSION_ASYNC_CONTEXT "async-context"
+#define SOUP_SESSION_TIMEOUT "timeout"
+#define SOUP_SESSION_USER_AGENT "user-agent"
+#define SOUP_SESSION_ACCEPT_LANGUAGE "accept-language"
+#define SOUP_SESSION_ACCEPT_LANGUAGE_AUTO "accept-language-auto"
+#define SOUP_SESSION_IDLE_TIMEOUT "idle-timeout"
+#define SOUP_SESSION_ADD_FEATURE "add-feature"
+#define SOUP_SESSION_ADD_FEATURE_BY_TYPE "add-feature-by-type"
+#define SOUP_SESSION_REMOVE_FEATURE_BY_TYPE "remove-feature-by-type"
+
+GMainContext *soup_session_get_async_context(SoupSession *session);
+
+void soup_session_queue_message (SoupSession *session,
+ SoupMessage *msg,
+ SoupSessionCallback callback,
+ gpointer user_data);
+void soup_session_requeue_message (SoupSession *session,
+ SoupMessage *msg);
+
+guint soup_session_send_message (SoupSession *session,
+ SoupMessage *msg);
+
+void soup_session_pause_message (SoupSession *session,
+ SoupMessage *msg);
+void soup_session_unpause_message (SoupSession *session,
+ SoupMessage *msg);
+
+void soup_session_cancel_message (SoupSession *session,
+ SoupMessage *msg,
+ guint status_code);
+void soup_session_abort (SoupSession *session);
+
+void soup_session_prepare_for_uri (SoupSession *session,
+ SoupURI *uri);
+
+void soup_session_add_feature (SoupSession *session,
+ SoupSessionFeature *feature);
+void soup_session_add_feature_by_type (SoupSession *session,
+ GType feature_type);
+void soup_session_remove_feature (SoupSession *session,
+ SoupSessionFeature *feature);
+void soup_session_remove_feature_by_type (SoupSession *session,
+ GType feature_type);
+GSList *soup_session_get_features (SoupSession *session,
+ GType feature_type);
+SoupSessionFeature *soup_session_get_feature (SoupSession *session,
+ GType feature_type);
+SoupSessionFeature *soup_session_get_feature_for_message(SoupSession *session,
+ GType feature_type,
+ SoupMessage *msg);
+
+G_END_DECLS
+
+#endif /* SOUP_SESSION_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-socket.c: Socket networking code.
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "soup-address.h"
+#include "soup-socket.h"
+#include "soup-marshal.h"
+#include "soup-misc.h"
+#include "soup-misc-private.h"
+#include "soup-ssl.h"
+
+/**
+ * SECTION:soup-socket
+ * @short_description: A network socket
+ *
+ * #SoupSocket is libsoup's TCP socket type. While it is primarily
+ * intended for internal use, #SoupSocket<!-- -->s are exposed in the
+ * API in various places, and some of their methods (eg,
+ * soup_socket_get_remote_address()) may be useful to applications.
+ **/
+
+G_DEFINE_TYPE (SoupSocket, soup_socket, G_TYPE_OBJECT)
+
+enum {
+ READABLE,
+ WRITABLE,
+ DISCONNECTED,
+ NEW_CONNECTION,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+
+ PROP_LOCAL_ADDRESS,
+ PROP_REMOTE_ADDRESS,
+ PROP_NON_BLOCKING,
+ PROP_IS_SERVER,
+ PROP_SSL_CREDENTIALS,
+ PROP_SSL_STRICT,
+ PROP_SSL_FALLBACK,
+ PROP_ASYNC_CONTEXT,
+ PROP_TIMEOUT,
+ PROP_TRUSTED_CERTIFICATE,
+ PROP_CLEAN_DISPOSE,
+ PROP_TLS_CERTIFICATE,
+ PROP_TLS_ERRORS,
+
+ LAST_PROP
+};
+
+typedef struct {
+ SoupAddress *local_addr, *remote_addr;
+ GIOStream *conn;
+ GSocket *gsock;
+ GPollableInputStream *istream;
+ GPollableOutputStream *ostream;
+ GTlsCertificateFlags tls_errors;
+
+ guint non_blocking:1;
+ guint is_server:1;
+ guint ssl_strict:1;
+ guint ssl_fallback:1;
+ guint ssl_ca_in_creds:1;
+ guint clean_dispose:1;
+ gpointer ssl_creds;
+
+ GMainContext *async_context;
+ GSource *watch_src;
+ GSource *read_src, *write_src;
+ GByteArray *read_buf;
+
+ GMutex *iolock, *addrlock;
+ guint timeout;
+
+ GCancellable *connect_cancel;
+} SoupSocketPrivate;
+#define SOUP_SOCKET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SOCKET, SoupSocketPrivate))
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static void soup_socket_peer_certificate_changed (GObject *conn,
+ GParamSpec *pspec,
+ gpointer user_data);
+
+static void
+soup_socket_init (SoupSocket *sock)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ priv->non_blocking = TRUE;
+ priv->addrlock = g_mutex_new ();
+ priv->iolock = g_mutex_new ();
+}
+
+static void
+disconnect_internal (SoupSocket *sock)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ if (priv->gsock) {
+ g_socket_close (priv->gsock, NULL);
+ g_object_unref (priv->gsock);
+ priv->gsock = NULL;
+ }
+ if (priv->conn) {
+ if (G_IS_TLS_CONNECTION (priv->conn))
+ g_signal_handlers_disconnect_by_func (priv->conn, soup_socket_peer_certificate_changed, sock);
+ g_object_unref (priv->conn);
+ priv->conn = NULL;
+ priv->istream = NULL;
+ priv->ostream = NULL;
+ }
+
+ if (priv->read_src) {
+ g_source_destroy (priv->read_src);
+ priv->read_src = NULL;
+ }
+ if (priv->write_src) {
+ g_source_destroy (priv->write_src);
+ priv->write_src = NULL;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
+
+ if (priv->connect_cancel) {
+ if (priv->clean_dispose)
+ g_warning ("Disposing socket %p during connect", object);
+ g_object_unref (priv->connect_cancel);
+ }
+ if (priv->conn) {
+ if (priv->clean_dispose)
+ g_warning ("Disposing socket %p while still connected", object);
+ disconnect_internal (SOUP_SOCKET (object));
+ }
+
+ if (priv->local_addr)
+ g_object_unref (priv->local_addr);
+ if (priv->remote_addr)
+ g_object_unref (priv->remote_addr);
+
+ if (priv->watch_src) {
+ if (priv->clean_dispose && !priv->is_server)
+ g_warning ("Disposing socket %p during async op", object);
+ g_source_destroy (priv->watch_src);
+ }
+ if (priv->async_context)
+ g_main_context_unref (priv->async_context);
+
+ if (priv->read_buf)
+ g_byte_array_free (priv->read_buf, TRUE);
+
+ g_mutex_free (priv->addrlock);
+ g_mutex_free (priv->iolock);
+
+ G_OBJECT_CLASS (soup_socket_parent_class)->finalize (object);
+}
+
+static void
+soup_socket_class_init (SoupSocketClass *socket_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (socket_class);
+
+ g_type_class_add_private (socket_class, sizeof (SoupSocketPrivate));
+
+ /* virtual method override */
+ object_class->finalize = finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /* signals */
+
+ /**
+ * SoupSocket::readable:
+ * @sock: the socket
+ *
+ * Emitted when an async socket is readable. See
+ * soup_socket_read(), soup_socket_read_until() and
+ * #SoupSocket:non-blocking.
+ **/
+ signals[READABLE] =
+ g_signal_new ("readable",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SoupSocketClass, readable),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupSocket::writable:
+ * @sock: the socket
+ *
+ * Emitted when an async socket is writable. See
+ * soup_socket_write() and #SoupSocket:non-blocking.
+ **/
+ signals[WRITABLE] =
+ g_signal_new ("writable",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SoupSocketClass, writable),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupSocket::disconnected:
+ * @sock: the socket
+ *
+ * Emitted when the socket is disconnected, for whatever
+ * reason.
+ **/
+ signals[DISCONNECTED] =
+ g_signal_new ("disconnected",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SoupSocketClass, disconnected),
+ NULL, NULL,
+ soup_marshal_NONE__NONE,
+ G_TYPE_NONE, 0);
+
+ /**
+ * SoupSocket::new-connection:
+ * @sock: the socket
+ * @new: the new socket
+ *
+ * Emitted when a listening socket (set up with
+ * soup_socket_listen()) receives a new connection.
+ *
+ * You must ref the @new if you want to keep it; otherwise it
+ * will be destroyed after the signal is emitted.
+ **/
+ signals[NEW_CONNECTION] =
+ g_signal_new ("new_connection",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupSocketClass, new_connection),
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ SOUP_TYPE_SOCKET);
+
+ /* properties */
+ /**
+ * SOUP_SOCKET_LOCAL_ADDRESS:
+ *
+ * Alias for the #SoupSocket:local-address property. (Address
+ * of local end of socket.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_LOCAL_ADDRESS,
+ g_param_spec_object (SOUP_SOCKET_LOCAL_ADDRESS,
+ "Local address",
+ "Address of local end of socket",
+ SOUP_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SOCKET_REMOTE_ADDRESS:
+ *
+ * Alias for the #SoupSocket:remote-address property. (Address
+ * of remote end of socket.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REMOTE_ADDRESS,
+ g_param_spec_object (SOUP_SOCKET_REMOTE_ADDRESS,
+ "Remote address",
+ "Address of remote end of socket",
+ SOUP_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SoupSocket:non-blocking:
+ *
+ * Whether or not the socket uses non-blocking I/O.
+ *
+ * #SoupSocket's I/O methods are designed around the idea of
+ * using a single codepath for both synchronous and
+ * asynchronous I/O. If you want to read off a #SoupSocket,
+ * the "correct" way to do it is to call soup_socket_read() or
+ * soup_socket_read_until() repeatedly until you have read
+ * everything you want. If it returns %SOUP_SOCKET_WOULD_BLOCK
+ * at any point, stop reading and wait for it to emit the
+ * #SoupSocket::readable signal. Then go back to the
+ * reading-as-much-as-you-can loop. Likewise, for writing to a
+ * #SoupSocket, you should call soup_socket_write() either
+ * until you have written everything, or it returns
+ * %SOUP_SOCKET_WOULD_BLOCK (in which case you wait for
+ * #SoupSocket::writable and then go back into the loop).
+ *
+ * Code written this way will work correctly with both
+ * blocking and non-blocking sockets; blocking sockets will
+ * simply never return %SOUP_SOCKET_WOULD_BLOCK, and so the
+ * code that handles that case just won't get used for them.
+ **/
+ /**
+ * SOUP_SOCKET_FLAG_NONBLOCKING:
+ *
+ * Alias for the #SoupSocket:non-blocking property. (Whether
+ * or not the socket uses non-blocking I/O.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_NON_BLOCKING,
+ g_param_spec_boolean (SOUP_SOCKET_FLAG_NONBLOCKING,
+ "Non-blocking",
+ "Whether or not the socket uses non-blocking I/O",
+ TRUE,
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_SOCKET_IS_SERVER:
+ *
+ * Alias for the #SoupSocket:is-server property. (Whether or
+ * not the socket is a server socket.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_IS_SERVER,
+ g_param_spec_boolean (SOUP_SOCKET_IS_SERVER,
+ "Server",
+ "Whether or not the socket is a server socket",
+ FALSE,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_SOCKET_SSL_CREDENTIALS:
+ *
+ * Alias for the #SoupSocket:ssl-credentials property.
+ * (SSL credential information.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SSL_CREDENTIALS,
+ g_param_spec_pointer (SOUP_SOCKET_SSL_CREDENTIALS,
+ "SSL credentials",
+ "SSL credential information, passed from the session to the SSL implementation",
+ G_PARAM_READWRITE));
+ /**
+ * SOUP_SOCKET_SSL_STRICT:
+ *
+ * Alias for the #SoupSocket:ssl-strict property.
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SSL_STRICT,
+ g_param_spec_boolean (SOUP_SOCKET_SSL_STRICT,
+ "Strictly validate SSL certificates",
+ "Whether certificate errors should be considered a connection error",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SOCKET_SSL_FALLBACK:
+ *
+ * Alias for the #SoupSocket:ssl-fallback property.
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SSL_FALLBACK,
+ g_param_spec_boolean (SOUP_SOCKET_SSL_FALLBACK,
+ "SSLv3 fallback",
+ "Use SSLv3 instead of TLS (client-side only)",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SOCKET_TRUSTED_CERTIFICATE:
+ *
+ * Alias for the #SoupSocket:trusted-certificate
+ * property.
+ **/
+ g_object_class_install_property (
+ object_class, PROP_TRUSTED_CERTIFICATE,
+ g_param_spec_boolean (SOUP_SOCKET_TRUSTED_CERTIFICATE,
+ "Trusted Certificate",
+ "Whether the server certificate is trusted, if this is an SSL socket",
+ FALSE,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_SOCKET_ASYNC_CONTEXT:
+ *
+ * Alias for the #SoupSocket:async-context property. (The
+ * socket's #GMainContext.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_ASYNC_CONTEXT,
+ g_param_spec_pointer (SOUP_SOCKET_ASYNC_CONTEXT,
+ "Async GMainContext",
+ "The GMainContext to dispatch this socket's async I/O in",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * SOUP_SOCKET_TIMEOUT:
+ *
+ * Alias for the #SoupSocket:timeout property. (The timeout
+ * in seconds for blocking socket I/O operations.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_TIMEOUT,
+ g_param_spec_uint (SOUP_SOCKET_TIMEOUT,
+ "Timeout value",
+ "Value in seconds to timeout a blocking I/O",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class, PROP_CLEAN_DISPOSE,
+ g_param_spec_boolean ("clean-dispose",
+ "Clean dispose",
+ "Warn on unclean dispose",
+ FALSE,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_SOCKET_TLS_CERTIFICATE:
+ *
+ * Alias for the #SoupSocket:tls-certificate
+ * property. Note that this property's value is only useful
+ * if the socket is for a TLS connection, and only reliable
+ * after some data has been transferred to or from it.
+ *
+ * Since: 2.34
+ **/
+ g_object_class_install_property (
+ object_class, PROP_TLS_CERTIFICATE,
+ g_param_spec_object (SOUP_SOCKET_TLS_CERTIFICATE,
+ "TLS certificate",
+ "The peer's TLS certificate",
+ G_TYPE_TLS_CERTIFICATE,
+ G_PARAM_READABLE));
+ /**
+ * SOUP_SOCKET_TLS_ERRORS:
+ *
+ * Alias for the #SoupSocket:tls-errors
+ * property. Note that this property's value is only useful
+ * if the socket is for a TLS connection, and only reliable
+ * after some data has been transferred to or from it.
+ *
+ * Since: 2.34
+ **/
+ g_object_class_install_property (
+ object_class, PROP_TLS_ERRORS,
+ g_param_spec_flags (SOUP_SOCKET_TLS_ERRORS,
+ "TLS errors",
+ "Errors with the peer's TLS certificate",
+ G_TYPE_TLS_CERTIFICATE_FLAGS, 0,
+ G_PARAM_READABLE));
+}
+
+
+static void
+finish_socket_setup (SoupSocketPrivate *priv)
+{
+ if (!priv->gsock)
+ return;
+
+ if (!priv->conn)
+ priv->conn = (GIOStream *)g_socket_connection_factory_create_connection (priv->gsock);
+ if (!priv->istream)
+ priv->istream = G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (priv->conn));
+ if (!priv->ostream)
+ priv->ostream = G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream (priv->conn));
+
+ g_socket_set_timeout (priv->gsock, priv->timeout);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_LOCAL_ADDRESS:
+ priv->local_addr = (SoupAddress *)g_value_dup_object (value);
+ break;
+ case PROP_REMOTE_ADDRESS:
+ priv->remote_addr = (SoupAddress *)g_value_dup_object (value);
+ break;
+ case PROP_NON_BLOCKING:
+ priv->non_blocking = g_value_get_boolean (value);
+ break;
+ case PROP_SSL_CREDENTIALS:
+ priv->ssl_creds = g_value_get_pointer (value);
+ break;
+ case PROP_SSL_STRICT:
+ priv->ssl_strict = g_value_get_boolean (value);
+ break;
+ case PROP_SSL_FALLBACK:
+ priv->ssl_fallback = g_value_get_boolean (value);
+ break;
+ case PROP_ASYNC_CONTEXT:
+ priv->async_context = g_value_get_pointer (value);
+ if (priv->async_context)
+ g_main_context_ref (priv->async_context);
+ break;
+ case PROP_TIMEOUT:
+ priv->timeout = g_value_get_uint (value);
+ if (priv->conn)
+ g_socket_set_timeout (priv->gsock, priv->timeout);
+ break;
+ case PROP_CLEAN_DISPOSE:
+ priv->clean_dispose = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_LOCAL_ADDRESS:
+ g_value_set_object (value, soup_socket_get_local_address (SOUP_SOCKET (object)));
+ break;
+ case PROP_REMOTE_ADDRESS:
+ g_value_set_object (value, soup_socket_get_remote_address (SOUP_SOCKET (object)));
+ break;
+ case PROP_NON_BLOCKING:
+ g_value_set_boolean (value, priv->non_blocking);
+ break;
+ case PROP_IS_SERVER:
+ g_value_set_boolean (value, priv->is_server);
+ break;
+ case PROP_SSL_CREDENTIALS:
+ g_value_set_pointer (value, priv->ssl_creds);
+ break;
+ case PROP_SSL_STRICT:
+ g_value_set_boolean (value, priv->ssl_strict);
+ break;
+ case PROP_SSL_FALLBACK:
+ g_value_set_boolean (value, priv->ssl_fallback);
+ break;
+ case PROP_TRUSTED_CERTIFICATE:
+ g_value_set_boolean (value, priv->tls_errors == 0);
+ break;
+ case PROP_ASYNC_CONTEXT:
+ g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
+ break;
+ case PROP_TIMEOUT:
+ g_value_set_uint (value, priv->timeout);
+ break;
+ case PROP_TLS_CERTIFICATE:
+ if (G_IS_TLS_CONNECTION (priv->conn))
+ g_value_set_object (value, g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (priv->conn)));
+ else
+ g_value_set_object (value, NULL);
+ break;
+ case PROP_TLS_ERRORS:
+ g_value_set_flags (value, priv->tls_errors);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+/**
+ * soup_socket_new:
+ * @optname1: name of first property to set (or %NULL)
+ * @...: value of @optname1, followed by additional property/value pairs
+ *
+ * Creates a new (disconnected) socket
+ *
+ * Return value: the new socket
+ **/
+SoupSocket *
+soup_socket_new (const char *optname1, ...)
+{
+ SoupSocket *sock;
+ va_list ap;
+
+ va_start (ap, optname1);
+ sock = (SoupSocket *)g_object_new_valist (SOUP_TYPE_SOCKET,
+ optname1, ap);
+ va_end (ap);
+
+ return sock;
+}
+
+static guint
+socket_connected (SoupSocket *sock, GSocketConnection *conn, GError *error)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ g_object_unref (priv->connect_cancel);
+ priv->connect_cancel = NULL;
+
+ if (error) {
+ if (error->domain == G_RESOLVER_ERROR) {
+ g_error_free (error);
+ return SOUP_STATUS_CANT_RESOLVE;
+ } else {
+ g_error_free (error);
+ return SOUP_STATUS_CANT_CONNECT;
+ }
+ }
+
+ priv->conn = (GIOStream *)conn;
+ priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
+ finish_socket_setup (priv);
+
+ return SOUP_STATUS_OK;
+}
+
+/**
+ * SoupSocketCallback:
+ * @sock: the #SoupSocket
+ * @status: an HTTP status code indicating success or failure
+ * @user_data: the data passed to soup_socket_connect_async()
+ *
+ * The callback function passed to soup_socket_connect_async().
+ **/
+
+typedef struct {
+ SoupSocket *sock;
+ SoupSocketCallback callback;
+ gpointer user_data;
+} SoupSocketAsyncConnectData;
+
+static void
+async_connected (GObject *client, GAsyncResult *result, gpointer data)
+{
+ SoupSocketAsyncConnectData *sacd = data;
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
+ GError *error = NULL;
+ GSocketConnection *conn;
+ guint status;
+
+ if (priv->async_context)
+ g_main_context_pop_thread_default (priv->async_context);
+
+ conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
+ result, &error);
+ status = socket_connected (sacd->sock, conn, error);
+
+ sacd->callback (sacd->sock, status, sacd->user_data);
+ g_object_unref (sacd->sock);
+ g_slice_free (SoupSocketAsyncConnectData, sacd);
+}
+
+/**
+ * soup_socket_connect_async:
+ * @sock: a client #SoupSocket (which must not already be connected)
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: (scope async): callback to call after connecting
+ * @user_data: data to pass to @callback
+ *
+ * Begins asynchronously connecting to @sock's remote address. The
+ * socket will call @callback when it succeeds or fails (but not
+ * before returning from this function).
+ *
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * connection. @callback will still be invoked in this case, with a
+ * status of %SOUP_STATUS_CANCELLED.
+ **/
+void
+soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
+ SoupSocketCallback callback, gpointer user_data)
+{
+ SoupSocketPrivate *priv;
+ SoupSocketAsyncConnectData *sacd;
+ GSocketClient *client;
+
+ g_return_if_fail (SOUP_IS_SOCKET (sock));
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_if_fail (priv->remote_addr != NULL);
+
+ sacd = g_slice_new0 (SoupSocketAsyncConnectData);
+ sacd->sock = g_object_ref (sock);
+ sacd->callback = callback;
+ sacd->user_data = user_data;
+
+ priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+
+ if (priv->async_context)
+ g_main_context_push_thread_default (priv->async_context);
+
+ client = g_socket_client_new ();
+ if (priv->timeout)
+ g_socket_client_set_timeout (client, priv->timeout);
+ g_socket_client_connect_async (client,
+ G_SOCKET_CONNECTABLE (priv->remote_addr),
+ priv->connect_cancel,
+ async_connected, sacd);
+ g_object_unref (client);
+}
+
+/**
+ * soup_socket_connect_sync:
+ * @sock: a client #SoupSocket (which must not already be connected)
+ * @cancellable: a #GCancellable, or %NULL
+ *
+ * Attempt to synchronously connect @sock to its remote address.
+ *
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * connection, in which case soup_socket_connect_sync() will return
+ * %SOUP_STATUS_CANCELLED.
+ *
+ * Return value: a success or failure code.
+ **/
+guint
+soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
+{
+ SoupSocketPrivate *priv;
+ GSocketClient *client;
+ GSocketConnection *conn;
+ GError *error = NULL;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_val_if_fail (!priv->is_server, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (priv->gsock == NULL, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
+
+ if (cancellable)
+ g_object_ref (cancellable);
+ else
+ cancellable = g_cancellable_new ();
+ priv->connect_cancel = cancellable;
+
+ client = g_socket_client_new ();
+ if (priv->timeout)
+ g_socket_client_set_timeout (client, priv->timeout);
+ conn = g_socket_client_connect (client,
+ G_SOCKET_CONNECTABLE (priv->remote_addr),
+ priv->connect_cancel, &error);
+ g_object_unref (client);
+
+ return socket_connected (sock, conn, error);
+}
+
+int
+soup_socket_get_fd (SoupSocket *sock)
+{
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), -1);
+
+ return g_socket_get_fd (SOUP_SOCKET_GET_PRIVATE (sock)->gsock);
+}
+
+static GSource *
+soup_socket_create_watch (SoupSocketPrivate *priv, GIOCondition cond,
+ GPollableSourceFunc callback, gpointer user_data,
+ GCancellable *cancellable)
+{
+ GSource *watch;
+
+ if (cond == G_IO_IN)
+ watch = g_pollable_input_stream_create_source (priv->istream, cancellable);
+ else
+ watch = g_pollable_output_stream_create_source (priv->ostream, cancellable);
+ g_source_set_callback (watch, (GSourceFunc)callback, user_data, NULL);
+ g_source_attach (watch, priv->async_context);
+ g_source_unref (watch);
+
+ return watch;
+}
+
+static gboolean
+listen_watch (GObject *pollable, gpointer data)
+{
+ SoupSocket *sock = data, *new;
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock), *new_priv;
+ GSocket *new_gsock;
+
+ new_gsock = g_socket_accept (priv->gsock, NULL, NULL);
+ if (!new_gsock)
+ return FALSE;
+
+ new = g_object_new (SOUP_TYPE_SOCKET, NULL);
+ new_priv = SOUP_SOCKET_GET_PRIVATE (new);
+ new_priv->gsock = new_gsock;
+ if (priv->async_context)
+ new_priv->async_context = g_main_context_ref (priv->async_context);
+ new_priv->non_blocking = priv->non_blocking;
+ new_priv->is_server = TRUE;
+ if (priv->ssl_creds)
+ new_priv->ssl_creds = priv->ssl_creds;
+ finish_socket_setup (new_priv);
+
+ if (new_priv->ssl_creds) {
+ if (!soup_socket_start_proxy_ssl (new, NULL, NULL)) {
+ g_object_unref (new);
+ return TRUE;
+ }
+ }
+
+ g_signal_emit (sock, signals[NEW_CONNECTION], 0, new);
+ g_object_unref (new);
+
+ return TRUE;
+}
+
+/**
+ * soup_socket_listen:
+ * @sock: a server #SoupSocket (which must not already be connected or
+ * listening)
+ *
+ * Makes @sock start listening on its local address. When connections
+ * come in, @sock will emit %new_connection.
+ *
+ * Return value: whether or not @sock is now listening.
+ **/
+gboolean
+soup_socket_listen (SoupSocket *sock)
+
+{
+ SoupSocketPrivate *priv;
+ GSocketAddress *addr;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE);
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_val_if_fail (priv->gsock == NULL, FALSE);
+ g_return_val_if_fail (priv->local_addr != NULL, FALSE);
+
+ priv->is_server = TRUE;
+
+ /* @local_addr may have its port set to 0. So we intentionally
+ * don't store it in priv->local_addr, so that if the
+ * caller calls soup_socket_get_local_address() later, we'll
+ * have to make a new addr by calling getsockname(), which
+ * will have the right port number.
+ */
+ addr = soup_address_get_gsockaddr (priv->local_addr);
+ g_return_val_if_fail (addr != NULL, FALSE);
+
+ priv->gsock = g_socket_new (g_socket_address_get_family (addr),
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ NULL);
+ if (!priv->gsock)
+ goto cant_listen;
+ finish_socket_setup (priv);
+
+ /* Bind */
+ if (!g_socket_bind (priv->gsock, addr, TRUE, NULL))
+ goto cant_listen;
+ /* Force local_addr to be re-resolved now */
+ g_object_unref (priv->local_addr);
+ priv->local_addr = NULL;
+
+ /* Listen */
+ if (!g_socket_listen (priv->gsock, NULL))
+ goto cant_listen;
+
+ priv->watch_src = soup_socket_create_watch (priv, G_IO_IN,
+ listen_watch, sock,
+ NULL);
+ g_object_unref (addr);
+ return TRUE;
+
+ cant_listen:
+ if (priv->conn)
+ disconnect_internal (sock);
+ g_object_unref (addr);
+
+ return FALSE;
+}
+
+static void
+soup_socket_peer_certificate_changed (GObject *conn, GParamSpec *pspec,
+ gpointer sock)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ priv->tls_errors = g_tls_connection_get_peer_certificate_errors (G_TLS_CONNECTION (priv->conn));
+ if (priv->ssl_ca_in_creds)
+ priv->tls_errors &= ~G_TLS_CERTIFICATE_UNKNOWN_CA;
+
+ g_object_notify (sock, "tls-certificate");
+ g_object_notify (sock, "tls-errors");
+}
+
+static gboolean
+soup_socket_accept_certificate (GTlsConnection *conn, GTlsCertificate *cert,
+ GTlsCertificateFlags errors, gpointer sock)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ if (soup_ssl_credentials_verify_certificate (priv->ssl_creds,
+ cert, errors)) {
+ priv->ssl_ca_in_creds = TRUE;
+ return TRUE;
+ }
+
+ return !priv->ssl_strict;
+}
+
+/**
+ * soup_socket_start_ssl:
+ * @sock: the socket
+ * @cancellable: a #GCancellable
+ *
+ * Starts using SSL on @socket.
+ *
+ * Return value: success or failure
+ **/
+gboolean
+soup_socket_start_ssl (SoupSocket *sock, GCancellable *cancellable)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ return soup_socket_start_proxy_ssl (sock, soup_address_get_name (priv->remote_addr), cancellable);
+}
+
+/**
+ * soup_socket_start_proxy_ssl:
+ * @sock: the socket
+ * @ssl_host: hostname of the SSL server
+ * @cancellable: a #GCancellable
+ *
+ * Starts using SSL on @socket, expecting to find a host named
+ * @ssl_host.
+ *
+ * Return value: success or failure
+ **/
+gboolean
+soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
+ GCancellable *cancellable)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ GTlsBackend *backend = g_tls_backend_get_default ();
+
+ if (G_IS_TLS_CONNECTION (priv->conn))
+ return TRUE;
+ if (!priv->ssl_creds)
+ return FALSE;
+
+ if (!priv->is_server) {
+ GTlsClientConnection *conn;
+ GSocketConnectable *identity;
+
+ identity = g_network_address_new (ssl_host, 0);
+ conn = g_initable_new (g_tls_backend_get_client_connection_type (backend),
+ NULL, NULL,
+ "base-io-stream", priv->conn,
+ "server-identity", identity,
+ "use-system-certdb", FALSE,
+ "require-close-notify", FALSE,
+ "use-ssl3", TRUE,
+ NULL);
+ g_object_unref (identity);
+
+ if (!conn)
+ return FALSE;
+
+ g_object_unref (priv->conn);
+ priv->conn = G_IO_STREAM (conn);
+
+ g_signal_connect (conn, "accept-certificate",
+ G_CALLBACK (soup_socket_accept_certificate),
+ sock);
+ } else {
+ GTlsServerConnection *conn;
+
+ conn = g_initable_new (g_tls_backend_get_server_connection_type (backend),
+ NULL, NULL,
+ "base-io-stream", priv->conn,
+ "certificate", soup_ssl_credentials_get_certificate (priv->ssl_creds),
+ "use-system-certdb", FALSE,
+ "require-close-notify", FALSE,
+ NULL);
+ if (!conn)
+ return FALSE;
+
+ g_object_unref (priv->conn);
+ priv->conn = G_IO_STREAM (conn);
+ }
+
+ priv->ssl_ca_in_creds = FALSE;
+ g_signal_connect (priv->conn, "notify::peer-certificate",
+ G_CALLBACK (soup_socket_peer_certificate_changed), sock);
+
+ priv->istream = G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (priv->conn));
+ priv->ostream = G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream (priv->conn));
+ return TRUE;
+}
+
+guint
+soup_socket_handshake_sync (SoupSocket *sock,
+ GCancellable *cancellable)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ GError *error = NULL;
+
+ if (g_tls_connection_handshake (G_TLS_CONNECTION (priv->conn),
+ cancellable, &error))
+ return SOUP_STATUS_OK;
+ else if (!priv->ssl_fallback &&
+ g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
+ g_error_free (error);
+ return SOUP_STATUS_TLS_FAILED;
+ } else {
+ g_error_free (error);
+ return SOUP_STATUS_SSL_FAILED;
+ }
+}
+
+static void
+handshake_async_ready (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ SoupSocketAsyncConnectData *data = user_data;
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (data->sock);
+ GError *error = NULL;
+ guint status;
+
+ if (priv->async_context)
+ g_main_context_pop_thread_default (priv->async_context);
+
+ if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (priv->conn),
+ result, &error))
+ status = SOUP_STATUS_OK;
+ else if (!priv->ssl_fallback &&
+ g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS))
+ status = SOUP_STATUS_TLS_FAILED;
+ else
+ status = SOUP_STATUS_SSL_FAILED;
+ g_clear_error (&error);
+
+ data->callback (data->sock, status, data->user_data);
+ g_object_unref (data->sock);
+ g_slice_free (SoupSocketAsyncConnectData, data);
+}
+
+void
+soup_socket_handshake_async (SoupSocket *sock,
+ GCancellable *cancellable,
+ SoupSocketCallback callback,
+ gpointer user_data)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ SoupSocketAsyncConnectData *data;
+
+ data = g_slice_new (SoupSocketAsyncConnectData);
+ data->sock = g_object_ref (sock);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ if (priv->async_context)
+ g_main_context_push_thread_default (priv->async_context);
+ g_tls_connection_handshake_async (G_TLS_CONNECTION (priv->conn),
+ G_PRIORITY_DEFAULT,
+ cancellable, handshake_async_ready,
+ data);
+}
+
+/**
+ * soup_socket_is_ssl:
+ * @sock: a #SoupSocket
+ *
+ * Tests if @sock is set up to do SSL. Note that this simply means
+ * that the %SOUP_SOCKET_SSL_CREDENTIALS property has been set; it
+ * does not mean that soup_socket_start_ssl() has been called.
+ *
+ * Return value: %TRUE if @sock has SSL credentials set
+ **/
+gboolean
+soup_socket_is_ssl (SoupSocket *sock)
+{
+ g_return_if_fail (SOUP_IS_SOCKET (sock));
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ return priv->ssl_creds != NULL;
+}
+
+/**
+ * soup_socket_disconnect:
+ * @sock: a #SoupSocket
+ *
+ * Disconnects @sock. Any further read or write attempts on it will
+ * fail.
+ **/
+void
+soup_socket_disconnect (SoupSocket *sock)
+{
+ SoupSocketPrivate *priv;
+ gboolean already_disconnected = FALSE;
+
+ g_return_if_fail (SOUP_IS_SOCKET (sock));
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ if (priv->connect_cancel) {
+ g_cancellable_cancel (priv->connect_cancel);
+ return;
+ } else if (g_mutex_trylock (priv->iolock)) {
+ if (priv->conn)
+ disconnect_internal (sock);
+ else
+ already_disconnected = TRUE;
+ g_mutex_unlock (priv->iolock);
+ } else {
+ /* Another thread is currently doing IO, so
+ * we can't close the socket. So just shutdown
+ * the file descriptor to force the I/O to fail.
+ * (It will actually be closed when the socket
+ * is destroyed.)
+ */
+ g_socket_shutdown (priv->gsock, TRUE, TRUE, NULL);
+ }
+
+ if (already_disconnected)
+ return;
+
+ /* Keep ref around signals in case the object is unreferenced
+ * in a handler
+ */
+ g_object_ref (sock);
+
+ /* Give all readers a chance to notice the connection close */
+ g_signal_emit (sock, signals[READABLE], 0);
+
+ /* FIXME: can't disconnect until all data is read */
+
+ /* Then let everyone know we're disconnected */
+ g_signal_emit (sock, signals[DISCONNECTED], 0);
+
+ g_object_unref (sock);
+}
+
+/**
+ * soup_socket_is_connected:
+ * @sock: a #SoupSocket
+ *
+ * Tests if @sock is connected to another host
+ *
+ * Return value: %TRUE or %FALSE.
+ **/
+gboolean
+soup_socket_is_connected (SoupSocket *sock)
+{
+ SoupSocketPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE);
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ return priv->conn != NULL;
+}
+
+/**
+ * soup_socket_get_local_address:
+ * @sock: a #SoupSocket
+ *
+ * Returns the #SoupAddress corresponding to the local end of @sock.
+ *
+ * Return value: (transfer none): the #SoupAddress
+ **/
+SoupAddress *
+soup_socket_get_local_address (SoupSocket *sock)
+{
+ SoupSocketPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL);
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ g_mutex_lock (priv->addrlock);
+ if (!priv->local_addr) {
+ GSocketAddress *addr;
+ struct sockaddr_storage sa;
+ gssize sa_len;
+
+ addr = g_socket_get_local_address (priv->gsock, NULL);
+ sa_len = g_socket_address_get_native_size (addr);
+ g_socket_address_to_native (addr, &sa, sa_len, NULL);
+ priv->local_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
+ g_object_unref (addr);
+ }
+ g_mutex_unlock (priv->addrlock);
+
+ return priv->local_addr;
+}
+
+/**
+ * soup_socket_get_remote_address:
+ * @sock: a #SoupSocket
+ *
+ * Returns the #SoupAddress corresponding to the remote end of @sock.
+ *
+ * Return value: (transfer none): the #SoupAddress
+ **/
+SoupAddress *
+soup_socket_get_remote_address (SoupSocket *sock)
+{
+ SoupSocketPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL);
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ g_mutex_lock (priv->addrlock);
+ if (!priv->remote_addr) {
+ GSocketAddress *addr;
+ struct sockaddr_storage sa;
+ gssize sa_len;
+
+ addr = g_socket_get_remote_address (priv->gsock, NULL);
+ sa_len = g_socket_address_get_native_size (addr);
+ g_socket_address_to_native (addr, &sa, sa_len, NULL);
+ priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
+ g_object_unref (addr);
+ }
+ g_mutex_unlock (priv->addrlock);
+
+ return priv->remote_addr;
+}
+
+
+static gboolean
+socket_read_watch (GObject *pollable, gpointer user_data)
+{
+ SoupSocket *sock = user_data;
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ priv->read_src = NULL;
+ g_signal_emit (sock, signals[READABLE], 0);
+ return FALSE;
+}
+
+static SoupSocketIOStatus
+read_from_network (SoupSocket *sock, gpointer buffer, gsize len,
+ gsize *nread, GCancellable *cancellable, GError **error)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ GError *my_err = NULL;
+ gssize my_nread;
+
+ *nread = 0;
+
+ if (!priv->conn)
+ return SOUP_SOCKET_EOF;
+
+ if (!priv->non_blocking) {
+ my_nread = g_input_stream_read (G_INPUT_STREAM (priv->istream),
+ buffer, len,
+ cancellable, &my_err);
+ } else {
+ my_nread = g_pollable_input_stream_read_nonblocking (
+ priv->istream, buffer, len,
+ cancellable, &my_err);
+ }
+
+ if (my_nread > 0) {
+ g_clear_error (&my_err);
+ *nread = my_nread;
+ return SOUP_SOCKET_OK;
+ } else if (my_nread == 0) {
+ g_clear_error (&my_err);
+ *nread = my_nread;
+ return SOUP_SOCKET_EOF;
+ } else if (g_error_matches (my_err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ g_clear_error (&my_err);
+ if (!priv->read_src) {
+ priv->read_src =
+ soup_socket_create_watch (priv, G_IO_IN,
+ socket_read_watch, sock,
+ cancellable);
+ }
+ return SOUP_SOCKET_WOULD_BLOCK;
+ } else if (g_error_matches (my_err, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE)) {
+ my_err->domain = SOUP_SSL_ERROR;
+ my_err->code = SOUP_SSL_ERROR_CERTIFICATE;
+ }
+
+ g_propagate_error (error, my_err);
+ return SOUP_SOCKET_ERROR;
+}
+
+static SoupSocketIOStatus
+read_from_buf (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ GByteArray *read_buf = priv->read_buf;
+
+ *nread = MIN (read_buf->len, len);
+ memcpy (buffer, read_buf->data, *nread);
+
+ if (*nread == read_buf->len) {
+ g_byte_array_free (read_buf, TRUE);
+ priv->read_buf = NULL;
+ } else {
+ memmove (read_buf->data, read_buf->data + *nread,
+ read_buf->len - *nread);
+ g_byte_array_set_size (read_buf, read_buf->len - *nread);
+ }
+
+ return SOUP_SOCKET_OK;
+}
+
+/**
+ * SoupSocketIOStatus:
+ * @SOUP_SOCKET_OK: Success
+ * @SOUP_SOCKET_WOULD_BLOCK: Cannot read/write any more at this time
+ * @SOUP_SOCKET_EOF: End of file
+ * @SOUP_SOCKET_ERROR: Other error
+ *
+ * Return value from the #SoupSocket IO methods.
+ **/
+
+/**
+ * soup_socket_read:
+ * @sock: the socket
+ * @buffer: buffer to read into
+ * @len: size of @buffer in bytes
+ * @nread: (out): on return, the number of bytes read into @buffer
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: error pointer
+ *
+ * Attempts to read up to @len bytes from @sock into @buffer. If some
+ * data is successfully read, soup_socket_read() will return
+ * %SOUP_SOCKET_OK, and *@nread will contain the number of bytes
+ * actually read (which may be less than @len).
+ *
+ * If @sock is non-blocking, and no data is available, the return
+ * value will be %SOUP_SOCKET_WOULD_BLOCK. In this case, the caller
+ * can connect to the #SoupSocket::readable signal to know when there
+ * is more data to read. (NB: You MUST read all available data off the
+ * socket first. #SoupSocket::readable is only emitted after
+ * soup_socket_read() returns %SOUP_SOCKET_WOULD_BLOCK, and it is only
+ * emitted once. See the documentation for #SoupSocket:non-blocking.)
+ *
+ * Return value: a #SoupSocketIOStatus, as described above (or
+ * %SOUP_SOCKET_EOF if the socket is no longer connected, or
+ * %SOUP_SOCKET_ERROR on any other error, in which case @error will
+ * also be set).
+ **/
+SoupSocketIOStatus
+soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len,
+ gsize *nread, GCancellable *cancellable, GError **error)
+{
+ SoupSocketPrivate *priv;
+ SoupSocketIOStatus status;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_SOCKET_ERROR);
+ g_return_val_if_fail (nread != NULL, SOUP_SOCKET_ERROR);
+
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ g_mutex_lock (priv->iolock);
+ if (priv->read_buf)
+ status = read_from_buf (sock, buffer, len, nread);
+ else
+ status = read_from_network (sock, buffer, len, nread, cancellable, error);
+ g_mutex_unlock (priv->iolock);
+
+ return status;
+}
+
+/**
+ * soup_socket_read_until:
+ * @sock: the socket
+ * @buffer: buffer to read into
+ * @len: size of @buffer in bytes
+ * @boundary: boundary to read until
+ * @boundary_len: length of @boundary in bytes
+ * @nread: (out): on return, the number of bytes read into @buffer
+ * @got_boundary: on return, whether or not the data in @buffer
+ * ends with the boundary string
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: error pointer
+ *
+ * Like soup_socket_read(), but reads no further than the first
+ * occurrence of @boundary. (If the boundary is found, it will be
+ * included in the returned data, and *@got_boundary will be set to
+ * %TRUE.) Any data after the boundary will returned in future reads.
+ *
+ * soup_socket_read_until() will almost always return fewer than @len
+ * bytes: if the boundary is found, then it will only return the bytes
+ * up until the end of the boundary, and if the boundary is not found,
+ * then it will leave the last <literal>(boundary_len - 1)</literal>
+ * bytes in its internal buffer, in case they form the start of the
+ * boundary string. Thus, @len normally needs to be at least 1 byte
+ * longer than @boundary_len if you want to make any progress at all.
+ *
+ * Return value: as for soup_socket_read()
+ **/
+SoupSocketIOStatus
+soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len,
+ gconstpointer boundary, gsize boundary_len,
+ gsize *nread, gboolean *got_boundary,
+ GCancellable *cancellable, GError **error)
+{
+ SoupSocketPrivate *priv;
+ SoupSocketIOStatus status;
+ GByteArray *read_buf;
+ guint match_len, prev_len;
+ guint8 *p, *end;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_SOCKET_ERROR);
+ g_return_val_if_fail (nread != NULL, SOUP_SOCKET_ERROR);
+ g_return_val_if_fail (len >= boundary_len, SOUP_SOCKET_ERROR);
+
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ g_mutex_lock (priv->iolock);
+
+ *got_boundary = FALSE;
+
+ if (!priv->read_buf)
+ priv->read_buf = g_byte_array_new ();
+ read_buf = priv->read_buf;
+
+ if (read_buf->len < boundary_len) {
+ prev_len = read_buf->len;
+ g_byte_array_set_size (read_buf, len);
+ status = read_from_network (sock,
+ read_buf->data + prev_len,
+ len - prev_len, nread, cancellable, error);
+ read_buf->len = prev_len + *nread;
+
+ if (status != SOUP_SOCKET_OK) {
+ g_mutex_unlock (priv->iolock);
+ return status;
+ }
+ }
+
+ /* Scan for the boundary */
+ end = read_buf->data + read_buf->len;
+ for (p = read_buf->data; p <= end - boundary_len; p++) {
+ if (!memcmp (p, boundary, boundary_len)) {
+ p += boundary_len;
+ *got_boundary = TRUE;
+ break;
+ }
+ }
+
+ /* Return everything up to 'p' (which is either just after the
+ * boundary, or @boundary_len - 1 bytes before the end of the
+ * buffer).
+ */
+ match_len = p - read_buf->data;
+ status = read_from_buf (sock, buffer, MIN (len, match_len), nread);
+
+ g_mutex_unlock (priv->iolock);
+ return status;
+}
+
+static gboolean
+socket_write_watch (GObject *pollable, gpointer user_data)
+{
+ SoupSocket *sock = user_data;
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ priv->write_src = NULL;
+ g_signal_emit (sock, signals[WRITABLE], 0);
+ return FALSE;
+}
+
+/**
+ * soup_socket_write:
+ * @sock: the socket
+ * @buffer: data to write
+ * @len: size of @buffer, in bytes
+ * @nwrote: (out): on return, number of bytes written
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: error pointer
+ *
+ * Attempts to write @len bytes from @buffer to @sock. If some data is
+ * successfully written, the return status will be %SOUP_SOCKET_OK,
+ * and *@nwrote will contain the number of bytes actually written
+ * (which may be less than @len).
+ *
+ * If @sock is non-blocking, and no data could be written right away,
+ * the return value will be %SOUP_SOCKET_WOULD_BLOCK. In this case,
+ * the caller can connect to the #SoupSocket::writable signal to know
+ * when more data can be written. (NB: #SoupSocket::writable is only
+ * emitted after soup_socket_write() returns %SOUP_SOCKET_WOULD_BLOCK,
+ * and it is only emitted once. See the documentation for
+ * #SoupSocket:non-blocking.)
+ *
+ * Return value: a #SoupSocketIOStatus, as described above (or
+ * %SOUP_SOCKET_EOF or %SOUP_SOCKET_ERROR. @error will be set if the
+ * return value is %SOUP_SOCKET_ERROR.)
+ **/
+SoupSocketIOStatus
+soup_socket_write (SoupSocket *sock, gconstpointer buffer,
+ gsize len, gsize *nwrote,
+ GCancellable *cancellable, GError **error)
+{
+ SoupSocketPrivate *priv;
+ GError *my_err = NULL;
+ gssize my_nwrote;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_SOCKET_ERROR);
+ g_return_val_if_fail (nwrote != NULL, SOUP_SOCKET_ERROR);
+
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ g_mutex_lock (priv->iolock);
+
+ if (!priv->conn) {
+ g_mutex_unlock (priv->iolock);
+ return SOUP_SOCKET_EOF;
+ }
+ if (priv->write_src) {
+ g_mutex_unlock (priv->iolock);
+ return SOUP_SOCKET_WOULD_BLOCK;
+ }
+
+ if (!priv->non_blocking) {
+ my_nwrote = g_output_stream_write (G_OUTPUT_STREAM (priv->ostream),
+ buffer, len,
+ cancellable, &my_err);
+ } else {
+ my_nwrote = g_pollable_output_stream_write_nonblocking (
+ priv->ostream, buffer, len,
+ cancellable, &my_err);
+ }
+
+ if (my_nwrote > 0) {
+ g_mutex_unlock (priv->iolock);
+ g_clear_error (&my_err);
+ *nwrote = my_nwrote;
+ return SOUP_SOCKET_OK;
+ }
+
+ if (g_error_matches (my_err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ g_mutex_unlock (priv->iolock);
+ g_clear_error (&my_err);
+
+ priv->write_src =
+ soup_socket_create_watch (priv,
+ G_IO_OUT,
+ socket_write_watch, sock, cancellable);
+ return SOUP_SOCKET_WOULD_BLOCK;
+ } else if (g_error_matches (my_err, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE)) {
+ my_err->domain = SOUP_SSL_ERROR;
+ my_err->code = SOUP_SSL_ERROR_CERTIFICATE;
+ }
+
+ g_mutex_unlock (priv->iolock);
+ g_propagate_error (error, my_err);
+ return SOUP_SOCKET_ERROR;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SOCKET_H
+#define SOUP_SOCKET_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_SOCKET (soup_socket_get_type ())
+#define SOUP_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SOCKET, SoupSocket))
+#define SOUP_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SOCKET, SoupSocketClass))
+#define SOUP_IS_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SOCKET))
+#define SOUP_IS_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SOCKET))
+#define SOUP_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SOCKET, SoupSocketClass))
+
+struct _SoupSocket {
+ GObject parent;
+
+};
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*readable) (SoupSocket *);
+ void (*writable) (SoupSocket *);
+ void (*disconnected) (SoupSocket *);
+
+ void (*new_connection) (SoupSocket *, SoupSocket *);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupSocketClass;
+
+#define SOUP_SOCKET_LOCAL_ADDRESS "local-address"
+#define SOUP_SOCKET_REMOTE_ADDRESS "remote-address"
+#define SOUP_SOCKET_FLAG_NONBLOCKING "non-blocking"
+#define SOUP_SOCKET_IS_SERVER "is-server"
+#define SOUP_SOCKET_SSL_CREDENTIALS "ssl-creds"
+#define SOUP_SOCKET_SSL_STRICT "ssl-strict"
+#define SOUP_SOCKET_SSL_FALLBACK "ssl-fallback"
+#define SOUP_SOCKET_TRUSTED_CERTIFICATE "trusted-certificate"
+#define SOUP_SOCKET_ASYNC_CONTEXT "async-context"
+#define SOUP_SOCKET_TIMEOUT "timeout"
+#define SOUP_SOCKET_TLS_CERTIFICATE "tls-certificate"
+#define SOUP_SOCKET_TLS_ERRORS "tls-errors"
+
+typedef void (*SoupSocketCallback) (SoupSocket *sock,
+ guint status,
+ gpointer user_data);
+
+GType soup_socket_get_type (void);
+
+SoupSocket *soup_socket_new (const char *optname1,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void soup_socket_connect_async (SoupSocket *sock,
+ GCancellable *cancellable,
+ SoupSocketCallback callback,
+ gpointer user_data);
+guint soup_socket_connect_sync (SoupSocket *sock,
+ GCancellable *cancellable);
+int soup_socket_get_fd (SoupSocket *sock);
+
+gboolean soup_socket_listen (SoupSocket *sock);
+
+gboolean soup_socket_start_ssl (SoupSocket *sock,
+ GCancellable *cancellable);
+gboolean soup_socket_start_proxy_ssl (SoupSocket *sock,
+ const char *ssl_host,
+ GCancellable *cancellable);
+gboolean soup_socket_is_ssl (SoupSocket *sock);
+
+void soup_socket_disconnect (SoupSocket *sock);
+gboolean soup_socket_is_connected (SoupSocket *sock);
+
+SoupAddress *soup_socket_get_local_address (SoupSocket *sock);
+SoupAddress *soup_socket_get_remote_address (SoupSocket *sock);
+
+
+typedef enum {
+ SOUP_SOCKET_OK,
+ SOUP_SOCKET_WOULD_BLOCK,
+ SOUP_SOCKET_EOF,
+ SOUP_SOCKET_ERROR
+} SoupSocketIOStatus;
+
+SoupSocketIOStatus soup_socket_read (SoupSocket *sock,
+ gpointer buffer,
+ gsize len,
+ gsize *nread,
+ GCancellable *cancellable,
+ GError **error);
+SoupSocketIOStatus soup_socket_read_until (SoupSocket *sock,
+ gpointer buffer,
+ gsize len,
+ gconstpointer boundary,
+ gsize boundary_len,
+ gsize *nread,
+ gboolean *got_boundary,
+ GCancellable *cancellable,
+ GError **error);
+
+SoupSocketIOStatus soup_socket_write (SoupSocket *sock,
+ gconstpointer buffer,
+ gsize len,
+ gsize *nwrote,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* SOUP_SOCKET_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-ssl.c: temporary ssl integration
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+
+#include "soup-ssl.h"
+#include "soup-misc.h"
+
+const gboolean soup_ssl_supported = TRUE;
+
+struct SoupSSLCredentials {
+ GList *ca_list;
+ GTlsCertificateFlags validation_flags;
+ GTlsCertificate *certificate;
+};
+
+SoupSSLCredentials *
+soup_ssl_get_client_credentials (const char *ca_file)
+{
+ SoupSSLCredentials *creds;
+
+ creds = g_slice_new0 (SoupSSLCredentials);
+
+ if (ca_file) {
+ GError *error = NULL;
+
+ creds->ca_list = g_tls_certificate_list_new_from_file (ca_file, &error);
+ if (error) {
+ if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
+ g_warning ("Could not set SSL credentials from '%s': %s",
+ ca_file, error->message);
+ }
+ g_error_free (error);
+ }
+ creds->validation_flags = G_TLS_CERTIFICATE_VALIDATE_ALL;
+ }
+
+ return creds;
+}
+
+gboolean
+soup_ssl_credentials_verify_certificate (SoupSSLCredentials *creds,
+ GTlsCertificate *cert,
+ GTlsCertificateFlags errors)
+{
+ errors = errors & creds->validation_flags;
+
+ if (errors & G_TLS_CERTIFICATE_UNKNOWN_CA) {
+ GList *ca;
+
+ for (ca = creds->ca_list; ca; ca = ca->next) {
+ if ((g_tls_certificate_verify (cert, NULL, ca->data) & G_TLS_CERTIFICATE_UNKNOWN_CA) == 0) {
+ errors &= ~G_TLS_CERTIFICATE_UNKNOWN_CA;
+ break;
+ }
+ }
+ }
+
+ return errors == 0;
+}
+
+void
+soup_ssl_free_client_credentials (SoupSSLCredentials *client_creds)
+{
+ GList *c;
+
+ for (c = client_creds->ca_list; c; c = c->next)
+ g_object_unref (c->data);
+ g_list_free (client_creds->ca_list);
+ g_slice_free (SoupSSLCredentials, client_creds);
+}
+
+SoupSSLCredentials *
+soup_ssl_get_server_credentials (const char *cert_file, const char *key_file)
+{
+ SoupSSLCredentials *creds;
+ GError *error = NULL;
+
+ creds = g_slice_new0 (SoupSSLCredentials);
+
+ creds->certificate = g_tls_certificate_new_from_files (cert_file, key_file, &error);
+ if (!creds->certificate) {
+ g_warning ("Could not read SSL certificate from '%s': %s",
+ cert_file, error->message);
+ g_error_free (error);
+ g_slice_free (SoupSSLCredentials, creds);
+ return NULL;
+ }
+
+ return creds;
+}
+
+GTlsCertificate *
+soup_ssl_credentials_get_certificate (SoupSSLCredentials *creds)
+{
+ return creds->certificate;
+}
+
+void
+soup_ssl_free_server_credentials (SoupSSLCredentials *server_creds)
+{
+ g_object_unref (server_creds->certificate);
+ g_slice_free (SoupSSLCredentials, server_creds);
+}
+
+/**
+ * SOUP_SSL_ERROR:
+ *
+ * A #GError domain representing an SSL error. Used with #SoupSSLError.
+ **/
+/**
+ * soup_ssl_error_quark:
+ *
+ * The quark used as %SOUP_SSL_ERROR
+ *
+ * Return value: The quark used as %SOUP_SSL_ERROR
+ **/
+GQuark
+soup_ssl_error_quark (void)
+{
+ static GQuark error;
+ if (!error)
+ error = g_quark_from_static_string ("soup_ssl_error_quark");
+ return error;
+}
+
+/**
+ * SoupSSLError:
+ * @SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ: Internal error. Never exposed
+ * outside of libsoup.
+ * @SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE: Internal error. Never exposed
+ * outside of libsoup.
+ * @SOUP_SSL_ERROR_CERTIFICATE: Indicates an error validating an SSL
+ * certificate
+ *
+ * SSL-related I/O errors.
+ **/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SSL_H
+#define SOUP_SSL_H 1
+
+#include "soup-types.h"
+
+typedef enum {
+ SOUP_SSL_TYPE_CLIENT = 0,
+ SOUP_SSL_TYPE_SERVER
+} SoupSSLType;
+
+typedef struct SoupSSLCredentials SoupSSLCredentials;
+
+SoupSSLCredentials *soup_ssl_get_client_credentials (const char *ca_file);
+void soup_ssl_free_client_credentials (SoupSSLCredentials *creds);
+gboolean soup_ssl_credentials_verify_certificate (SoupSSLCredentials *creds,
+ GTlsCertificate *cert,
+ GTlsCertificateFlags errors);
+
+SoupSSLCredentials *soup_ssl_get_server_credentials (const char *cert_file,
+ const char *key_file);
+void soup_ssl_free_server_credentials (SoupSSLCredentials *creds);
+GTlsCertificate *soup_ssl_credentials_get_certificate (SoupSSLCredentials *creds);
+
+#endif /* SOUP_SSL_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-status.c: Status code descriptions
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "soup-status.h"
+
+/**
+ * SECTION:soup-status
+ * @short_description: HTTP (and libsoup) status codes
+ *
+ **/
+
+/**
+ * SOUP_STATUS_IS_TRANSPORT_ERROR:
+ * @status: a status code
+ *
+ * Tests if @status is a libsoup transport error.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_INFORMATIONAL:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is an Informational (1xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_SUCCESSFUL:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is a Successful (2xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_REDIRECTION:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is a Redirection (3xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_CLIENT_ERROR:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is a Client Error (4xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+/**
+ * SOUP_STATUS_IS_SERVER_ERROR:
+ * @status: an HTTP status code
+ *
+ * Tests if @status is a Server Error (5xx) response.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+
+/**
+ * SoupKnownStatusCode:
+ * @SOUP_STATUS_NONE: No status available. (Eg, the message has not
+ * been sent yet)
+ * @SOUP_STATUS_CANCELLED: Message was cancelled locally
+ * @SOUP_STATUS_CANT_RESOLVE: Unable to resolve destination host name
+ * @SOUP_STATUS_CANT_RESOLVE_PROXY: Unable to resolve proxy host name
+ * @SOUP_STATUS_CANT_CONNECT: Unable to connect to remote host
+ * @SOUP_STATUS_CANT_CONNECT_PROXY: Unable to connect to proxy
+ * @SOUP_STATUS_SSL_FAILED: SSL/TLS negotiation failed
+ * @SOUP_STATUS_IO_ERROR: A network error occurred, or the other end
+ * closed the connection unexpectedly
+ * @SOUP_STATUS_MALFORMED: Malformed data (usually a programmer error)
+ * @SOUP_STATUS_TRY_AGAIN: Used internally
+ * @SOUP_STATUS_TOO_MANY_REDIRECTS: There were too many redirections
+ * @SOUP_STATUS_TLS_FAILED: Used internally
+ * @SOUP_STATUS_CONTINUE: 100 Continue (HTTP)
+ * @SOUP_STATUS_SWITCHING_PROTOCOLS: 101 Switching Protocols (HTTP)
+ * @SOUP_STATUS_PROCESSING: 102 Processing (WebDAV)
+ * @SOUP_STATUS_OK: 200 Success (HTTP). Also used by many lower-level
+ * soup routines to indicate success.
+ * @SOUP_STATUS_CREATED: 201 Created (HTTP)
+ * @SOUP_STATUS_ACCEPTED: 202 Accepted (HTTP)
+ * @SOUP_STATUS_NON_AUTHORITATIVE: 203 Non-Authoritative Information
+ * (HTTP)
+ * @SOUP_STATUS_NO_CONTENT: 204 No Content (HTTP)
+ * @SOUP_STATUS_RESET_CONTENT: 205 Reset Content (HTTP)
+ * @SOUP_STATUS_PARTIAL_CONTENT: 206 Partial Content (HTTP)
+ * @SOUP_STATUS_MULTI_STATUS: 207 Multi-Status (WebDAV)
+ * @SOUP_STATUS_MULTIPLE_CHOICES: 300 Multiple Choices (HTTP)
+ * @SOUP_STATUS_MOVED_PERMANENTLY: 301 Moved Permanently (HTTP)
+ * @SOUP_STATUS_FOUND: 302 Found (HTTP)
+ * @SOUP_STATUS_MOVED_TEMPORARILY: 302 Moved Temporarily (old name,
+ * RFC 2068)
+ * @SOUP_STATUS_SEE_OTHER: 303 See Other (HTTP)
+ * @SOUP_STATUS_NOT_MODIFIED: 304 Not Modified (HTTP)
+ * @SOUP_STATUS_USE_PROXY: 305 Use Proxy (HTTP)
+ * @SOUP_STATUS_NOT_APPEARING_IN_THIS_PROTOCOL: 306 [Unused] (HTTP)
+ * @SOUP_STATUS_TEMPORARY_REDIRECT: 307 Temporary Redirect (HTTP)
+ * @SOUP_STATUS_BAD_REQUEST: 400 Bad Request (HTTP)
+ * @SOUP_STATUS_UNAUTHORIZED: 401 Unauthorized (HTTP)
+ * @SOUP_STATUS_PAYMENT_REQUIRED: 402 Payment Required (HTTP)
+ * @SOUP_STATUS_FORBIDDEN: 403 Forbidden (HTTP)
+ * @SOUP_STATUS_NOT_FOUND: 404 Not Found (HTTP)
+ * @SOUP_STATUS_METHOD_NOT_ALLOWED: 405 Method Not Allowed (HTTP)
+ * @SOUP_STATUS_NOT_ACCEPTABLE: 406 Not Acceptable (HTTP)
+ * @SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED: 407 Proxy Authentication
+ * Required (HTTP)
+ * @SOUP_STATUS_PROXY_UNAUTHORIZED: shorter alias for
+ * %SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED
+ * @SOUP_STATUS_REQUEST_TIMEOUT: 408 Request Timeout (HTTP)
+ * @SOUP_STATUS_CONFLICT: 409 Conflict (HTTP)
+ * @SOUP_STATUS_GONE: 410 Gone (HTTP)
+ * @SOUP_STATUS_LENGTH_REQUIRED: 411 Length Required (HTTP)
+ * @SOUP_STATUS_PRECONDITION_FAILED: 412 Precondition Failed (HTTP)
+ * @SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE: 413 Request Entity Too Large
+ * (HTTP)
+ * @SOUP_STATUS_REQUEST_URI_TOO_LONG: 414 Request-URI Too Long (HTTP)
+ * @SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE: 415 Unsupported Media Type
+ * (HTTP)
+ * @SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE: 416 Requested Range
+ * Not Satisfiable (HTTP)
+ * @SOUP_STATUS_INVALID_RANGE: shorter alias for
+ * %SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE
+ * @SOUP_STATUS_EXPECTATION_FAILED: 417 Expectation Failed (HTTP)
+ * @SOUP_STATUS_UNPROCESSABLE_ENTITY: 422 Unprocessable Entity
+ * (WebDAV)
+ * @SOUP_STATUS_LOCKED: 423 Locked (WebDAV)
+ * @SOUP_STATUS_FAILED_DEPENDENCY: 424 Failed Dependency (WebDAV)
+ * @SOUP_STATUS_INTERNAL_SERVER_ERROR: 500 Internal Server Error
+ * (HTTP)
+ * @SOUP_STATUS_NOT_IMPLEMENTED: 501 Not Implemented (HTTP)
+ * @SOUP_STATUS_BAD_GATEWAY: 502 Bad Gateway (HTTP)
+ * @SOUP_STATUS_SERVICE_UNAVAILABLE: 503 Service Unavailable (HTTP)
+ * @SOUP_STATUS_GATEWAY_TIMEOUT: 504 Gateway Timeout (HTTP)
+ * @SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED: 505 HTTP Version Not
+ * Supported (HTTP)
+ * @SOUP_STATUS_INSUFFICIENT_STORAGE: 507 Insufficient Storage
+ * (WebDAV)
+ * @SOUP_STATUS_NOT_EXTENDED: 510 Not Extended (RFC 2774)
+ *
+ * These represent the known HTTP status code values, plus various
+ * network and internal errors.
+ **/
+
+/**
+ * SOUP_HTTP_ERROR:
+ *
+ * A #GError domain representing an HTTP status. Use a
+ * #SoupKnownStatusCode for the <structfield>code</structfield>
+ * value.
+ **/
+
+
+/* The reason_phrases are not localized because:
+ *
+ * 1. Only ASCII can be used portably in the HTTP Status-Line, so we
+ * would not be able to return localized reason phrases from
+ * SoupServer anyway.
+ *
+ * 2. Having a way for clients to get a localized version of a status
+ * code would just encourage them to present those strings to the
+ * user, which is bad because many of them are fairly
+ * incomprehensible anyway.
+ */
+
+static const struct {
+ guint code;
+ const char *phrase;
+} reason_phrases [] = {
+ /* Transport errors */
+ { SOUP_STATUS_CANCELLED, "Cancelled" },
+ { SOUP_STATUS_CANT_RESOLVE, "Cannot resolve hostname" },
+ { SOUP_STATUS_CANT_RESOLVE_PROXY, "Cannot resolve proxy hostname" },
+ { SOUP_STATUS_CANT_CONNECT, "Cannot connect to destination" },
+ { SOUP_STATUS_CANT_CONNECT_PROXY, "Cannot connect to proxy" },
+ { SOUP_STATUS_SSL_FAILED, "SSL handshake failed" },
+ { SOUP_STATUS_IO_ERROR, "Connection terminated unexpectedly" },
+ { SOUP_STATUS_MALFORMED, "Message Corrupt" },
+ { SOUP_STATUS_TOO_MANY_REDIRECTS, "Too many redirects" },
+
+ /* Informational */
+ { SOUP_STATUS_CONTINUE, "Continue" },
+ { SOUP_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols" },
+ { SOUP_STATUS_PROCESSING, "Processing" },
+
+ /* Success */
+ { SOUP_STATUS_OK, "OK" },
+ { SOUP_STATUS_CREATED, "Created" },
+ { SOUP_STATUS_ACCEPTED, "Accepted" },
+ { SOUP_STATUS_NON_AUTHORITATIVE, "Non-Authoritative Information" },
+ { SOUP_STATUS_NO_CONTENT, "No Content" },
+ { SOUP_STATUS_RESET_CONTENT, "Reset Content" },
+ { SOUP_STATUS_PARTIAL_CONTENT, "Partial Content" },
+ { SOUP_STATUS_MULTI_STATUS, "Multi-Status" },
+
+ /* Redirection */
+ { SOUP_STATUS_MULTIPLE_CHOICES, "Multiple Choices" },
+ { SOUP_STATUS_MOVED_PERMANENTLY, "Moved Permanently" },
+ { SOUP_STATUS_FOUND, "Found" },
+ { SOUP_STATUS_SEE_OTHER, "See Other" },
+ { SOUP_STATUS_NOT_MODIFIED, "Not Modified" },
+ { SOUP_STATUS_USE_PROXY, "Use Proxy" },
+ { SOUP_STATUS_TEMPORARY_REDIRECT, "Temporary Redirect" },
+
+ /* Client error */
+ { SOUP_STATUS_BAD_REQUEST, "Bad Request" },
+ { SOUP_STATUS_UNAUTHORIZED, "Unauthorized" },
+ { SOUP_STATUS_PAYMENT_REQUIRED, "Payment Required" },
+ { SOUP_STATUS_FORBIDDEN, "Forbidden" },
+ { SOUP_STATUS_NOT_FOUND, "Not Found" },
+ { SOUP_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed" },
+ { SOUP_STATUS_NOT_ACCEPTABLE, "Not Acceptable" },
+ { SOUP_STATUS_PROXY_UNAUTHORIZED, "Proxy Authentication Required" },
+ { SOUP_STATUS_REQUEST_TIMEOUT, "Request Timeout" },
+ { SOUP_STATUS_CONFLICT, "Conflict" },
+ { SOUP_STATUS_GONE, "Gone" },
+ { SOUP_STATUS_LENGTH_REQUIRED, "Length Required" },
+ { SOUP_STATUS_PRECONDITION_FAILED, "Precondition Failed" },
+ { SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE, "Request Entity Too Large" },
+ { SOUP_STATUS_REQUEST_URI_TOO_LONG, "Request-URI Too Long" },
+ { SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" },
+ { SOUP_STATUS_INVALID_RANGE, "Requested Range Not Satisfiable" },
+ { SOUP_STATUS_EXPECTATION_FAILED, "Expectation Failed" },
+ { SOUP_STATUS_UNPROCESSABLE_ENTITY, "Unprocessable Entity" },
+ { SOUP_STATUS_LOCKED, "Locked" },
+ { SOUP_STATUS_FAILED_DEPENDENCY, "Failed Dependency" },
+
+ /* Server error */
+ { SOUP_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error" },
+ { SOUP_STATUS_NOT_IMPLEMENTED, "Not Implemented" },
+ { SOUP_STATUS_BAD_GATEWAY, "Bad Gateway" },
+ { SOUP_STATUS_SERVICE_UNAVAILABLE, "Service Unavailable" },
+ { SOUP_STATUS_GATEWAY_TIMEOUT, "Gateway Timeout" },
+ { SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported" },
+ { SOUP_STATUS_INSUFFICIENT_STORAGE, "Insufficient Storage" },
+ { SOUP_STATUS_NOT_EXTENDED, "Not Extended" },
+
+ { 0 }
+};
+
+/**
+ * soup_status_get_phrase:
+ * @status_code: an HTTP status code
+ *
+ * Looks up the stock HTTP description of @status_code. This is used
+ * by soup_message_set_status() to get the correct text to go with a
+ * given status code.
+ *
+ * <emphasis>There is no reason for you to ever use this
+ * function.</emphasis> If you wanted the textual description for the
+ * %status_code of a given #SoupMessage, you should just look at the
+ * message's %reason_phrase. However, you should only do that for use
+ * in debugging messages; HTTP reason phrases are not localized, and
+ * are not generally very descriptive anyway, and so they should never
+ * be presented to the user directly. Instead, you should create you
+ * own error messages based on the status code, and on what you were
+ * trying to do.
+ *
+ * Return value: the (terse, English) description of @status_code
+ **/
+const char *
+soup_status_get_phrase (guint status_code)
+{
+ int i;
+
+ for (i = 0; reason_phrases [i].code; i++) {
+ if (reason_phrases [i].code == status_code)
+ return reason_phrases [i].phrase;
+ }
+
+ return "Unknown Error";
+}
+
+/**
+ * soup_status_proxify:
+ * @status_code: a status code
+ *
+ * Turns %SOUP_STATUS_CANT_RESOLVE into
+ * %SOUP_STATUS_CANT_RESOLVE_PROXY and %SOUP_STATUS_CANT_CONNECT into
+ * %SOUP_STATUS_CANT_CONNECT_PROXY. Other status codes are passed
+ * through unchanged.
+ *
+ * Return value: the "proxified" equivalent of @status_code.
+ *
+ * Since: 2.26
+ **/
+guint
+soup_status_proxify (guint status_code)
+{
+ if (status_code == SOUP_STATUS_CANT_RESOLVE)
+ return SOUP_STATUS_CANT_RESOLVE_PROXY;
+ else if (status_code == SOUP_STATUS_CANT_CONNECT)
+ return SOUP_STATUS_CANT_CONNECT_PROXY;
+ else
+ return status_code;
+}
+
+GQuark
+soup_http_error_quark (void)
+{
+ static GQuark error;
+ if (!error)
+ error = g_quark_from_static_string ("soup_http_error_quark");
+ return error;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-status.h: HTTP status code and status class definitions
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_STATUS_H
+#define SOUP_STATUS_H 1
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_STATUS_IS_TRANSPORT_ERROR(status) ((status) > 0 && (status) < 100)
+#define SOUP_STATUS_IS_INFORMATIONAL(status) ((status) >= 100 && (status) < 200)
+#define SOUP_STATUS_IS_SUCCESSFUL(status) ((status) >= 200 && (status) < 300)
+#define SOUP_STATUS_IS_REDIRECTION(status) ((status) >= 300 && (status) < 400)
+#define SOUP_STATUS_IS_CLIENT_ERROR(status) ((status) >= 400 && (status) < 500)
+#define SOUP_STATUS_IS_SERVER_ERROR(status) ((status) >= 500 && (status) < 600)
+
+typedef enum {
+ SOUP_STATUS_NONE,
+
+ /* Transport Errors */
+ SOUP_STATUS_CANCELLED = 1,
+ SOUP_STATUS_CANT_RESOLVE,
+ SOUP_STATUS_CANT_RESOLVE_PROXY,
+ SOUP_STATUS_CANT_CONNECT,
+ SOUP_STATUS_CANT_CONNECT_PROXY,
+ SOUP_STATUS_SSL_FAILED,
+ SOUP_STATUS_IO_ERROR,
+ SOUP_STATUS_MALFORMED,
+ SOUP_STATUS_TRY_AGAIN,
+ SOUP_STATUS_TOO_MANY_REDIRECTS,
+ SOUP_STATUS_TLS_FAILED,
+
+ /* HTTP Status Codes */
+ SOUP_STATUS_CONTINUE = 100,
+ SOUP_STATUS_SWITCHING_PROTOCOLS = 101,
+ SOUP_STATUS_PROCESSING = 102, /* WebDAV */
+
+ SOUP_STATUS_OK = 200,
+ SOUP_STATUS_CREATED = 201,
+ SOUP_STATUS_ACCEPTED = 202,
+ SOUP_STATUS_NON_AUTHORITATIVE = 203,
+ SOUP_STATUS_NO_CONTENT = 204,
+ SOUP_STATUS_RESET_CONTENT = 205,
+ SOUP_STATUS_PARTIAL_CONTENT = 206,
+ SOUP_STATUS_MULTI_STATUS = 207, /* WebDAV */
+
+ SOUP_STATUS_MULTIPLE_CHOICES = 300,
+ SOUP_STATUS_MOVED_PERMANENTLY = 301,
+ SOUP_STATUS_FOUND = 302,
+ SOUP_STATUS_MOVED_TEMPORARILY = 302, /* RFC 2068 */
+ SOUP_STATUS_SEE_OTHER = 303,
+ SOUP_STATUS_NOT_MODIFIED = 304,
+ SOUP_STATUS_USE_PROXY = 305,
+ SOUP_STATUS_NOT_APPEARING_IN_THIS_PROTOCOL = 306, /* (reserved) */
+ SOUP_STATUS_TEMPORARY_REDIRECT = 307,
+
+ SOUP_STATUS_BAD_REQUEST = 400,
+ SOUP_STATUS_UNAUTHORIZED = 401,
+ SOUP_STATUS_PAYMENT_REQUIRED = 402, /* (reserved) */
+ SOUP_STATUS_FORBIDDEN = 403,
+ SOUP_STATUS_NOT_FOUND = 404,
+ SOUP_STATUS_METHOD_NOT_ALLOWED = 405,
+ SOUP_STATUS_NOT_ACCEPTABLE = 406,
+ SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
+ SOUP_STATUS_PROXY_UNAUTHORIZED = SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED,
+ SOUP_STATUS_REQUEST_TIMEOUT = 408,
+ SOUP_STATUS_CONFLICT = 409,
+ SOUP_STATUS_GONE = 410,
+ SOUP_STATUS_LENGTH_REQUIRED = 411,
+ SOUP_STATUS_PRECONDITION_FAILED = 412,
+ SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE = 413,
+ SOUP_STATUS_REQUEST_URI_TOO_LONG = 414,
+ SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
+ SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
+ SOUP_STATUS_INVALID_RANGE = SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE,
+ SOUP_STATUS_EXPECTATION_FAILED = 417,
+ SOUP_STATUS_UNPROCESSABLE_ENTITY = 422, /* WebDAV */
+ SOUP_STATUS_LOCKED = 423, /* WebDAV */
+ SOUP_STATUS_FAILED_DEPENDENCY = 424, /* WebDAV */
+
+ SOUP_STATUS_INTERNAL_SERVER_ERROR = 500,
+ SOUP_STATUS_NOT_IMPLEMENTED = 501,
+ SOUP_STATUS_BAD_GATEWAY = 502,
+ SOUP_STATUS_SERVICE_UNAVAILABLE = 503,
+ SOUP_STATUS_GATEWAY_TIMEOUT = 504,
+ SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
+ SOUP_STATUS_INSUFFICIENT_STORAGE = 507, /* WebDAV search */
+ SOUP_STATUS_NOT_EXTENDED = 510 /* RFC 2774 */
+} SoupKnownStatusCode;
+
+const char *soup_status_get_phrase (guint status_code);
+guint soup_status_proxify (guint status_code);
+
+#define SOUP_HTTP_ERROR soup_http_error_quark()
+GQuark soup_http_error_quark (void);
+
+G_END_DECLS
+
+#endif /* SOUP_STATUS_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_TYPES_H
+#define SOUP_TYPES_H 1
+
+#include <gio/gio.h>
+
+#include <libsoup/soup-status.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SoupAddress SoupAddress;
+typedef struct _SoupAuth SoupAuth;
+typedef struct _SoupAuthDomain SoupAuthDomain;
+typedef struct _SoupCookie SoupCookie;
+typedef struct _SoupCookieJar SoupCookieJar;
+typedef struct _SoupDate SoupDate;
+typedef struct _SoupMessage SoupMessage;
+typedef struct _SoupServer SoupServer;
+typedef struct _SoupSession SoupSession;
+typedef struct _SoupSessionAsync SoupSessionAsync;
+typedef struct _SoupSessionFeature SoupSessionFeature;
+typedef struct _SoupSessionSync SoupSessionSync;
+typedef struct _SoupSocket SoupSocket;
+typedef struct _SoupURI SoupURI;
+
+/*< private >*/
+typedef struct _SoupConnection SoupConnection;
+typedef struct _SoupMessageQueue SoupMessageQueue;
+typedef struct _SoupMessageQueueItem SoupMessageQueueItem;
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* soup-uri.c : utility functions to parse URLs */
+
+/*
+ * Copyright 1999-2003 Ximian, Inc.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "soup-uri.h"
+#include "soup-misc-private.h"
+#include "soup-form.h"
+#include "soup-misc.h"
+
+/**
+ * SECTION:soup-uri
+ * @short_description: URIs
+ *
+ * A #SoupURI represents a (parsed) URI.
+ *
+ * Many applications will not need to use #SoupURI directly at all; on
+ * the client side, soup_message_new() takes a stringified URI, and on
+ * the server side, the path and query components are provided for you
+ * in the server callback.
+ **/
+
+/**
+ * SoupURI:
+ * @scheme: the URI scheme (eg, "http")
+ * @user: a username, or %NULL
+ * @password: a password, or %NULL
+ * @host: the hostname or IP address
+ * @port: the port number on @host
+ * @path: the path on @host
+ * @query: a query for @path, or %NULL
+ * @fragment: a fragment identifier within @path, or %NULL
+ *
+ * A #SoupURI represents a (parsed) URI. #SoupURI supports RFC 3986
+ * (URI Generic Syntax), and can parse any valid URI. However, libsoup
+ * only uses "http" and "https" URIs internally; You can use
+ * SOUP_URI_VALID_FOR_HTTP() to test if a #SoupURI is a valid HTTP
+ * URI.
+ *
+ * @scheme will always be set in any URI. It is an interned string and
+ * is always all lowercase. (If you parse a URI with a non-lowercase
+ * scheme, it will be converted to lowercase.) The macros
+ * %SOUP_URI_SCHEME_HTTP and %SOUP_URI_SCHEME_HTTPS provide the
+ * interned values for "http" and "https" and can be compared against
+ * URI @scheme values.
+ *
+ * @user and @password are parsed as defined in the older URI specs
+ * (ie, separated by a colon; RFC 3986 only talks about a single
+ * "userinfo" field). Note that @password is not included in the
+ * output of soup_uri_to_string(). libsoup does not normally use these
+ * fields; authentication is handled via #SoupSession signals.
+ *
+ * @host contains the hostname, and @port the port specified in the
+ * URI. If the URI doesn't contain a hostname, @host will be %NULL,
+ * and if it doesn't specify a port, @port may be 0. However, for
+ * "http" and "https" URIs, @host is guaranteed to be non-%NULL
+ * (trying to parse an http URI with no @host will return %NULL), and
+ * @port will always be non-0 (because libsoup knows the default value
+ * to use when it is not specified in the URI).
+ *
+ * @path is always non-%NULL. For http/https URIs, @path will never be
+ * an empty string either; if the input URI has no path, the parsed
+ * #SoupURI will have a @path of "/".
+ *
+ * @query and @fragment are optional for all URI types.
+ * soup_form_decode() may be useful for parsing @query.
+ *
+ * Note that @path, @query, and @fragment may contain
+ * %<!-- -->-encoded characters. soup_uri_new() calls
+ * soup_uri_normalize() on them, but not soup_uri_decode(). This is
+ * necessary to ensure that soup_uri_to_string() will generate a URI
+ * that has exactly the same meaning as the original. (In theory,
+ * #SoupURI should leave @user, @password, and @host partially-encoded
+ * as well, but this would be more annoying than useful.)
+ **/
+
+/**
+ * SOUP_URI_VALID_FOR_HTTP:
+ * @uri: a #SoupURI
+ *
+ * Tests if @uri is a valid #SoupURI for HTTP communication; that is, if
+ * it can be used to construct a #SoupMessage.
+ *
+ * Return value: %TRUE if @uri is a valid "http" or "https" URI.
+ *
+ * Since: 2.24
+ **/
+
+static void append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars);
+static char *uri_normalized_copy (const char *str, int length, const char *unescape_extra);
+
+gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
+gpointer _SOUP_URI_SCHEME_FTP;
+gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA;
+
+static inline const char *
+soup_uri_parse_scheme (const char *scheme, int len)
+{
+ if (len == 4 && !g_ascii_strncasecmp (scheme, "http", len)) {
+ return SOUP_URI_SCHEME_HTTP;
+ } else if (len == 5 && !g_ascii_strncasecmp (scheme, "https", len)) {
+ return SOUP_URI_SCHEME_HTTPS;
+ } else {
+ char *lower_scheme;
+
+ lower_scheme = g_ascii_strdown (scheme, len);
+ scheme = g_intern_static_string (lower_scheme);
+ if (scheme != (const char *)lower_scheme)
+ g_free (lower_scheme);
+ return scheme;
+ }
+}
+
+static inline guint
+soup_scheme_default_port (const char *scheme)
+{
+ if (scheme == SOUP_URI_SCHEME_HTTP)
+ return 80;
+ else if (scheme == SOUP_URI_SCHEME_HTTPS)
+ return 443;
+ else if (scheme == SOUP_URI_SCHEME_FTP)
+ return 21;
+ else
+ return 0;
+}
+
+/**
+ * soup_uri_new_with_base:
+ * @base: a base URI
+ * @uri_string: the URI
+ *
+ * Parses @uri_string relative to @base.
+ *
+ * Return value: a parsed #SoupURI.
+ **/
+SoupURI *
+soup_uri_new_with_base (SoupURI *base, const char *uri_string)
+{
+ SoupURI *uri;
+ const char *end, *hash, *colon, *at, *path, *question;
+ const char *p, *hostend;
+ gboolean remove_dot_segments = TRUE;
+ int len;
+
+ /* First some cleanup steps (which are supposed to all be no-ops,
+ * but...). Skip initial whitespace, strip out internal tabs and
+ * line breaks, and ignore trailing whitespace.
+ */
+ while (g_ascii_isspace (*uri_string))
+ uri_string++;
+
+ len = strcspn (uri_string, "\t\n\r");
+ if (uri_string[len]) {
+ char *clean = g_malloc (strlen (uri_string) + 1), *d;
+ const char *s;
+
+ for (s = uri_string, d = clean; *s; s++) {
+ if (*s != '\t' && *s != '\n' && *s != '\r')
+ *d++ = *s;
+ }
+ *d = '\0';
+
+ uri = soup_uri_new_with_base (base, clean);
+ g_free (clean);
+ return uri;
+ }
+ end = uri_string + len;
+ while (end > uri_string && g_ascii_isspace (end[-1]))
+ end--;
+
+ uri = g_slice_new0 (SoupURI);
+
+ /* Find fragment. */
+ hash = strchr (uri_string, '#');
+ if (hash) {
+ uri->fragment = uri_normalized_copy (hash + 1, end - hash + 1,
+ NULL);
+ end = hash;
+ }
+
+ /* Find scheme: initial [a-z+.-]* substring until ":" */
+ p = uri_string;
+ while (p < end && (g_ascii_isalnum (*p) ||
+ *p == '.' || *p == '+' || *p == '-'))
+ p++;
+
+ if (p > uri_string && *p == ':') {
+ uri->scheme = soup_uri_parse_scheme (uri_string, p - uri_string);
+ uri_string = p + 1;
+ }
+
+ if (uri_string == end && !base && !uri->fragment)
+ return uri;
+
+ /* Check for authority */
+ if (strncmp (uri_string, "//", 2) == 0) {
+ uri_string += 2;
+
+ path = uri_string + strcspn (uri_string, "/?#");
+ if (path > end)
+ path = end;
+ at = strchr (uri_string, '@');
+ if (at && at < path) {
+ colon = strchr (uri_string, ':');
+ if (colon && colon < at) {
+ uri->password = uri_decoded_copy (colon + 1,
+ at - colon - 1);
+ } else {
+ uri->password = NULL;
+ colon = at;
+ }
+
+ uri->user = uri_decoded_copy (uri_string,
+ colon - uri_string);
+ uri_string = at + 1;
+ } else
+ uri->user = uri->password = NULL;
+
+ /* Find host and port. */
+ if (*uri_string == '[') {
+ uri_string++;
+ hostend = strchr (uri_string, ']');
+ if (!hostend || hostend > path) {
+ soup_uri_free (uri);
+ return NULL;
+ }
+ if (*(hostend + 1) == ':')
+ colon = hostend + 1;
+ else
+ colon = NULL;
+ } else {
+ colon = memchr (uri_string, ':', path - uri_string);
+ hostend = colon ? colon : path;
+ }
+
+ uri->host = uri_decoded_copy (uri_string, hostend - uri_string);
+
+ if (colon && colon != path - 1) {
+ char *portend;
+ uri->port = strtoul (colon + 1, &portend, 10);
+ if (portend != (char *)path) {
+ soup_uri_free (uri);
+ return NULL;
+ }
+ }
+
+ uri_string = path;
+ }
+
+ /* Find query */
+ question = memchr (uri_string, '?', end - uri_string);
+ if (question) {
+ uri->query = uri_normalized_copy (question + 1,
+ end - (question + 1),
+ NULL);
+ end = question;
+ }
+
+ if (end != uri_string) {
+ uri->path = uri_normalized_copy (uri_string, end - uri_string,
+ NULL);
+ }
+
+ /* Apply base URI. This is spelled out in RFC 3986. */
+ if (base && !uri->scheme && uri->host)
+ uri->scheme = base->scheme;
+ else if (base && !uri->scheme) {
+ uri->scheme = base->scheme;
+ uri->user = g_strdup (base->user);
+ uri->password = g_strdup (base->password);
+ uri->host = g_strdup (base->host);
+ uri->port = base->port;
+
+ if (!uri->path) {
+ uri->path = g_strdup (base->path);
+ if (!uri->query)
+ uri->query = g_strdup (base->query);
+ remove_dot_segments = FALSE;
+ } else if (*uri->path != '/') {
+ char *newpath, *last;
+
+ last = strrchr (base->path, '/');
+ if (last) {
+ newpath = g_strdup_printf ("%.*s%s",
+ (int)(last + 1 - base->path),
+ base->path,
+ uri->path);
+ } else
+ newpath = g_strdup_printf ("/%s", uri->path);
+
+ g_free (uri->path);
+ uri->path = newpath;
+ }
+ }
+
+ if (remove_dot_segments && uri->path && *uri->path) {
+ char *p, *q;
+
+ /* Remove "./" where "." is a complete segment. */
+ for (p = uri->path + 1; *p; ) {
+ if (*(p - 1) == '/' &&
+ *p == '.' && *(p + 1) == '/')
+ memmove (p, p + 2, strlen (p + 2) + 1);
+ else
+ p++;
+ }
+ /* Remove "." at end. */
+ if (p > uri->path + 2 &&
+ *(p - 1) == '.' && *(p - 2) == '/')
+ *(p - 1) = '\0';
+
+ /* Remove "<segment>/../" where <segment> != ".." */
+ for (p = uri->path + 1; *p; ) {
+ if (!strncmp (p, "../", 3)) {
+ p += 3;
+ continue;
+ }
+ q = strchr (p + 1, '/');
+ if (!q)
+ break;
+ if (strncmp (q, "/../", 4) != 0) {
+ p = q + 1;
+ continue;
+ }
+ memmove (p, q + 4, strlen (q + 4) + 1);
+ p = uri->path + 1;
+ }
+ /* Remove "<segment>/.." at end where <segment> != ".." */
+ q = strrchr (uri->path, '/');
+ if (q && !strcmp (q, "/..")) {
+ p = q - 1;
+ while (p > uri->path && *p != '/')
+ p--;
+ if (strncmp (p, "/../", 4) != 0)
+ *(p + 1) = 0;
+ }
+
+ /* Remove extraneous initial "/.."s */
+ while (!strncmp (uri->path, "/../", 4))
+ memmove (uri->path, uri->path + 3, strlen (uri->path) - 2);
+ if (!strcmp (uri->path, "/.."))
+ uri->path[1] = '\0';
+ }
+
+ /* HTTP-specific stuff */
+ if (uri->scheme == SOUP_URI_SCHEME_HTTP ||
+ uri->scheme == SOUP_URI_SCHEME_HTTPS) {
+ if (!uri->path)
+ uri->path = g_strdup ("/");
+ if (!SOUP_URI_VALID_FOR_HTTP (uri)) {
+ soup_uri_free (uri);
+ return NULL;
+ }
+ }
+
+ if (uri->scheme == SOUP_URI_SCHEME_FTP) {
+ if (!uri->host) {
+ soup_uri_free (uri);
+ return NULL;
+ }
+ }
+
+ if (!uri->port)
+ uri->port = soup_scheme_default_port (uri->scheme);
+ if (!uri->path)
+ uri->path = g_strdup ("");
+
+ return uri;
+}
+
+/**
+ * soup_uri_new:
+ * @uri_string: a URI
+ *
+ * Parses an absolute URI.
+ *
+ * You can also pass %NULL for @uri_string if you want to get back an
+ * "empty" #SoupURI that you can fill in by hand. (You will need to
+ * call at least soup_uri_set_scheme() and soup_uri_set_path(), since
+ * those fields are required.)
+ *
+ * Return value: a #SoupURI, or %NULL.
+ **/
+SoupURI *
+soup_uri_new (const char *uri_string)
+{
+ SoupURI *uri;
+
+ if (!uri_string)
+ return g_slice_new0 (SoupURI);
+
+ uri = soup_uri_new_with_base (NULL, uri_string);
+ if (!uri)
+ return NULL;
+ if (!uri->scheme) {
+ soup_uri_free (uri);
+ return NULL;
+ }
+
+ return uri;
+}
+
+
+/**
+ * soup_uri_to_string:
+ * @uri: a #SoupURI
+ * @just_path_and_query: if %TRUE, output just the path and query portions
+ *
+ * Returns a string representing @uri.
+ *
+ * If @just_path_and_query is %TRUE, this concatenates the path and query
+ * together. That is, it constructs the string that would be needed in
+ * the Request-Line of an HTTP request for @uri.
+ *
+ * Return value: a string representing @uri, which the caller must free.
+ **/
+char *
+soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
+{
+ GString *str;
+ char *return_result;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ /* IF YOU CHANGE ANYTHING IN THIS FUNCTION, RUN
+ * tests/uri-parsing AFTERWARD.
+ */
+
+ str = g_string_sized_new (20);
+
+ if (uri->scheme && !just_path_and_query)
+ g_string_append_printf (str, "%s:", uri->scheme);
+ if (uri->host && !just_path_and_query) {
+ g_string_append (str, "//");
+ if (uri->user) {
+ append_uri_encoded (str, uri->user, ":;@?/");
+ g_string_append_c (str, '@');
+ }
+ if (strchr (uri->host, ':')) {
+ g_string_append_c (str, '[');
+ g_string_append (str, uri->host);
+ g_string_append_c (str, ']');
+ } else
+ append_uri_encoded (str, uri->host, ":/");
+ if (uri->port && uri->port != soup_scheme_default_port (uri->scheme))
+ g_string_append_printf (str, ":%u", uri->port);
+ if (!uri->path && (uri->query || uri->fragment))
+ g_string_append_c (str, '/');
+ }
+
+ if (uri->path && *uri->path)
+ g_string_append (str, uri->path);
+
+ if (uri->query) {
+ g_string_append_c (str, '?');
+ g_string_append (str, uri->query);
+ }
+ if (uri->fragment && !just_path_and_query) {
+ g_string_append_c (str, '#');
+ g_string_append (str, uri->fragment);
+ }
+
+ return_result = str->str;
+ g_string_free (str, FALSE);
+
+ return return_result;
+}
+
+/**
+ * soup_uri_copy:
+ * @uri: a #SoupURI
+ *
+ * Copies @uri
+ *
+ * Return value: a copy of @uri, which must be freed with soup_uri_free()
+ **/
+SoupURI *
+soup_uri_copy (SoupURI *uri)
+{
+ SoupURI *dup;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ dup = g_slice_new0 (SoupURI);
+ dup->scheme = uri->scheme;
+ dup->user = g_strdup (uri->user);
+ dup->password = g_strdup (uri->password);
+ dup->host = g_strdup (uri->host);
+ dup->port = uri->port;
+ dup->path = g_strdup (uri->path);
+ dup->query = g_strdup (uri->query);
+ dup->fragment = g_strdup (uri->fragment);
+
+ return dup;
+}
+
+static inline gboolean
+parts_equal (const char *one, const char *two, gboolean insensitive)
+{
+ if (!one && !two)
+ return TRUE;
+ if (!one || !two)
+ return FALSE;
+ return insensitive ? !g_ascii_strcasecmp (one, two) : !strcmp (one, two);
+}
+
+/**
+ * soup_uri_equal:
+ * @uri1: a #SoupURI
+ * @uri2: another #SoupURI
+ *
+ * Tests whether or not @uri1 and @uri2 are equal in all parts
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+soup_uri_equal (SoupURI *uri1, SoupURI *uri2)
+{
+ if (uri1->scheme != uri2->scheme ||
+ uri1->port != uri2->port ||
+ !parts_equal (uri1->user, uri2->user, FALSE) ||
+ !parts_equal (uri1->password, uri2->password, FALSE) ||
+ !parts_equal (uri1->host, uri2->host, TRUE) ||
+ !parts_equal (uri1->path, uri2->path, FALSE) ||
+ !parts_equal (uri1->query, uri2->query, FALSE) ||
+ !parts_equal (uri1->fragment, uri2->fragment, FALSE))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * soup_uri_free:
+ * @uri: a #SoupURI
+ *
+ * Frees @uri.
+ **/
+void
+soup_uri_free (SoupURI *uri)
+{
+ g_return_if_fail (uri != NULL);
+
+ g_free (uri->user);
+ g_free (uri->password);
+ g_free (uri->host);
+ g_free (uri->path);
+ g_free (uri->query);
+ g_free (uri->fragment);
+
+ g_slice_free (SoupURI, uri);
+}
+
+static void
+append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars)
+{
+ const unsigned char *s = (const unsigned char *)in;
+
+ while (*s) {
+ if (soup_char_is_uri_percent_encoded (*s) ||
+ soup_char_is_uri_gen_delims (*s) ||
+ (extra_enc_chars && strchr (extra_enc_chars, *s)))
+ g_string_append_printf (str, "%%%02X", (int)*s++);
+ else
+ g_string_append_c (str, *s++);
+ }
+}
+
+/**
+ * soup_uri_encode:
+ * @part: a URI part
+ * @escape_extra: (allow-none): additional reserved characters to
+ * escape (or %NULL)
+ *
+ * This %<!-- -->-encodes the given URI part and returns the escaped
+ * version in allocated memory, which the caller must free when it is
+ * done.
+ *
+ * Return value: the encoded URI part
+ **/
+char *
+soup_uri_encode (const char *part, const char *escape_extra)
+{
+ GString *str;
+ char *encoded;
+
+ str = g_string_new (NULL);
+ append_uri_encoded (str, part, escape_extra);
+ encoded = str->str;
+ g_string_free (str, FALSE);
+
+ return encoded;
+}
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+char *
+uri_decoded_copy (const char *part, int length)
+{
+ unsigned char *s, *d;
+ char *decoded = g_strndup (part, length);
+
+ s = d = (unsigned char *)decoded;
+ do {
+ if (*s == '%') {
+ if (!g_ascii_isxdigit (s[1]) ||
+ !g_ascii_isxdigit (s[2])) {
+ *d++ = *s;
+ continue;
+ }
+ *d++ = HEXCHAR (s);
+ s += 2;
+ } else
+ *d++ = *s;
+ } while (*s++);
+
+ return decoded;
+}
+
+/**
+ * soup_uri_decode:
+ * @part: a URI part
+ *
+ * Fully %<!-- -->-decodes @part.
+ *
+ * In the past, this would return %NULL if @part contained invalid
+ * percent-encoding, but now it just ignores the problem (as
+ * soup_uri_new() already did).
+ *
+ * Return value: the decoded URI part.
+ */
+char *
+soup_uri_decode (const char *part)
+{
+ return uri_decoded_copy (part, strlen (part));
+}
+
+static char *
+uri_normalized_copy (const char *part, int length,
+ const char *unescape_extra)
+{
+ unsigned char *s, *d, c;
+ char *normalized = g_strndup (part, length);
+ gboolean need_fixup = FALSE;
+
+ s = d = (unsigned char *)normalized;
+ do {
+ if (*s == '%') {
+ if (!g_ascii_isxdigit (s[1]) ||
+ !g_ascii_isxdigit (s[2])) {
+ *d++ = *s;
+ continue;
+ }
+
+ c = HEXCHAR (s);
+ if (soup_char_is_uri_unreserved (c) ||
+ (unescape_extra && strchr (unescape_extra, c))) {
+ *d++ = c;
+ s += 2;
+ } else {
+ /* We leave it unchanged. We used to uppercase percent-encoded
+ * triplets but we do not do it any more as RFC3986 Section 6.2.2.1
+ * says that they only SHOULD be case normalized.
+ */
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s;
+ }
+ } else {
+ if (*s == ' ')
+ need_fixup = TRUE;
+ *d++ = *s;
+ }
+ } while (*s++);
+
+ if (need_fixup) {
+ GString *fixed;
+ char *sp, *p;
+
+ fixed = g_string_new (NULL);
+ p = normalized;
+ while ((sp = strchr (p, ' '))) {
+ g_string_append_len (fixed, p, sp - p);
+ g_string_append (fixed, "%20");
+ p = sp + 1;
+ }
+ g_string_append (fixed, p);
+ g_free (normalized);
+ normalized = g_string_free (fixed, FALSE);
+ }
+
+ return normalized;
+}
+
+/**
+ * soup_uri_normalize:
+ * @part: a URI part
+ * @unescape_extra: reserved characters to unescape (or %NULL)
+ *
+ * %<!-- -->-decodes any "unreserved" characters (or characters in
+ * @unescape_extra) in @part.
+ *
+ * "Unreserved" characters are those that are not allowed to be used
+ * for punctuation according to the URI spec. For example, letters are
+ * unreserved, so soup_uri_normalize() will turn
+ * <literal>http://example.com/foo/b%<!-- -->61r</literal> into
+ * <literal>http://example.com/foo/bar</literal>, which is guaranteed
+ * to mean the same thing. However, "/" is "reserved", so
+ * <literal>http://example.com/foo%<!-- -->2Fbar</literal> would not
+ * be changed, because it might mean something different to the
+ * server.
+ *
+ * In the past, this would return %NULL if @part contained invalid
+ * percent-encoding, but now it just ignores the problem (as
+ * soup_uri_new() already did).
+ *
+ * Return value: the normalized URI part
+ */
+char *
+soup_uri_normalize (const char *part, const char *unescape_extra)
+{
+ return uri_normalized_copy (part, strlen (part), unescape_extra);
+}
+
+
+/**
+ * soup_uri_uses_default_port:
+ * @uri: a #SoupURI
+ *
+ * Tests if @uri uses the default port for its scheme. (Eg, 80 for
+ * http.) (This only works for http and https; libsoup does not know
+ * the default ports of other protocols.)
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+soup_uri_uses_default_port (SoupURI *uri)
+{
+ g_return_val_if_fail (uri->scheme == SOUP_URI_SCHEME_HTTP ||
+ uri->scheme == SOUP_URI_SCHEME_HTTPS ||
+ uri->scheme == SOUP_URI_SCHEME_FTP, FALSE);
+
+ return uri->port == soup_scheme_default_port (uri->scheme);
+}
+
+/**
+ * SOUP_URI_SCHEME_HTTP:
+ *
+ * "http" as an interned string. This can be compared directly against
+ * the value of a #SoupURI's <structfield>scheme</structfield>
+ **/
+
+/**
+ * SOUP_URI_SCHEME_HTTPS:
+ *
+ * "https" as an interned string. This can be compared directly
+ * against the value of a #SoupURI's <structfield>scheme</structfield>
+ **/
+
+/**
+ * soup_uri_get_scheme:
+ * @uri: a #SoupURI
+ *
+ * Gets @uri's scheme.
+ *
+ * Return value: @uri's scheme.
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_uri_get_scheme (SoupURI *uri)
+{
+ return uri->scheme;
+}
+
+/**
+ * soup_uri_set_scheme:
+ * @uri: a #SoupURI
+ * @scheme: the URI scheme
+ *
+ * Sets @uri's scheme to @scheme. This will also set @uri's port to
+ * the default port for @scheme, if known.
+ **/
+void
+soup_uri_set_scheme (SoupURI *uri, const char *scheme)
+{
+ uri->scheme = soup_uri_parse_scheme (scheme, strlen (scheme));
+ uri->port = soup_scheme_default_port (uri->scheme);
+}
+
+/**
+ * soup_uri_get_user:
+ * @uri: a #SoupURI
+ *
+ * Gets @uri's user.
+ *
+ * Return value: @uri's user.
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_uri_get_user (SoupURI *uri)
+{
+ return uri->user;
+}
+
+/**
+ * soup_uri_set_user:
+ * @uri: a #SoupURI
+ * @user: the username, or %NULL
+ *
+ * Sets @uri's user to @user.
+ **/
+void
+soup_uri_set_user (SoupURI *uri, const char *user)
+{
+ g_free (uri->user);
+ uri->user = g_strdup (user);
+}
+
+/**
+ * soup_uri_get_password:
+ * @uri: a #SoupURI
+ *
+ * Gets @uri's password.
+ *
+ * Return value: @uri's password.
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_uri_get_password (SoupURI *uri)
+{
+ return uri->password;
+}
+
+/**
+ * soup_uri_set_password:
+ * @uri: a #SoupURI
+ * @password: the password, or %NULL
+ *
+ * Sets @uri's password to @password.
+ **/
+void
+soup_uri_set_password (SoupURI *uri, const char *password)
+{
+ g_free (uri->password);
+ uri->password = g_strdup (password);
+}
+
+/**
+ * soup_uri_get_host:
+ * @uri: a #SoupURI
+ *
+ * Gets @uri's host.
+ *
+ * Return value: @uri's host.
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_uri_get_host (SoupURI *uri)
+{
+ return uri->host;
+}
+
+/**
+ * soup_uri_set_host:
+ * @uri: a #SoupURI
+ * @host: the hostname or IP address, or %NULL
+ *
+ * Sets @uri's host to @host.
+ *
+ * If @host is an IPv6 IP address, it should not include the brackets
+ * required by the URI syntax; they will be added automatically when
+ * converting @uri to a string.
+ **/
+void
+soup_uri_set_host (SoupURI *uri, const char *host)
+{
+ g_free (uri->host);
+ uri->host = g_strdup (host);
+}
+
+/**
+ * soup_uri_get_port:
+ * @uri: a #SoupURI
+ *
+ * Gets @uri's port.
+ *
+ * Return value: @uri's port.
+ *
+ * Since: 2.32
+ **/
+guint
+soup_uri_get_port (SoupURI *uri)
+{
+ return uri->port;
+}
+
+/**
+ * soup_uri_set_port:
+ * @uri: a #SoupURI
+ * @port: the port, or 0
+ *
+ * Sets @uri's port to @port. If @port is 0, @uri will not have an
+ * explicitly-specified port.
+ **/
+void
+soup_uri_set_port (SoupURI *uri, guint port)
+{
+ uri->port = port;
+}
+
+/**
+ * soup_uri_get_path:
+ * @uri: a #SoupURI
+ *
+ * Gets @uri's path.
+ *
+ * Return value: @uri's path.
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_uri_get_path (SoupURI *uri)
+{
+ return uri->path;
+}
+
+/**
+ * soup_uri_set_path:
+ * @uri: a #SoupURI
+ * @path: the path
+ *
+ * Sets @uri's path to @path.
+ **/
+void
+soup_uri_set_path (SoupURI *uri, const char *path)
+{
+ g_free (uri->path);
+ uri->path = g_strdup (path);
+}
+
+/**
+ * soup_uri_get_query:
+ * @uri: a #SoupURI
+ *
+ * Gets @uri's query.
+ *
+ * Return value: @uri's query.
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_uri_get_query (SoupURI *uri)
+{
+ return uri->query;
+}
+
+/**
+ * soup_uri_set_query:
+ * @uri: a #SoupURI
+ * @query: the query
+ *
+ * Sets @uri's query to @query.
+ **/
+void
+soup_uri_set_query (SoupURI *uri, const char *query)
+{
+ g_free (uri->query);
+ uri->query = g_strdup (query);
+}
+
+/**
+ * soup_uri_set_query_from_form:
+ * @uri: a #SoupURI
+ * @form: (element-type utf8 utf8): a #GHashTable containing HTML form
+ * information
+ *
+ * Sets @uri's query to the result of encoding @form according to the
+ * HTML form rules. See soup_form_encode_hash() for more information.
+ **/
+void
+soup_uri_set_query_from_form (SoupURI *uri, GHashTable *form)
+{
+ g_free (uri->query);
+ uri->query = soup_form_encode_hash (form);
+}
+
+/**
+ * soup_uri_set_query_from_fields:
+ * @uri: a #SoupURI
+ * @first_field: name of the first form field to encode into query
+ * @...: value of @first_field, followed by additional field names
+ * and values, terminated by %NULL.
+ *
+ * Sets @uri's query to the result of encoding the given form fields
+ * and values according to the * HTML form rules. See
+ * soup_form_encode() for more information.
+ **/
+void
+soup_uri_set_query_from_fields (SoupURI *uri,
+ const char *first_field,
+ ...)
+{
+ va_list args;
+
+ g_free (uri->query);
+ va_start (args, first_field);
+ uri->query = soup_form_encode_valist (first_field, args);
+ va_end (args);
+}
+
+/**
+ * soup_uri_get_fragment:
+ * @uri: a #SoupURI
+ *
+ * Gets @uri's fragment.
+ *
+ * Return value: @uri's fragment.
+ *
+ * Since: 2.32
+ **/
+const char *
+soup_uri_get_fragment (SoupURI *uri)
+{
+ return uri->fragment;
+}
+
+/**
+ * soup_uri_set_fragment:
+ * @uri: a #SoupURI
+ * @fragment: the fragment
+ *
+ * Sets @uri's fragment to @fragment.
+ **/
+void
+soup_uri_set_fragment (SoupURI *uri, const char *fragment)
+{
+ g_free (uri->fragment);
+ uri->fragment = g_strdup (fragment);
+}
+
+/**
+ * soup_uri_copy_host:
+ * @uri: a #SoupUri
+ *
+ * Makes a copy of @uri, considering only the protocol, host, and port
+ *
+ * Return value: the new #SoupUri
+ *
+ * Since: 2.26.3
+ **/
+SoupURI *
+soup_uri_copy_host (SoupURI *uri)
+{
+ SoupURI *dup;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ dup = soup_uri_new (NULL);
+ dup->scheme = uri->scheme;
+ dup->host = g_strdup (uri->host);
+ dup->port = uri->port;
+ if (dup->scheme == SOUP_URI_SCHEME_HTTP ||
+ dup->scheme == SOUP_URI_SCHEME_HTTPS)
+ dup->path = g_strdup ("");
+
+ return dup;
+}
+
+/**
+ * soup_uri_host_hash:
+ * @key: (type Soup.URI): a #SoupURI
+ *
+ * Hashes @key, considering only the scheme, host, and port.
+ *
+ * Return value: a hash
+ *
+ * Since: 2.26.3
+ **/
+guint
+soup_uri_host_hash (gconstpointer key)
+{
+ const SoupURI *uri = key;
+
+ g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
+
+ return GPOINTER_TO_UINT (uri->scheme) + uri->port +
+ soup_str_case_hash (uri->host);
+}
+
+/**
+ * soup_uri_host_equal:
+ * @v1: (type Soup.URI): a #SoupURI
+ * @v2: (type Soup.URI): a #SoupURI
+ *
+ * Compares @v1 and @v2, considering only the scheme, host, and port.
+ *
+ * Return value: whether or not the URIs are equal in scheme, host,
+ * and port.
+ *
+ * Since: 2.26.3
+ **/
+gboolean
+soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
+{
+ const SoupURI *one = v1;
+ const SoupURI *two = v2;
+
+ g_return_val_if_fail (one != NULL && two != NULL, one == two);
+ g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
+
+ if (one->scheme != two->scheme)
+ return FALSE;
+ if (one->port != two->port)
+ return FALSE;
+
+ return g_ascii_strcasecmp (one->host, two->host) == 0;
+}
+
+
+GType
+soup_uri_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupURI"),
+ (GBoxedCopyFunc) soup_uri_copy,
+ (GBoxedFreeFunc) soup_uri_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Copyright 1999-2002 Ximian, Inc.
+ */
+
+
+#ifndef SOUP_URI_H
+#define SOUP_URI_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-misc.h>
+
+G_BEGIN_DECLS
+
+struct _SoupURI {
+ const char *scheme;
+
+ char *user;
+ char *password;
+
+ char *host;
+ guint port;
+
+ char *path;
+ char *query;
+
+ char *fragment;
+};
+
+GType soup_uri_get_type (void);
+#define SOUP_TYPE_URI (soup_uri_get_type ())
+
+#define SOUP_URI_SCHEME_HTTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTP, "http")
+#define SOUP_URI_SCHEME_HTTPS _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTPS, "https")
+#define SOUP_URI_SCHEME_FTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FTP, "ftp")
+#define SOUP_URI_SCHEME_FILE _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FILE, "file")
+#define SOUP_URI_SCHEME_DATA _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_DATA, "data")
+extern gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
+extern gpointer _SOUP_URI_SCHEME_FTP;
+extern gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA;
+
+SoupURI *soup_uri_new_with_base (SoupURI *base,
+ const char *uri_string);
+SoupURI *soup_uri_new (const char *uri_string);
+
+char *soup_uri_to_string (SoupURI *uri,
+ gboolean just_path_and_query);
+
+SoupURI *soup_uri_copy (SoupURI *uri);
+
+gboolean soup_uri_equal (SoupURI *uri1,
+ SoupURI *uri2);
+
+void soup_uri_free (SoupURI *uri);
+
+char *soup_uri_encode (const char *part,
+ const char *escape_extra);
+char *soup_uri_decode (const char *part);
+char *soup_uri_normalize (const char *part,
+ const char *unescape_extra);
+
+gboolean soup_uri_uses_default_port (SoupURI *uri);
+
+const char *soup_uri_get_scheme (SoupURI *uri);
+void soup_uri_set_scheme (SoupURI *uri,
+ const char *scheme);
+const char *soup_uri_get_user (SoupURI *uri);
+void soup_uri_set_user (SoupURI *uri,
+ const char *user);
+const char *soup_uri_get_password (SoupURI *uri);
+void soup_uri_set_password (SoupURI *uri,
+ const char *password);
+const char *soup_uri_get_host (SoupURI *uri);
+void soup_uri_set_host (SoupURI *uri,
+ const char *host);
+guint soup_uri_get_port (SoupURI *uri);
+void soup_uri_set_port (SoupURI *uri,
+ guint port);
+const char *soup_uri_get_path (SoupURI *uri);
+void soup_uri_set_path (SoupURI *uri,
+ const char *path);
+const char *soup_uri_get_query (SoupURI *uri);
+void soup_uri_set_query (SoupURI *uri,
+ const char *query);
+void soup_uri_set_query_from_form (SoupURI *uri,
+ GHashTable *form);
+void soup_uri_set_query_from_fields (SoupURI *uri,
+ const char *first_field,
+ ...) G_GNUC_NULL_TERMINATED;
+const char *soup_uri_get_fragment (SoupURI *uri);
+void soup_uri_set_fragment (SoupURI *uri,
+ const char *fragment);
+
+SoupURI *soup_uri_copy_host (SoupURI *uri);
+guint soup_uri_host_hash (gconstpointer key);
+gboolean soup_uri_host_equal (gconstpointer v1,
+ gconstpointer v2);
+
+#define SOUP_URI_VALID_FOR_HTTP(uri) ((uri) && ((uri)->scheme == SOUP_URI_SCHEME_HTTP || (uri)->scheme == SOUP_URI_SCHEME_HTTPS) && (uri)->host && (uri)->path)
+
+G_END_DECLS
+
+#endif /*SOUP_URI_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-type-utils.c: GValue and GType-related utilities
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#include "soup-value-utils.h"
+
+#include <string.h>
+
+/**
+ * SECTION:soup-value-utils
+ * @short_description: #GValue utilities
+ *
+ * These methods are useful for manipulating #GValue<!-- -->s, and in
+ * particular, arrays and hash tables of #GValue<!-- -->s, in a
+ * slightly nicer way than the standard #GValue API.
+ *
+ * They are written for use with soup-xmlrpc, but they also work with
+ * types not used by XML-RPC.
+ **/
+
+/**
+ * SOUP_VALUE_SETV:
+ * @val: a #GValue
+ * @type: a #GType
+ * @args: #va_list pointing to a value of type @type
+ *
+ * Copies an argument of type @type from @args into @val. @val will
+ * point directly to the value in @args rather than copying it, so you
+ * must g_value_copy() it if you want it to remain valid.
+ **/
+
+/**
+ * SOUP_VALUE_GETV:
+ * @val: a #GValue
+ * @type: a #GType
+ * @args: #va_list pointing to a value of type pointer-to-@type
+ *
+ * Extracts a value of type @type from @val into @args. The return
+ * value will point to the same data as @val rather than being a copy
+ * of it.
+ **/
+
+static void
+soup_value_hash_value_free (gpointer val)
+{
+ g_value_unset (val);
+ g_free (val);
+}
+
+/**
+ * soup_value_hash_new:
+ *
+ * Creates a #GHashTable whose keys are strings and whose values
+ * are #GValue.
+ *
+ * Return value: (element-type utf8 GValue) (transfer full): a new
+ * empty #GHashTable
+ **/
+GHashTable *
+soup_value_hash_new (void)
+{
+ return g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, soup_value_hash_value_free);
+}
+
+static void
+soup_value_hash_insert_valist (GHashTable *hash, const char *first_key,
+ va_list args)
+{
+ const char *key;
+ GType type;
+ GValue value;
+
+ key = first_key;
+ while (key) {
+ type = va_arg (args, GType);
+ SOUP_VALUE_SETV (&value, type, args);
+
+ soup_value_hash_insert_value (hash, key, &value);
+ key = va_arg (args, const char *);
+ }
+}
+
+/**
+ * soup_value_hash_new_with_vals:
+ * @first_key: the key for the first value
+ * @...: the type of @first_key, followed by the value, followed
+ * by additional key/type/value triplets, terminated by %NULL
+ *
+ * Creates a #GHashTable whose keys are strings and whose values
+ * are #GValue, and initializes it with the provided data. As
+ * with soup_value_hash_insert(), the keys and values are copied
+ * rather than being inserted directly.
+ *
+ * Return value: (element-type utf8 GValue) (transfer full): a new
+ * #GHashTable, initialized with the given values
+ **/
+GHashTable *
+soup_value_hash_new_with_vals (const char *first_key, ...)
+{
+ GHashTable *hash = soup_value_hash_new ();
+ va_list args;
+
+ va_start (args, first_key);
+ soup_value_hash_insert_valist (hash, first_key, args);
+ va_end (args);
+
+ return hash;
+}
+
+/**
+ * soup_value_hash_insert_value:
+ * @hash: (element-type utf8 GValue): a value hash
+ * @key: the key
+ * @value: a value
+ *
+ * Inserts @value into @hash. (Unlike with g_hash_table_insert(), both
+ * the key and the value are copied).
+ **/
+void
+soup_value_hash_insert_value (GHashTable *hash, const char *key, GValue *value)
+{
+ GValue *copy = g_new0 (GValue, 1);
+
+ g_value_init (copy, G_VALUE_TYPE (value));
+ g_value_copy (value, copy);
+ g_hash_table_insert (hash, g_strdup (key), copy);
+}
+
+/**
+ * soup_value_hash_insert:
+ * @hash: (element-type utf8 GValue): a value hash
+ * @key: the key
+ * @type: a #GType
+ * @...: a value of type @type
+ *
+ * Inserts the provided value of type @type into @hash. (Unlike with
+ * g_hash_table_insert(), both the key and the value are copied).
+ **/
+void
+soup_value_hash_insert (GHashTable *hash, const char *key, GType type, ...)
+{
+ va_list args;
+ GValue val;
+
+ va_start (args, type);
+ SOUP_VALUE_SETV (&val, type, args);
+ va_end (args);
+ soup_value_hash_insert_value (hash, key, &val);
+}
+
+/**
+ * soup_value_hash_insert_vals:
+ * @hash: (element-type utf8 GValue): a value hash
+ * @first_key: the key for the first value
+ * @...: the type of @first_key, followed by the value, followed
+ * by additional key/type/value triplets, terminated by %NULL
+ *
+ * Inserts the given data into @hash. As with
+ * soup_value_hash_insert(), the keys and values are copied rather
+ * than being inserted directly.
+ **/
+void
+soup_value_hash_insert_vals (GHashTable *hash, const char *first_key, ...)
+{
+ va_list args;
+
+ va_start (args, first_key);
+ soup_value_hash_insert_valist (hash, first_key, args);
+ va_end (args);
+}
+
+/**
+ * soup_value_hash_lookup:
+ * @hash: (element-type utf8 GValue): a value hash
+ * @key: the key to look up
+ * @type: a #GType
+ * @...: a value of type pointer-to-@type
+ *
+ * Looks up @key in @hash and stores its value into the provided
+ * location.
+ *
+ * Return value: %TRUE if @hash contained a value with key @key and
+ * type @type, %FALSE if not.
+ **/
+gboolean
+soup_value_hash_lookup (GHashTable *hash, const char *key, GType type, ...)
+{
+ va_list args;
+ GValue *value;
+
+ value = g_hash_table_lookup (hash, key);
+ if (!value || !G_VALUE_HOLDS (value, type))
+ return FALSE;
+
+ va_start (args, type);
+ SOUP_VALUE_GETV (value, type, args);
+ va_end (args);
+
+ return TRUE;
+}
+
+/**
+ * soup_value_hash_lookup_vals:
+ * @hash: (element-type utf8 GValue): a value hash
+ * @first_key: the first key to look up
+ * @...: the type of @first_key, a pointer to that type, and
+ * then additional key/type/pointer triplets, terminated
+ * by %NULL.
+ *
+ * Looks up a number of keys in @hash and returns their values.
+ *
+ * Return value: %TRUE if all of the keys were found, %FALSE
+ * if any were missing; note that you will generally need to
+ * initialize each destination variable to a reasonable default
+ * value, since there is no way to tell which keys were found
+ * and which were not.
+ **/
+gboolean
+soup_value_hash_lookup_vals (GHashTable *hash, const char *first_key, ...)
+{
+ va_list args;
+ GValue *value;
+ const char *key;
+ GType type;
+ gboolean found_all = TRUE;
+
+ va_start (args, first_key);
+ key = first_key;
+ while (key) {
+ type = va_arg (args, GType);
+
+ value = g_hash_table_lookup (hash, key);
+ if (!value || !G_VALUE_HOLDS (value, type)) {
+ found_all = FALSE;
+ /* skip a pointer */
+ va_arg (args, gpointer);
+ } else
+ SOUP_VALUE_GETV (value, type, args);
+
+ key = va_arg (args, const char *);
+ }
+ va_end (args);
+
+ return found_all;
+}
+
+
+/**
+ * soup_value_array_from_args:
+ * @args: arguments to create a #GValueArray from
+ *
+ * Creates a #GValueArray from the provided arguments, which must
+ * consist of pairs of a #GType and a value of that type, terminated
+ * by %G_TYPE_INVALID. (The array will contain copies of the provided
+ * data rather than pointing to the passed-in data directly.)
+ *
+ * Return value: a new #GValueArray, or %NULL if an error occurred.
+ **/
+GValueArray *
+soup_value_array_from_args (va_list args)
+{
+ GValueArray *array;
+ GType type;
+ GValue val;
+
+ array = g_value_array_new (1);
+ while ((type = va_arg (args, GType)) != G_TYPE_INVALID) {
+ SOUP_VALUE_SETV (&val, type, args);
+ g_value_array_append (array, &val);
+ }
+ return array;
+}
+
+/**
+ * soup_value_array_to_args:
+ * @array: a #GValueArray
+ * @args: arguments to extract @array into
+ *
+ * Extracts a #GValueArray into the provided arguments, which must
+ * consist of pairs of a #GType and a value of pointer-to-that-type,
+ * terminated by %G_TYPE_INVALID. The returned values will point to the
+ * same memory as the values in the array.
+ *
+ * Return value: success or failure
+ **/
+gboolean
+soup_value_array_to_args (GValueArray *array, va_list args)
+{
+ GType type;
+ GValue *value;
+ int i;
+
+ for (i = 0; i < array->n_values; i++) {
+ type = va_arg (args, GType);
+ if (type == G_TYPE_INVALID)
+ return FALSE;
+ value = g_value_array_get_nth (array, i);
+ if (!G_VALUE_HOLDS (value, type))
+ return FALSE;
+ SOUP_VALUE_GETV (value, type, args);
+ }
+ return TRUE;
+}
+
+/**
+ * soup_value_array_new:
+ *
+ * Creates a new %GValueArray. (This is just a wrapper around
+ * g_value_array_new(), for naming consistency purposes.)
+ *
+ * Return value: a new %GValueArray
+ **/
+GValueArray *
+soup_value_array_new (void)
+{
+ return g_value_array_new (1);
+}
+
+static void
+soup_value_array_append_valist (GValueArray *array,
+ GType first_type, va_list args)
+{
+ GType type;
+ GValue value;
+
+ type = first_type;
+ while (type != G_TYPE_INVALID) {
+ SOUP_VALUE_SETV (&value, type, args);
+
+ g_value_array_append (array, &value);
+ type = va_arg (args, GType);
+ }
+}
+
+/**
+ * soup_value_array_new_with_vals:
+ * @first_type: the type of the first value to add
+ * @...: the first value to add, followed by other type/value
+ * pairs, terminated by %G_TYPE_INVALID
+ *
+ * Creates a new %GValueArray and copies the provided values
+ * into it.
+ *
+ * Return value: a new %GValueArray
+ **/
+GValueArray *
+soup_value_array_new_with_vals (GType first_type, ...)
+{
+ GValueArray *array = soup_value_array_new ();
+ va_list args;
+
+ va_start (args, first_type);
+ soup_value_array_append_valist (array, first_type, args);
+ va_end (args);
+
+ return array;
+}
+
+/**
+ * soup_value_array_insert:
+ * @array: a #GValueArray
+ * @index_: the index to insert at
+ * @type: a #GType
+ * @...: a value of type @type
+ *
+ * Inserts the provided value of type @type into @array as with
+ * g_value_array_insert(). (The provided data is copied rather than
+ * being inserted directly.)
+ **/
+void
+soup_value_array_insert (GValueArray *array, guint index_, GType type, ...)
+{
+ va_list args;
+ GValue val;
+
+ va_start (args, type);
+ SOUP_VALUE_SETV (&val, type, args);
+ va_end (args);
+ g_value_array_insert (array, index_, &val);
+}
+
+/**
+ * soup_value_array_append:
+ * @array: a #GValueArray
+ * @type: a #GType
+ * @...: a value of type @type
+ *
+ * Appends the provided value of type @type to @array as with
+ * g_value_array_append(). (The provided data is copied rather than
+ * being inserted directly.)
+ **/
+void
+soup_value_array_append (GValueArray *array, GType type, ...)
+{
+ va_list args;
+ GValue val;
+
+ va_start (args, type);
+ SOUP_VALUE_SETV (&val, type, args);
+ va_end (args);
+ g_value_array_append (array, &val);
+}
+
+/**
+ * soup_value_array_append_vals:
+ * @array: a #GValueArray
+ * @first_type: the type of the first value to add
+ * @...: the first value to add, followed by other type/value
+ * pairs, terminated by %G_TYPE_INVALID
+ *
+ * Appends the provided values into @array as with
+ * g_value_array_append(). (The provided data is copied rather than
+ * being inserted directly.)
+ **/
+void
+soup_value_array_append_vals (GValueArray *array, GType first_type, ...)
+{
+ va_list args;
+
+ va_start (args, first_type);
+ soup_value_array_append_valist (array, first_type, args);
+ va_end (args);
+}
+
+/**
+ * soup_value_array_get_nth:
+ * @array: a #GValueArray
+ * @index_: the index to look up
+ * @type: a #GType
+ * @...: a value of type pointer-to-@type
+ *
+ * Gets the @index_ element of @array and stores its value into the
+ * provided location.
+ *
+ * Return value: %TRUE if @array contained a value with index @index_
+ * and type @type, %FALSE if not.
+ **/
+gboolean
+soup_value_array_get_nth (GValueArray *array, guint index_, GType type, ...)
+{
+ GValue *value;
+ va_list args;
+
+ value = g_value_array_get_nth (array, index_);
+ if (!value || !G_VALUE_HOLDS (value, type))
+ return FALSE;
+
+ va_start (args, type);
+ SOUP_VALUE_GETV (value, type, args);
+ va_end (args);
+ return TRUE;
+}
+
+
+static GByteArray *
+soup_byte_array_copy (GByteArray *ba)
+{
+ GByteArray *copy;
+
+ copy = g_byte_array_sized_new (ba->len);
+ g_byte_array_append (copy, ba->data, ba->len);
+ return copy;
+}
+
+static void
+soup_byte_array_free (GByteArray *ba)
+{
+ g_byte_array_free (ba, TRUE);
+}
+
+/**
+ * SOUP_TYPE_BYTE_ARRAY:
+ *
+ * glib does not define a #GType for #GByteArray, so libsoup
+ * defines this one itself.
+ **/
+GType
+soup_byte_array_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("SoupByteArray"),
+ (GBoxedCopyFunc) soup_byte_array_copy,
+ (GBoxedFreeFunc) soup_byte_array_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_VALUE_UTILS_H
+#define SOUP_VALUE_UTILS_H 1
+
+#include <libsoup/soup-types.h>
+#include <gobject/gvaluecollector.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_VALUE_SETV(val, type, args) \
+G_STMT_START { \
+ char *error = NULL; \
+ \
+ memset (val, 0, sizeof (GValue)); \
+ g_value_init (val, type); \
+ G_VALUE_COLLECT (val, args, G_VALUE_NOCOPY_CONTENTS, &error); \
+ if (error) \
+ g_free (error); \
+} G_STMT_END
+
+#define SOUP_VALUE_GETV(val, type, args) \
+G_STMT_START { \
+ char *error = NULL; \
+ \
+ G_VALUE_LCOPY (val, args, G_VALUE_NOCOPY_CONTENTS, &error); \
+ if (error) \
+ g_free (error); \
+} G_STMT_END
+
+GHashTable *soup_value_hash_new (void);
+GHashTable *soup_value_hash_new_with_vals (const char *first_key,
+ ...) G_GNUC_NULL_TERMINATED;
+void soup_value_hash_insert_value (GHashTable *hash,
+ const char *key,
+ GValue *value);
+void soup_value_hash_insert (GHashTable *hash,
+ const char *key,
+ GType type,
+ ...);
+void soup_value_hash_insert_vals (GHashTable *hash,
+ const char *first_key,
+ ...) G_GNUC_NULL_TERMINATED;
+gboolean soup_value_hash_lookup (GHashTable *hash,
+ const char *key,
+ GType type,
+ ...);
+gboolean soup_value_hash_lookup_vals (GHashTable *hash,
+ const char *first_key,
+ ...) G_GNUC_NULL_TERMINATED;
+
+GValueArray *soup_value_array_from_args (va_list args);
+gboolean soup_value_array_to_args (GValueArray *array,
+ va_list args);
+
+GValueArray *soup_value_array_new (void);
+GValueArray *soup_value_array_new_with_vals (GType first_type,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void soup_value_array_insert (GValueArray *array,
+ guint index_,
+ GType type,
+ ...);
+void soup_value_array_append (GValueArray *array,
+ GType type,
+ ...);
+void soup_value_array_append_vals (GValueArray *array,
+ GType first_type,
+ ...) G_GNUC_NULL_TERMINATED;
+gboolean soup_value_array_get_nth (GValueArray *array,
+ guint index_,
+ GType type,
+ ...);
+
+
+GType soup_byte_array_get_type (void);
+#define SOUP_TYPE_BYTE_ARRAY (soup_byte_array_get_type ())
+
+G_END_DECLS
+
+#endif /* SOUP_VALUE_UTILS_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-xmlrpc.c: XML-RPC parser/generator
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+
+#include <libxml/tree.h>
+
+#include "soup-xmlrpc.h"
+#include "soup-value-utils.h"
+#include "soup-date.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-session.h"
+
+/**
+ * SECTION:soup-xmlrpc
+ * @short_description: XML-RPC support
+ *
+ **/
+
+static xmlNode *find_real_node (xmlNode *node);
+
+static gboolean insert_value (xmlNode *parent, GValue *value);
+
+static void
+insert_member (gpointer name, gpointer value, gpointer data)
+{
+ xmlNode *member, **struct_node = data;
+
+ if (!*struct_node)
+ return;
+
+ member = xmlNewChild (*struct_node, NULL,
+ (const xmlChar *)"member", NULL);
+ xmlNewTextChild (member, NULL,
+ (const xmlChar *)"name", (const xmlChar *)name);
+ if (!insert_value (member, value)) {
+ xmlFreeNode (*struct_node);
+ *struct_node = NULL;
+ }
+}
+
+static gboolean
+insert_value (xmlNode *parent, GValue *value)
+{
+ GType type = G_VALUE_TYPE (value);
+ xmlNode *xvalue;
+ char buf[128];
+
+ xvalue = xmlNewChild (parent, NULL, (const xmlChar *)"value", NULL);
+
+ if (type == G_TYPE_INT) {
+ snprintf (buf, sizeof (buf), "%d", g_value_get_int (value));
+ xmlNewChild (xvalue, NULL,
+ (const xmlChar *)"int",
+ (const xmlChar *)buf);
+ } else if (type == G_TYPE_BOOLEAN) {
+ snprintf (buf, sizeof (buf), "%d", g_value_get_boolean (value));
+ xmlNewChild (xvalue, NULL,
+ (const xmlChar *)"boolean",
+ (const xmlChar *)buf);
+ } else if (type == G_TYPE_STRING) {
+ xmlNewTextChild (xvalue, NULL,
+ (const xmlChar *)"string",
+ (const xmlChar *)g_value_get_string (value));
+ } else if (type == G_TYPE_DOUBLE) {
+ g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value));
+ xmlNewChild (xvalue, NULL,
+ (const xmlChar *)"double",
+ (const xmlChar *)buf);
+ } else if (type == SOUP_TYPE_DATE) {
+ SoupDate *date = g_value_get_boxed (value);
+ char *timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
+ xmlNewChild (xvalue, NULL,
+ (const xmlChar *)"dateTime.iso8601",
+ (const xmlChar *)timestamp);
+ g_free (timestamp);
+ } else if (type == SOUP_TYPE_BYTE_ARRAY) {
+ GByteArray *ba = g_value_get_boxed (value);
+ char *encoded;
+
+ encoded = g_base64_encode (ba->data, ba->len);
+ xmlNewChild (xvalue, NULL,
+ (const xmlChar *)"base64",
+ (const xmlChar *)encoded);
+ g_free (encoded);
+ } else if (type == G_TYPE_HASH_TABLE) {
+ GHashTable *hash = g_value_get_boxed (value);
+ xmlNode *struct_node;
+
+ struct_node = xmlNewChild (xvalue, NULL,
+ (const xmlChar *)"struct", NULL);
+ g_hash_table_foreach (hash, insert_member, &struct_node);
+ if (!struct_node)
+ return FALSE;
+ } else if (type == G_TYPE_VALUE_ARRAY) {
+ GValueArray *va = g_value_get_boxed (value);
+ xmlNode *node;
+ int i;
+
+ node = xmlNewChild (xvalue, NULL,
+ (const xmlChar *)"array", NULL);
+ node = xmlNewChild (node, NULL,
+ (const xmlChar *)"data", NULL);
+ for (i = 0; i < va->n_values; i++) {
+ if (!insert_value (node, &va->values[i]))
+ return FALSE;
+ }
+ } else
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * soup_xmlrpc_build_method_call:
+ * @method_name: the name of the XML-RPC method
+ * @params: (array length=n_params): arguments to @method
+ * @n_params: length of @params
+ *
+ * This creates an XML-RPC methodCall and returns it as a string.
+ * This is the low-level method that soup_xmlrpc_request_new() and
+ * soup_xmlrpc_call() are built on.
+ *
+ * @params is an array of #GValue representing the parameters to
+ * @method. (It is *not* a #GValueArray, although if you have a
+ * #GValueArray, you can just pass its %values and %n_values fields.)
+ *
+ * The correspondence between glib types and XML-RPC types is:
+ *
+ * int: #int (%G_TYPE_INT)
+ * boolean: #gboolean (%G_TYPE_BOOLEAN)
+ * string: #char* (%G_TYPE_STRING)
+ * double: #double (%G_TYPE_DOUBLE)
+ * datetime.iso8601: #SoupDate (%SOUP_TYPE_DATE)
+ * base64: #GByteArray (%SOUP_TYPE_BYTE_ARRAY)
+ * struct: #GHashTable (%G_TYPE_HASH_TABLE)
+ * array: #GValueArray (%G_TYPE_VALUE_ARRAY)
+ *
+ * For structs, use a #GHashTable that maps strings to #GValue;
+ * soup_value_hash_new() and related methods can help with this.
+ *
+ * Return value: the text of the methodCall, or %NULL on error
+ **/
+char *
+soup_xmlrpc_build_method_call (const char *method_name,
+ GValue *params, int n_params)
+{
+ xmlDoc *doc;
+ xmlNode *node, *param;
+ xmlChar *xmlbody;
+ int i, len;
+ char *body;
+
+ doc = xmlNewDoc ((const xmlChar *)"1.0");
+ doc->standalone = FALSE;
+ doc->encoding = xmlCharStrdup ("UTF-8");
+
+ node = xmlNewDocNode (doc, NULL, (const xmlChar *)"methodCall", NULL);
+ xmlDocSetRootElement (doc, node);
+ xmlNewChild (node, NULL, (const xmlChar *)"methodName",
+ (const xmlChar *)method_name);
+
+ node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
+ for (i = 0; i < n_params; i++) {
+ param = xmlNewChild (node, NULL,
+ (const xmlChar *)"param", NULL);
+ if (!insert_value (param, ¶ms[i])) {
+ xmlFreeDoc (doc);
+ return NULL;
+ }
+ }
+
+ xmlDocDumpMemory (doc, &xmlbody, &len);
+ body = g_strndup ((char *)xmlbody, len);
+ xmlFree (xmlbody);
+ xmlFreeDoc (doc);
+ return body;
+}
+
+static SoupMessage *
+soup_xmlrpc_request_newv (const char *uri, const char *method_name, va_list args)
+{
+ SoupMessage *msg;
+ GValueArray *params;
+ char *body;
+
+ params = soup_value_array_from_args (args);
+ if (!params)
+ return NULL;
+
+ body = soup_xmlrpc_build_method_call (method_name, params->values,
+ params->n_values);
+ g_value_array_free (params);
+ if (!body)
+ return NULL;
+
+ msg = soup_message_new ("POST", uri);
+ soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
+ body, strlen (body));
+ return msg;
+}
+
+/**
+ * soup_xmlrpc_request_new:
+ * @uri: URI of the XML-RPC service
+ * @method_name: the name of the XML-RPC method to invoke at @uri
+ * @...: parameters for @method
+ *
+ * Creates an XML-RPC methodCall and returns a #SoupMessage, ready
+ * to send, for that method call.
+ *
+ * The parameters are passed as type/value pairs; ie, first a #GType,
+ * and then a value of the appropriate type, finally terminated by
+ * %G_TYPE_INVALID.
+ *
+ * Return value: (transfer full): a #SoupMessage encoding the
+ * indicated XML-RPC request.
+ **/
+SoupMessage *
+soup_xmlrpc_request_new (const char *uri, const char *method_name, ...)
+{
+ SoupMessage *msg;
+ va_list args;
+
+ va_start (args, method_name);
+ msg = soup_xmlrpc_request_newv (uri, method_name, args);
+ va_end (args);
+ return msg;
+}
+
+/**
+ * soup_xmlrpc_build_method_response:
+ * @value: the return value
+ *
+ * This creates a (successful) XML-RPC methodResponse and returns it
+ * as a string. To create a fault response, use
+ * soup_xmlrpc_build_fault().
+ *
+ * The glib type to XML-RPC type mapping is as with
+ * soup_xmlrpc_build_method_call(), qv.
+ *
+ * Return value: the text of the methodResponse, or %NULL on error
+ **/
+char *
+soup_xmlrpc_build_method_response (GValue *value)
+{
+ xmlDoc *doc;
+ xmlNode *node;
+ xmlChar *xmlbody;
+ char *body;
+ int len;
+
+ doc = xmlNewDoc ((const xmlChar *)"1.0");
+ doc->standalone = FALSE;
+ doc->encoding = xmlCharStrdup ("UTF-8");
+
+ node = xmlNewDocNode (doc, NULL,
+ (const xmlChar *)"methodResponse", NULL);
+ xmlDocSetRootElement (doc, node);
+
+ node = xmlNewChild (node, NULL, (const xmlChar *)"params", NULL);
+ node = xmlNewChild (node, NULL, (const xmlChar *)"param", NULL);
+ if (!insert_value (node, value)) {
+ xmlFreeDoc (doc);
+ return NULL;
+ }
+
+ xmlDocDumpMemory (doc, &xmlbody, &len);
+ body = g_strndup ((char *)xmlbody, len);
+ xmlFree (xmlbody);
+ xmlFreeDoc (doc);
+ return body;
+}
+
+static char *
+soup_xmlrpc_build_faultv (int fault_code, const char *fault_format, va_list args)
+{
+ xmlDoc *doc;
+ xmlNode *node, *member;
+ GValue value;
+ xmlChar *xmlbody;
+ char *fault_string, *body;
+ int len;
+
+ fault_string = g_strdup_vprintf (fault_format, args);
+
+ doc = xmlNewDoc ((const xmlChar *)"1.0");
+ doc->standalone = FALSE;
+ doc->encoding = xmlCharStrdup ("UTF-8");
+
+ node = xmlNewDocNode (doc, NULL,
+ (const xmlChar *)"methodResponse", NULL);
+ xmlDocSetRootElement (doc, node);
+ node = xmlNewChild (node, NULL, (const xmlChar *)"fault", NULL);
+ node = xmlNewChild (node, NULL, (const xmlChar *)"value", NULL);
+ node = xmlNewChild (node, NULL, (const xmlChar *)"struct", NULL);
+
+ memset (&value, 0, sizeof (value));
+
+ member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
+ xmlNewChild (member, NULL,
+ (const xmlChar *)"name", (const xmlChar *)"faultCode");
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, fault_code);
+ insert_value (member, &value);
+ g_value_unset (&value);
+
+ member = xmlNewChild (node, NULL, (const xmlChar *)"member", NULL);
+ xmlNewChild (member, NULL,
+ (const xmlChar *)"name", (const xmlChar *)"faultString");
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_take_string (&value, fault_string);
+ insert_value (member, &value);
+ g_value_unset (&value);
+
+ xmlDocDumpMemory (doc, &xmlbody, &len);
+ body = g_strndup ((char *)xmlbody, len);
+ xmlFree (xmlbody);
+ xmlFreeDoc (doc);
+
+ return body;
+}
+
+/**
+ * soup_xmlrpc_build_fault:
+ * @fault_code: the fault code
+ * @fault_format: a printf()-style format string
+ * @...: the parameters to @fault_format
+ *
+ * This creates an XML-RPC fault response and returns it as a string.
+ * (To create a successful response, use
+ * soup_xmlrpc_build_method_response().)
+ *
+ * Return value: the text of the fault
+ **/
+char *
+soup_xmlrpc_build_fault (int fault_code, const char *fault_format, ...)
+{
+ va_list args;
+ char *body;
+
+ va_start (args, fault_format);
+ body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
+ va_end (args);
+ return body;
+}
+
+/**
+ * soup_xmlrpc_set_response:
+ * @msg: an XML-RPC request
+ * @type: the type of the response value
+ * @...: the response value
+ *
+ * Sets the status code and response body of @msg to indicate a
+ * successful XML-RPC call, with a return value given by @type and the
+ * following varargs argument, of the type indicated by @type.
+ **/
+void
+soup_xmlrpc_set_response (SoupMessage *msg, GType type, ...)
+{
+ va_list args;
+ GValue value;
+ char *body;
+
+ va_start (args, type);
+ SOUP_VALUE_SETV (&value, type, args);
+ va_end (args);
+
+ body = soup_xmlrpc_build_method_response (&value);
+ g_value_unset (&value);
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
+ body, strlen (body));
+}
+
+/**
+ * soup_xmlrpc_set_fault:
+ * @msg: an XML-RPC request
+ * @fault_code: the fault code
+ * @fault_format: a printf()-style format string
+ * @...: the parameters to @fault_format
+ *
+ * Sets the status code and response body of @msg to indicate an
+ * unsuccessful XML-RPC call, with the error described by @fault_code
+ * and @fault_format.
+ **/
+void
+soup_xmlrpc_set_fault (SoupMessage *msg, int fault_code,
+ const char *fault_format, ...)
+{
+ va_list args;
+ char *body;
+
+ va_start (args, fault_format);
+ body = soup_xmlrpc_build_faultv (fault_code, fault_format, args);
+ va_end (args);
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/xml", SOUP_MEMORY_TAKE,
+ body, strlen (body));
+}
+
+
+
+static gboolean
+parse_value (xmlNode *xmlvalue, GValue *value)
+{
+ xmlNode *typenode;
+ const char *typename;
+ xmlChar *content;
+
+ memset (value, 0, sizeof (GValue));
+
+ typenode = find_real_node (xmlvalue->children);
+ if (!typenode) {
+ /* If no type node, it's a string */
+ content = xmlNodeGetContent (typenode);
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, (char *)content);
+ xmlFree (content);
+ return TRUE;
+ }
+
+ typename = (const char *)typenode->name;
+
+ if (!strcmp (typename, "i4") || !strcmp (typename, "int")) {
+ content = xmlNodeGetContent (typenode);
+ g_value_init (value, G_TYPE_INT);
+ g_value_set_int (value, atoi ((char *)content));
+ xmlFree (content);
+ } else if (!strcmp (typename, "boolean")) {
+ content = xmlNodeGetContent (typenode);
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, atoi ((char *)content));
+ xmlFree (content);
+ } else if (!strcmp (typename, "string")) {
+ content = xmlNodeGetContent (typenode);
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, (char *)content);
+ xmlFree (content);
+ } else if (!strcmp (typename, "double")) {
+ content = xmlNodeGetContent (typenode);
+ g_value_init (value, G_TYPE_DOUBLE);
+ g_value_set_double (value, g_ascii_strtod ((char *)content, NULL));
+ xmlFree (content);
+ } else if (!strcmp (typename, "dateTime.iso8601")) {
+ content = xmlNodeGetContent (typenode);
+ g_value_init (value, SOUP_TYPE_DATE);
+ g_value_take_boxed (value, soup_date_new_from_string ((char *)content));
+ xmlFree (content);
+ } else if (!strcmp (typename, "base64")) {
+ GByteArray *ba;
+ guchar *decoded;
+ gsize len;
+
+ content = xmlNodeGetContent (typenode);
+ decoded = g_base64_decode ((char *)content, &len);
+ ba = g_byte_array_sized_new (len);
+ g_byte_array_append (ba, decoded, len);
+ g_free (decoded);
+ xmlFree (content);
+ g_value_init (value, SOUP_TYPE_BYTE_ARRAY);
+ g_value_take_boxed (value, ba);
+ } else if (!strcmp (typename, "struct")) {
+ xmlNode *member, *child, *mname, *mxval;
+ GHashTable *hash;
+ GValue mgval;
+
+ hash = soup_value_hash_new ();
+ for (member = find_real_node (typenode->children);
+ member;
+ member = find_real_node (member->next)) {
+ if (strcmp ((const char *)member->name, "member") != 0) {
+ g_hash_table_destroy (hash);
+ return FALSE;
+ }
+ mname = mxval = NULL;
+ memset (&mgval, 0, sizeof (mgval));
+
+ for (child = find_real_node (member->children);
+ child;
+ child = find_real_node (child->next)) {
+ if (!strcmp ((const char *)child->name, "name"))
+ mname = child;
+ else if (!strcmp ((const char *)child->name, "value"))
+ mxval = child;
+ else
+ break;
+ }
+
+ if (!mname || !mxval || !parse_value (mxval, &mgval)) {
+ g_hash_table_destroy (hash);
+ return FALSE;
+ }
+
+ content = xmlNodeGetContent (mname);
+ soup_value_hash_insert_value (hash, (char *)content, &mgval);
+ xmlFree (content);
+ g_value_unset (&mgval);
+ }
+ g_value_init (value, G_TYPE_HASH_TABLE);
+ g_value_take_boxed (value, hash);
+ } else if (!strcmp (typename, "array")) {
+ xmlNode *data, *xval;
+ GValueArray *array;
+ GValue gval;
+
+ data = find_real_node (typenode->children);
+ if (!data || strcmp ((const char *)data->name, "data") != 0)
+ return FALSE;
+
+ array = g_value_array_new (1);
+ for (xval = find_real_node (data->children);
+ xval;
+ xval = find_real_node (xval->next)) {
+ memset (&gval, 0, sizeof (gval));
+ if (strcmp ((const char *)xval->name, "value") != 0 ||
+ !parse_value (xval, &gval)) {
+ g_value_array_free (array);
+ return FALSE;
+ }
+
+ g_value_array_append (array, &gval);
+ g_value_unset (&gval);
+ }
+ g_value_init (value, G_TYPE_VALUE_ARRAY);
+ g_value_take_boxed (value, array);
+ } else
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * soup_xmlrpc_parse_method_call:
+ * @method_call: the XML-RPC methodCall string
+ * @length: the length of @method_call, or -1 if it is NUL-terminated
+ * @method_name: (out): on return, the methodName from @method_call
+ * @params: (out): on return, the parameters from @method_call
+ *
+ * Parses @method_call to get the name and parameters, and returns the
+ * parameter values in a #GValueArray; see also
+ * soup_xmlrpc_extract_method_call(), which is more convenient if you
+ * know in advance what the types of the parameters will be.
+ *
+ * Return value: success or failure.
+ **/
+gboolean
+soup_xmlrpc_parse_method_call (const char *method_call, int length,
+ char **method_name, GValueArray **params)
+{
+ xmlDoc *doc;
+ xmlNode *node, *param, *xval;
+ xmlChar *xmlMethodName = NULL;
+ gboolean success = FALSE;
+ GValue value;
+
+ doc = xmlParseMemory (method_call,
+ length == -1 ? strlen (method_call) : length);
+ if (!doc)
+ return FALSE;
+
+ node = xmlDocGetRootElement (doc);
+ if (!node || strcmp ((const char *)node->name, "methodCall") != 0)
+ goto fail;
+
+ node = find_real_node (node->children);
+ if (!node || strcmp ((const char *)node->name, "methodName") != 0)
+ goto fail;
+ xmlMethodName = xmlNodeGetContent (node);
+
+ node = find_real_node (node->next);
+ if (!node || strcmp ((const char *)node->name, "params") != 0)
+ goto fail;
+
+ *params = g_value_array_new (1);
+ param = find_real_node (node->children);
+ while (param && !strcmp ((const char *)param->name, "param")) {
+ xval = find_real_node (param->children);
+ if (!xval || strcmp ((const char *)xval->name, "value") != 0 ||
+ !parse_value (xval, &value)) {
+ g_value_array_free (*params);
+ goto fail;
+ }
+ g_value_array_append (*params, &value);
+ g_value_unset (&value);
+
+ param = find_real_node (param->next);
+ }
+
+ success = TRUE;
+ *method_name = g_strdup ((char *)xmlMethodName);
+
+fail:
+ xmlFreeDoc (doc);
+ if (xmlMethodName)
+ xmlFree (xmlMethodName);
+ return success;
+}
+
+/**
+ * soup_xmlrpc_extract_method_call:
+ * @method_call: the XML-RPC methodCall string
+ * @length: the length of @method_call, or -1 if it is NUL-terminated
+ * @method_name: (out): on return, the methodName from @method_call
+ * @...: return types and locations for parameters
+ *
+ * Parses @method_call to get the name and parameters, and puts
+ * the parameters into variables of the appropriate types.
+ *
+ * The parameters are handled similarly to
+ * @soup_xmlrpc_build_method_call, with pairs of types and values,
+ * terminated by %G_TYPE_INVALID, except that values are pointers to
+ * variables of the indicated type, rather than values of the type.
+ *
+ * See also soup_xmlrpc_parse_method_call(), which can be used if
+ * you don't know the types of the parameters.
+ *
+ * Return value: success or failure.
+ **/
+gboolean
+soup_xmlrpc_extract_method_call (const char *method_call, int length,
+ char **method_name, ...)
+{
+ GValueArray *params;
+ gboolean success;
+ va_list args;
+
+ if (!soup_xmlrpc_parse_method_call (method_call, length,
+ method_name, ¶ms))
+ return FALSE;
+
+ va_start (args, method_name);
+ success = soup_value_array_to_args (params, args);
+ va_end (args);
+
+ g_value_array_free (params);
+ return success;
+}
+
+/**
+ * soup_xmlrpc_parse_method_response:
+ * @method_response: the XML-RPC methodResponse string
+ * @length: the length of @method_response, or -1 if it is NUL-terminated
+ * @value: (out): on return, the return value from @method_call
+ * @error: error return value
+ *
+ * Parses @method_response and returns the return value in @value. If
+ * @method_response is a fault, @value will be unchanged, and @error
+ * will be set to an error of type %SOUP_XMLRPC_FAULT, with the error
+ * #code containing the fault code, and the error #message containing
+ * the fault string. (If @method_response cannot be parsed at all,
+ * soup_xmlrpc_parse_method_response() will return %FALSE, but @error
+ * will be unset.)
+ *
+ * Return value: %TRUE if a return value was parsed, %FALSE if the
+ * response could not be parsed, or contained a fault.
+ **/
+gboolean
+soup_xmlrpc_parse_method_response (const char *method_response, int length,
+ GValue *value, GError **error)
+{
+ xmlDoc *doc;
+ xmlNode *node;
+ gboolean success = FALSE;
+
+ doc = xmlParseMemory (method_response,
+ length == -1 ? strlen (method_response) : length);
+ if (!doc)
+ return FALSE;
+
+ node = xmlDocGetRootElement (doc);
+ if (!node || strcmp ((const char *)node->name, "methodResponse") != 0)
+ goto fail;
+
+ node = find_real_node (node->children);
+ if (!node)
+ goto fail;
+
+ if (!strcmp ((const char *)node->name, "fault") && error) {
+ int fault_code;
+ char *fault_string;
+ GValue fault_val;
+ GHashTable *fault_hash;
+
+ node = find_real_node (node->children);
+ if (!node || strcmp ((const char *)node->name, "value") != 0)
+ goto fail;
+ if (!parse_value (node, &fault_val))
+ goto fail;
+ if (!G_VALUE_HOLDS (&fault_val, G_TYPE_HASH_TABLE)) {
+ g_value_unset (&fault_val);
+ goto fail;
+ }
+ fault_hash = g_value_get_boxed (&fault_val);
+ if (!soup_value_hash_lookup (fault_hash, "faultCode",
+ G_TYPE_INT, &fault_code) ||
+ !soup_value_hash_lookup (fault_hash, "faultString",
+ G_TYPE_STRING, &fault_string)) {
+ g_value_unset (&fault_val);
+ goto fail;
+ }
+
+ g_set_error (error, SOUP_XMLRPC_FAULT,
+ fault_code, "%s", fault_string);
+ g_value_unset (&fault_val);
+ } else if (!strcmp ((const char *)node->name, "params")) {
+ node = find_real_node (node->children);
+ if (!node || strcmp ((const char *)node->name, "param") != 0)
+ goto fail;
+ node = find_real_node (node->children);
+ if (!node || strcmp ((const char *)node->name, "value") != 0)
+ goto fail;
+ if (!parse_value (node, value))
+ goto fail;
+ success = TRUE;
+ }
+
+fail:
+ xmlFreeDoc (doc);
+ return success;
+}
+
+/**
+ * soup_xmlrpc_extract_method_response:
+ * @method_response: the XML-RPC methodResponse string
+ * @length: the length of @method_response, or -1 if it is NUL-terminated
+ * @error: error return value
+ * @type: the expected type of the return value
+ * @...: location for return value
+ *
+ * Parses @method_response and extracts the return value into
+ * a variable of the correct type.
+ *
+ * If @method_response is a fault, the return value will be unset,
+ * and @error will be set to an error of type %SOUP_XMLRPC_FAULT, with
+ * the error #code containing the fault code, and the error #message
+ * containing the fault string. (If @method_response cannot be parsed
+ * at all, soup_xmlrpc_extract_method_response() will return %FALSE,
+ * but @error will be unset.)
+ *
+ * Return value: %TRUE if a return value was parsed, %FALSE if the
+ * response was of the wrong type, or contained a fault.
+ **/
+gboolean
+soup_xmlrpc_extract_method_response (const char *method_response, int length,
+ GError **error, GType type, ...)
+{
+ GValue value;
+ va_list args;
+
+ if (!soup_xmlrpc_parse_method_response (method_response, length,
+ &value, error))
+ return FALSE;
+ if (!G_VALUE_HOLDS (&value, type))
+ return FALSE;
+
+ va_start (args, type);
+ SOUP_VALUE_GETV (&value, type, args);
+ va_end (args);
+
+ return TRUE;
+}
+
+
+GQuark
+soup_xmlrpc_error_quark (void)
+{
+ static GQuark error;
+ if (!error)
+ error = g_quark_from_static_string ("soup_xmlrpc_error_quark");
+ return error;
+}
+
+GQuark
+soup_xmlrpc_fault_quark (void)
+{
+ static GQuark error;
+ if (!error)
+ error = g_quark_from_static_string ("soup_xmlrpc_fault_quark");
+ return error;
+}
+
+static xmlNode *
+find_real_node (xmlNode *node)
+{
+ while (node && (node->type == XML_COMMENT_NODE ||
+ xmlIsBlankNode (node)))
+ node = node->next;
+ return node;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#ifndef SOUP_XMLRPC_H
+#define SOUP_XMLRPC_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+/* XML-RPC client */
+char *soup_xmlrpc_build_method_call (const char *method_name,
+ GValue *params,
+ int n_params);
+SoupMessage *soup_xmlrpc_request_new (const char *uri,
+ const char *method_name,
+ ...);
+gboolean soup_xmlrpc_parse_method_response (const char *method_response,
+ int length,
+ GValue *value,
+ GError **error);
+gboolean soup_xmlrpc_extract_method_response (const char *method_response,
+ int length,
+ GError **error,
+ GType type,
+ ...);
+
+/* XML-RPC server */
+gboolean soup_xmlrpc_parse_method_call (const char *method_call,
+ int length,
+ char **method_name,
+ GValueArray **params);
+gboolean soup_xmlrpc_extract_method_call (const char *method_call,
+ int length,
+ char **method_name,
+ ...);
+char *soup_xmlrpc_build_method_response (GValue *value);
+char *soup_xmlrpc_build_fault (int fault_code,
+ const char *fault_format,
+ ...) G_GNUC_PRINTF (2, 3);
+void soup_xmlrpc_set_response (SoupMessage *msg,
+ GType type,
+ ...);
+void soup_xmlrpc_set_fault (SoupMessage *msg,
+ int fault_code,
+ const char *fault_format,
+ ...) G_GNUC_PRINTF (3, 4);
+
+
+/* Errors */
+#define SOUP_XMLRPC_ERROR soup_xmlrpc_error_quark()
+GQuark soup_xmlrpc_error_quark (void);
+
+typedef enum {
+ SOUP_XMLRPC_ERROR_ARGUMENTS,
+ SOUP_XMLRPC_ERROR_RETVAL
+} SoupXMLRPCError;
+
+#define SOUP_XMLRPC_FAULT soup_xmlrpc_fault_quark()
+GQuark soup_xmlrpc_fault_quark (void);
+
+/* From http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php.
+ * These are an extension, not part of the XML-RPC spec; you can't
+ * assume servers will use them.
+ */
+typedef enum {
+ SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED = -32700,
+ SOUP_XMLRPC_FAULT_PARSE_ERROR_UNSUPPORTED_ENCODING = -32701,
+ SOUP_XMLRPC_FAULT_PARSE_ERROR_INVALID_CHARACTER_FOR_ENCODING = -32702,
+ SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_XML_RPC = -32600,
+ SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND = -32601,
+ SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS = -32602,
+ SOUP_XMLRPC_FAULT_SERVER_ERROR_INTERNAL_XML_RPC_ERROR = -32603,
+ SOUP_XMLRPC_FAULT_APPLICATION_ERROR = -32500,
+ SOUP_XMLRPC_FAULT_SYSTEM_ERROR = -32400,
+ SOUP_XMLRPC_FAULT_TRANSPORT_ERROR = -32300
+} SoupXMLRPCFault;
+
+G_END_DECLS
+
+#endif /* SOUP_XMLRPC_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_H
+#define SOUP_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-auth.h>
+#include <libsoup/soup-auth-domain.h>
+#include <libsoup/soup-auth-domain-basic.h>
+#include <libsoup/soup-auth-domain-digest.h>
+#include <libsoup/soup-content-decoder.h>
+#include <libsoup/soup-content-sniffer.h>
+#include <libsoup/soup-cookie.h>
+#include <libsoup/soup-cookie-jar.h>
+#include <libsoup/soup-cookie-jar-text.h>
+#include <libsoup/soup-date.h>
+#include <libsoup/soup-enum-types.h>
+#include <libsoup/soup-form.h>
+#include <libsoup/soup-headers.h>
+#include <libsoup/soup-logger.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-method.h>
+#include <libsoup/soup-misc.h>
+#include <libsoup/soup-multipart.h>
+#include <libsoup/soup-password-manager.h>
+#include <libsoup/soup-proxy-resolver.h>
+#include <libsoup/soup-proxy-resolver-default.h>
+#include <libsoup/soup-proxy-uri-resolver.h>
+#include <libsoup/soup-server.h>
+#include <libsoup/soup-session-async.h>
+#include <libsoup/soup-session-feature.h>
+#include <libsoup/soup-session-sync.h>
+#include <libsoup/soup-socket.h>
+#include <libsoup/soup-status.h>
+#include <libsoup/soup-uri.h>
+#include <libsoup/soup-value-utils.h>
+#include <libsoup/soup-xmlrpc.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SOUP_H */
--- /dev/null
+dnl -*- mode: autoconf -*-
+dnl Copyright 2009 Johan Dahlin
+dnl
+dnl This file is free software; the author(s) gives unlimited
+dnl permission to copy and/or distribute it, with or without
+dnl modifications, as long as this notice is preserved.
+dnl
+
+# serial 1
+
+m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL],
+[
+ AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first
+ AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first
+ AC_BEFORE([LT_INIT],[$0])dnl setup libtool first
+
+ dnl enable/disable introspection
+ m4_if([$2], [require],
+ [dnl
+ enable_introspection=yes
+ ],[dnl
+ AC_ARG_ENABLE(introspection,
+ AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]],
+ [Enable introspection for this build]),,
+ [enable_introspection=auto])
+ ])dnl
+
+ AC_MSG_CHECKING([for gobject-introspection])
+
+ dnl presence/version checking
+ AS_CASE([$enable_introspection],
+ [no], [dnl
+ found_introspection="no (disabled, use --enable-introspection to enable)"
+ ],dnl
+ [yes],[dnl
+ PKG_CHECK_EXISTS([gobject-introspection-1.0],,
+ AC_MSG_ERROR([gobject-introspection-1.0 is not installed]))
+ PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1],
+ found_introspection=yes,
+ AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME]))
+ ],dnl
+ [auto],[dnl
+ PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no)
+ ],dnl
+ [dnl
+ AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@])
+ ])dnl
+
+ AC_MSG_RESULT([$found_introspection])
+
+ INTROSPECTION_SCANNER=
+ INTROSPECTION_COMPILER=
+ INTROSPECTION_GENERATE=
+ INTROSPECTION_GIRDIR=
+ INTROSPECTION_TYPELIBDIR=
+ if test "x$found_introspection" = "xyes"; then
+ INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
+ INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
+ INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
+ INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
+ INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
+ INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0`
+ INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0`
+ INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection
+ fi
+ AC_SUBST(INTROSPECTION_SCANNER)
+ AC_SUBST(INTROSPECTION_COMPILER)
+ AC_SUBST(INTROSPECTION_GENERATE)
+ AC_SUBST(INTROSPECTION_GIRDIR)
+ AC_SUBST(INTROSPECTION_TYPELIBDIR)
+ AC_SUBST(INTROSPECTION_CFLAGS)
+ AC_SUBST(INTROSPECTION_LIBS)
+ AC_SUBST(INTROSPECTION_MAKEFILE)
+
+ AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes")
+])
+
+
+dnl Usage:
+dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version])
+
+AC_DEFUN([GOBJECT_INTROSPECTION_CHECK],
+[
+ _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1])
+])
+
+dnl Usage:
+dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version])
+
+
+AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE],
+[
+ _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require])
+])
--- /dev/null
+dnl Autoconf macros for libgcrypt
+dnl Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+dnl
+dnl This file is free software; as a special exception the author gives
+dnl unlimited permission to copy and/or distribute it, with or without
+dnl modifications, as long as this notice is preserved.
+dnl
+dnl This file is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
+dnl AM_PATH_LIBGCRYPT([MINIMUM-VERSION,
+dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgcrypt and define LIBGCRYPT_CFLAGS and LIBGCRYPT_LIBS.
+dnl MINIMUN-VERSION is a string with the version number optionalliy prefixed
+dnl with the API version to also check the API compatibility. Example:
+dnl a MINIMUN-VERSION of 1:1.2.5 won't pass the test unless the installed
+dnl version of libgcrypt is at least 1.2.5 *and* the API number is 1. Using
+dnl this features allows to prevent build against newer versions of libgcrypt
+dnl with a changed API.
+dnl
+AC_DEFUN([AM_PATH_LIBGCRYPT],
+[ AC_ARG_WITH(libgcrypt-prefix,
+ AC_HELP_STRING([--with-libgcrypt-prefix=PFX],
+ [prefix where LIBGCRYPT is installed (optional)]),
+ libgcrypt_config_prefix="$withval", libgcrypt_config_prefix="")
+ if test x$libgcrypt_config_prefix != x ; then
+ if test x${LIBGCRYPT_CONFIG+set} != xset ; then
+ LIBGCRYPT_CONFIG=$libgcrypt_config_prefix/bin/libgcrypt-config
+ fi
+ fi
+
+ AC_PATH_PROG(LIBGCRYPT_CONFIG, libgcrypt-config, no)
+ tmp=ifelse([$1], ,1:1.2.0,$1)
+ if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+ req_libgcrypt_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'`
+ min_libgcrypt_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+ else
+ req_libgcrypt_api=0
+ min_libgcrypt_version="$tmp"
+ fi
+
+ AC_MSG_CHECKING(for LIBGCRYPT - version >= $min_libgcrypt_version)
+ ok=no
+ if test "$LIBGCRYPT_CONFIG" != "no" ; then
+ req_major=`echo $min_libgcrypt_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+ req_minor=`echo $min_libgcrypt_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+ req_micro=`echo $min_libgcrypt_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+ libgcrypt_config_version=`$LIBGCRYPT_CONFIG --version`
+ major=`echo $libgcrypt_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+ minor=`echo $libgcrypt_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+ micro=`echo $libgcrypt_config_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+ if test "$major" -gt "$req_major"; then
+ ok=yes
+ else
+ if test "$major" -eq "$req_major"; then
+ if test "$minor" -gt "$req_minor"; then
+ ok=yes
+ else
+ if test "$minor" -eq "$req_minor"; then
+ if test "$micro" -ge "$req_micro"; then
+ ok=yes
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ AC_MSG_RESULT([yes ($libgcrypt_config_version)])
+ else
+ AC_MSG_RESULT(no)
+ fi
+ if test $ok = yes; then
+ # If we have a recent libgcrypt, we should also check that the
+ # API is compatible
+ if test "$req_libgcrypt_api" -gt 0 ; then
+ tmp=`$LIBGCRYPT_CONFIG --api-version 2>/dev/null || echo 0`
+ if test "$tmp" -gt 0 ; then
+ AC_MSG_CHECKING([LIBGCRYPT API version])
+ if test "$req_libgcrypt_api" -eq "$tmp" ; then
+ AC_MSG_RESULT([okay])
+ else
+ ok=no
+ AC_MSG_RESULT([does not match. want=$req_libgcrypt_api got=$tmp])
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ LIBGCRYPT_CFLAGS=`$LIBGCRYPT_CONFIG --cflags`
+ LIBGCRYPT_LIBS=`$LIBGCRYPT_CONFIG --libs`
+ ifelse([$2], , :, [$2])
+ else
+ LIBGCRYPT_CFLAGS=""
+ LIBGCRYPT_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(LIBGCRYPT_CFLAGS)
+ AC_SUBST(LIBGCRYPT_LIBS)
+])
--- /dev/null
+INCLUDES = \
+ -I$(top_srcdir) \
+ -DSRCDIR=\""$(srcdir)"\" \
+ -DLIBSOUP_DISABLE_DEPRECATED \
+ $(SOUP_MAINTAINER_FLAGS) \
+ $(XML_CFLAGS) \
+ $(GLIB_CFLAGS)
+
+LIBS = \
+ $(top_builddir)/libsoup/libsoup-2.4.la \
+ $(LIBGNUTLS_LIBS) \
+ $(GLIB_LIBS)
+
+noinst_PROGRAMS = \
+ chunk-test \
+ coding-test \
+ context-test \
+ continue-test \
+ cookies-test \
+ date \
+ dns \
+ forms-test \
+ get \
+ getbug \
+ header-parsing \
+ misc-test \
+ ntlm-test \
+ redirect-test \
+ requester-test \
+ simple-httpd \
+ simple-proxy \
+ sniffing-test \
+ streaming-test \
+ timeout-test \
+ uri-parsing \
+ $(CURL_TESTS) \
+ $(APACHE_TESTS) \
+ $(XMLRPC_TESTS)
+
+TEST_SRCS = test-utils.c test-utils.h
+
+auth_test_SOURCES = auth-test.c $(TEST_SRCS)
+chunk_test_SOURCES = chunk-test.c $(TEST_SRCS)
+coding_test_SOURCES = coding-test.c $(TEST_SRCS)
+context_test_SOURCES = context-test.c $(TEST_SRCS)
+continue_test_SOURCES = continue-test.c $(TEST_SRCS)
+cookies_test_SOURCES = cookies-test.c $(TEST_SRCS)
+date_SOURCES = date.c $(TEST_SRCS)
+dns_SOURCES = dns.c
+forms_test_SOURCES = forms-test.c $(TEST_SRCS)
+get_SOURCES = get.c
+if BUILD_LIBSOUP_GNOME
+get_LDADD = $(top_builddir)/libsoup/libsoup-gnome-2.4.la
+endif
+getbug_SOURCES = getbug.c
+header_parsing_SOURCES = header-parsing.c $(TEST_SRCS)
+misc_test_SOURCES = misc-test.c $(TEST_SRCS)
+ntlm_test_SOURCES = ntlm-test.c $(TEST_SRCS)
+proxy_test_SOURCES = proxy-test.c $(TEST_SRCS)
+pull_api_SOURCES = pull-api.c $(TEST_SRCS)
+range_test_SOURCES = range-test.c $(TEST_SRCS)
+redirect_test_SOURCES = redirect-test.c $(TEST_SRCS)
+requester_test_SOURCES = requester-test.c $(TEST_SRCS)
+server_auth_test_SOURCES = server-auth-test.c $(TEST_SRCS)
+simple_httpd_SOURCES = simple-httpd.c
+simple_proxy_SOURCES = simple-proxy.c
+sniffing_test_SOURCES = sniffing-test.c $(TEST_SRCS)
+streaming_test_SOURCES = streaming-test.c $(TEST_SRCS)
+timeout_test_SOURCES = timeout-test.c $(TEST_SRCS)
+uri_parsing_SOURCES = uri-parsing.c $(TEST_SRCS)
+xmlrpc_test_SOURCES = xmlrpc-test.c $(TEST_SRCS)
+xmlrpc_server_test_SOURCES = xmlrpc-server-test.c $(TEST_SRCS)
+
+if HAVE_APACHE
+APACHE_TESTS = auth-test proxy-test pull-api range-test
+endif
+if HAVE_CURL
+CURL_TESTS = forms-test server-auth-test
+endif
+if HAVE_XMLRPC_EPI_PHP
+XMLRPC_TESTS = xmlrpc-test xmlrpc-server-test
+endif
+
+TESTS = \
+ chunk-test \
+ coding-test \
+ context-test \
+ continue-test \
+ cookies-test \
+ date \
+ header-parsing \
+ misc-test \
+ ntlm-test \
+ redirect-test \
+ requester-test \
+ sniffing-test \
+ streaming-test \
+ timeout-test \
+ uri-parsing \
+ $(APACHE_TESTS) \
+ $(CURL_TESTS) \
+ $(XMLRPC_TESTS)
+
+SNIFFING_FILES = \
+ resources/atom.xml \
+ resources/home.gif \
+ resources/html_binary.html \
+ resources/mbox \
+ resources/mbox.gz \
+ resources/ps_binary.ps \
+ resources/rss20.xml \
+ resources/test.html \
+ resources/text_binary.txt
+
+EXTRA_DIST = \
+ htdigest \
+ htpasswd \
+ httpd.conf.in \
+ index.txt \
+ libsoup.supp \
+ test-cert.pem \
+ test-key.pem \
+ xmlrpc-server.php \
+ $(SNIFFING_FILES)
+
+if MISSING_REGRESSION_TEST_PACKAGES
+check-local: check-TESTS
+ @echo ""
+ @echo "NOTE: some tests were not run due to missing packages:" $(MISSING_REGRESSION_TEST_PACKAGES)
+ @echo ""
+endif
+
+kill-httpd:
+ $(APACHE_HTTPD) -d `pwd` -f httpd.conf -k stop
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libsoup/soup.h"
+#include "libsoup/soup-auth.h"
+#include "libsoup/soup-session.h"
+
+#include "test-utils.h"
+
+static GMainLoop *loop;
+
+typedef struct {
+ /* Explanation of what you should see */
+ const char *explanation;
+
+ /* URL to test against */
+ const char *url;
+
+ /* Provided passwords, 1 character each. ('1', '2', and '3'
+ * mean the correct passwords for "realm1", "realm2", and
+ * "realm3" respectively. '4' means "use the wrong password".)
+ * The first password (if present) will be used by
+ * authenticate(), and the second (if present) will be used by
+ * reauthenticate().
+ */
+ const char *provided;
+
+ /* Whether to pass user and password in the URL or not.
+ */
+ gboolean url_auth;
+
+ /* Expected passwords, 1 character each. (As with the provided
+ * passwords, with the addition that '0' means "no
+ * Authorization header expected".) Used to verify that soup
+ * used the password it was supposed to at each step.
+ */
+ const char *expected;
+
+ /* What the final status code should be. */
+ guint final_status;
+} SoupAuthTest;
+
+/* Will either point to main_tests or relogin_tests
+ */
+static SoupAuthTest *current_tests;
+
+static SoupAuthTest main_tests[] = {
+ { "No auth available, should fail",
+ "Basic/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Should fail with no auth, fail again with bad password, and give up",
+ "Basic/realm2/", "4", FALSE, "04", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Auth provided this time, so should succeed",
+ "Basic/realm1/", "1", FALSE, "01", SOUP_STATUS_OK },
+
+ { "Now should automatically reuse previous auth",
+ "Basic/realm1/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Subdir should also automatically reuse auth",
+ "Basic/realm1/subdir/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Subdir should retry last auth, but will fail this time",
+ "Basic/realm1/realm2/", "", FALSE, "1", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Now should use provided auth",
+ "Basic/realm1/realm2/", "2", FALSE, "02", SOUP_STATUS_OK },
+
+ { "Reusing last auth. Should succeed on first try",
+ "Basic/realm1/realm2/", "", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Reuse will fail, but 2nd try will succeed because it's a known realm",
+ "Basic/realm1/realm2/realm1/", "", FALSE, "21", SOUP_STATUS_OK },
+
+ { "Should succeed on first try. (Known realm with cached password)",
+ "Basic/realm2/", "", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Fail once, then use typoed password, then use right password",
+ "Basic/realm3/", "43", FALSE, "043", SOUP_STATUS_OK },
+
+
+ { "No auth available, should fail",
+ "Digest/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Should fail with no auth, fail again with bad password, and give up",
+ "Digest/realm2/", "4", FALSE, "04", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Known realm, auth provided, so should succeed",
+ "Digest/realm1/", "1", FALSE, "01", SOUP_STATUS_OK },
+
+ { "Now should automatically reuse previous auth",
+ "Digest/realm1/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Subdir should also automatically reuse auth",
+ "Digest/realm1/subdir/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Password provided, should succeed",
+ "Digest/realm2/", "2", FALSE, "02", SOUP_STATUS_OK },
+
+ { "Should already know correct domain and use provided auth on first try",
+ "Digest/realm1/realm2/", "2", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Reusing last auth. Should succeed on first try",
+ "Digest/realm1/realm2/", "", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Should succeed on first try because of earlier domain directive",
+ "Digest/realm1/realm2/realm1/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Fail once, then use typoed password, then use right password",
+ "Digest/realm3/", "43", FALSE, "043", SOUP_STATUS_OK },
+
+
+ { "Make sure we haven't forgotten anything",
+ "Basic/realm1/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Make sure we haven't forgotten anything",
+ "Basic/realm1/realm2/", "", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Make sure we haven't forgotten anything",
+ "Basic/realm1/realm2/realm1/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Make sure we haven't forgotten anything",
+ "Basic/realm2/", "", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Make sure we haven't forgotten anything",
+ "Basic/realm3/", "", FALSE, "3", SOUP_STATUS_OK },
+
+
+ { "Make sure we haven't forgotten anything",
+ "Digest/realm1/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Make sure we haven't forgotten anything",
+ "Digest/realm1/realm2/", "", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Make sure we haven't forgotten anything",
+ "Digest/realm1/realm2/realm1/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Make sure we haven't forgotten anything",
+ "Digest/realm2/", "", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Make sure we haven't forgotten anything",
+ "Digest/realm3/", "", FALSE, "3", SOUP_STATUS_OK },
+
+ { "Now the server will reject the formerly-good password",
+ "Basic/realm1/not/", "1", FALSE, /* should not be used */ "1", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Make sure we've forgotten it",
+ "Basic/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Likewise, reject the formerly-good Digest password",
+ "Digest/realm1/not/", "1", FALSE, /* should not be used */ "1", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Make sure we've forgotten it",
+ "Digest/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED }
+};
+
+static const char *auths[] = {
+ "no password", "password 1",
+ "password 2", "password 3",
+ "intentionally wrong password",
+};
+
+static int
+identify_auth (SoupMessage *msg)
+{
+ const char *header;
+ int num;
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Authorization");
+ if (!header)
+ return 0;
+
+ if (!g_ascii_strncasecmp (header, "Basic ", 6)) {
+ char *token;
+ gsize len;
+
+ token = (char *)g_base64_decode (header + 6, &len);
+ num = token[len - 1] - '0';
+ g_free (token);
+ } else {
+ const char *user;
+
+ user = strstr (header, "username=\"user");
+ if (user)
+ num = user[14] - '0';
+ else
+ num = 0;
+ }
+
+ g_assert (num >= 0 && num <= 4);
+
+ return num;
+}
+
+static void
+handler (SoupMessage *msg, gpointer data)
+{
+ char *expected = data;
+ int auth, exp;
+
+ auth = identify_auth (msg);
+
+ debug_printf (1, " %d %s (using %s)\n",
+ msg->status_code, msg->reason_phrase,
+ auths[auth]);
+
+ if (*expected) {
+ exp = *expected - '0';
+ if (auth != exp) {
+ debug_printf (1, " expected %s!\n", auths[exp]);
+ errors++;
+ }
+ memmove (expected, expected + 1, strlen (expected));
+ } else {
+ debug_printf (1, " expected to be finished\n");
+ errors++;
+ }
+}
+
+static void
+authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ int *i = data;
+ char *username, *password;
+ char num;
+
+ if (!current_tests[*i].provided[0])
+ return;
+ if (retrying) {
+ if (!current_tests[*i].provided[1])
+ return;
+ num = current_tests[*i].provided[1];
+ } else
+ num = current_tests[*i].provided[0];
+
+ username = g_strdup_printf ("user%c", num);
+ password = g_strdup_printf ("realm%c", num);
+ soup_auth_authenticate (auth, username, password);
+ g_free (username);
+ g_free (password);
+}
+
+static void
+bug271540_sent (SoupMessage *msg, gpointer data)
+{
+ int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "#"));
+ gboolean *authenticated = data;
+ int auth = identify_auth (msg);
+
+ if (!*authenticated && auth) {
+ debug_printf (1, " using auth on message %d before authenticating!!??\n", n);
+ errors++;
+ } else if (*authenticated && !auth) {
+ debug_printf (1, " sent unauthenticated message %d after authenticating!\n", n);
+ errors++;
+ }
+}
+
+static void
+bug271540_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "#"));
+ gboolean *authenticated = data;
+
+ if (strcmp (soup_auth_get_scheme_name (auth), "Basic") != 0 ||
+ strcmp (soup_auth_get_realm (auth), "realm1") != 0)
+ return;
+
+ if (!*authenticated) {
+ debug_printf (1, " authenticating message %d\n", n);
+ soup_auth_authenticate (auth, "user1", "realm1");
+ *authenticated = TRUE;
+ } else {
+ debug_printf (1, " asked to authenticate message %d after authenticating!\n", n);
+ errors++;
+ }
+}
+
+static void
+bug271540_finished (SoupSession *session, SoupMessage *msg, gpointer data)
+{
+ int *left = data;
+ int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "#"));
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " got status '%d %s' on message %d!\n",
+ msg->status_code, msg->reason_phrase, n);
+ errors++;
+ }
+
+ (*left)--;
+ if (!*left)
+ g_main_loop_quit (loop);
+}
+
+static void
+digest_nonce_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ if (retrying)
+ return;
+
+ if (strcmp (soup_auth_get_scheme_name (auth), "Digest") != 0 ||
+ strcmp (soup_auth_get_realm (auth), "realm1") != 0)
+ return;
+
+ soup_auth_authenticate (auth, "user1", "realm1");
+}
+
+static void
+digest_nonce_unauthorized (SoupMessage *msg, gpointer data)
+{
+ gboolean *got_401 = data;
+ *got_401 = TRUE;
+}
+
+static void
+do_digest_nonce_test (SoupSession *session,
+ const char *nth, const char *uri,
+ gboolean expect_401, gboolean expect_signal)
+{
+ SoupMessage *msg;
+ gboolean got_401;
+
+ msg = soup_message_new (SOUP_METHOD_GET, uri);
+ if (expect_signal) {
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (digest_nonce_authenticate),
+ NULL);
+ }
+ soup_message_add_status_code_handler (msg, "got_headers",
+ SOUP_STATUS_UNAUTHORIZED,
+ G_CALLBACK (digest_nonce_unauthorized),
+ &got_401);
+ got_401 = FALSE;
+ soup_session_send_message (session, msg);
+ if (got_401 != expect_401) {
+ debug_printf (1, " %s request %s a 401 Unauthorized!\n", nth,
+ got_401 ? "got" : "did not get");
+ errors++;
+ }
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " %s request got status %d %s!\n", nth,
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ if (errors == 0)
+ debug_printf (1, " %s request succeeded\n", nth);
+ g_object_unref (msg);
+}
+
+/* Async auth test. We queue three requests to /Basic/realm1, ensuring
+ * that they are sent in order. The first and third ones will be
+ * paused from the authentication callback. The second will be allowed
+ * to fail. Shortly after the third one requests auth, we'll provide
+ * the auth and unpause the two remaining messages, allowing them to
+ * succeed.
+ */
+
+static void
+async_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ SoupAuth **saved_auth = data;
+ int id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "id"));
+
+ debug_printf (2, " async_authenticate msg%d\n", id);
+
+ /* The session will try to authenticate msg3 *before* sending
+ * it, because it already knows it's going to need the auth.
+ * Ignore that.
+ */
+ if (msg->status_code != SOUP_STATUS_UNAUTHORIZED) {
+ debug_printf (2, " (ignoring)\n");
+ return;
+ }
+
+ soup_session_pause_message (session, msg);
+ if (saved_auth)
+ *saved_auth = g_object_ref (auth);
+ g_main_loop_quit (loop);
+}
+
+static void
+async_finished (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ int *remaining = user_data;
+ int id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "id"));
+
+ debug_printf (2, " async_finished msg%d\n", id);
+
+ (*remaining)--;
+ if (!*remaining)
+ g_main_loop_quit (loop);
+}
+
+static void
+async_authenticate_assert_once (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ gboolean *been_here = data;
+
+ debug_printf (2, " async_authenticate_assert_once\n");
+
+ if (*been_here) {
+ debug_printf (1, " ERROR: async_authenticate_assert_once called twice\n");
+ errors++;
+ }
+ *been_here = TRUE;
+}
+
+static void
+async_authenticate_assert_once_and_stop (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ gboolean *been_here = data;
+
+ debug_printf (2, " async_authenticate_assert_once_and_stop\n");
+
+ if (*been_here) {
+ debug_printf (1, " ERROR: async_authenticate_assert_once called twice\n");
+ errors++;
+ }
+ *been_here = TRUE;
+
+ soup_session_pause_message (session, msg);
+ g_main_loop_quit (loop);
+}
+
+static void
+do_async_auth_test (const char *base_uri)
+{
+ SoupSession *session;
+ SoupMessage *msg1, *msg2, *msg3, msg2_bak;
+ guint auth_id;
+ char *uri;
+ SoupAuth *auth = NULL;
+ int remaining;
+ gboolean been_there;
+
+ debug_printf (1, "\nTesting async auth:\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ remaining = 0;
+
+ uri = g_strconcat (base_uri, "Basic/realm1/", NULL);
+
+ msg1 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1));
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate), &auth);
+ g_object_ref (msg1);
+ remaining++;
+ soup_session_queue_message (session, msg1, async_finished, &remaining);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+
+ /* async_authenticate will pause msg1 and quit loop */
+
+ msg2 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg2), "id", GINT_TO_POINTER (2));
+ soup_session_send_message (session, msg2);
+
+ if (msg2->status_code == SOUP_STATUS_UNAUTHORIZED)
+ debug_printf (1, " msg2 failed as expected\n");
+ else {
+ debug_printf (1, " msg2 got wrong status! (%u)\n",
+ msg2->status_code);
+ errors++;
+ }
+
+ /* msg2 should be done at this point; assuming everything is
+ * working correctly, the session won't look at it again; we
+ * ensure that if it does, it will crash the test program.
+ */
+ memcpy (&msg2_bak, msg2, sizeof (SoupMessage));
+ memset (msg2, 0, sizeof (SoupMessage));
+
+ msg3 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg3), "id", GINT_TO_POINTER (3));
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate), NULL);
+ g_object_ref (msg3);
+ remaining++;
+ soup_session_queue_message (session, msg3, async_finished, &remaining);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+
+ /* async_authenticate will pause msg3 and quit loop */
+
+ /* Now do the auth, and restart */
+ if (auth) {
+ soup_auth_authenticate (auth, "user1", "realm1");
+ g_object_unref (auth);
+ soup_session_unpause_message (session, msg1);
+ soup_session_unpause_message (session, msg3);
+
+ g_main_loop_run (loop);
+
+ /* async_finished will quit the loop */
+ } else {
+ debug_printf (1, " msg1 didn't get authenticate signal!\n");
+ errors++;
+ }
+
+ if (msg1->status_code == SOUP_STATUS_OK)
+ debug_printf (1, " msg1 succeeded\n");
+ else {
+ debug_printf (1, " msg1 FAILED! (%u %s)\n",
+ msg1->status_code, msg1->reason_phrase);
+ errors++;
+ }
+ if (msg3->status_code == SOUP_STATUS_OK)
+ debug_printf (1, " msg3 succeeded\n");
+ else {
+ debug_printf (1, " msg3 FAILED! (%u %s)\n",
+ msg3->status_code, msg3->reason_phrase);
+ errors++;
+ }
+
+ soup_test_session_abort_unref (session);
+
+ g_object_unref (msg1);
+ g_object_unref (msg3);
+ memcpy (msg2, &msg2_bak, sizeof (SoupMessage));
+ g_object_unref (msg2);
+
+ /* Test that giving the wrong password doesn't cause multiple
+ * authenticate signals the second time.
+ */
+ debug_printf (1, "\nTesting async auth with wrong password (#522601):\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ remaining = 0;
+ auth = NULL;
+
+ msg1 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1));
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate), &auth);
+ g_object_ref (msg1);
+ remaining++;
+ soup_session_queue_message (session, msg1, async_finished, &remaining);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+ soup_auth_authenticate (auth, "user1", "wrong");
+ g_object_unref (auth);
+ soup_session_unpause_message (session, msg1);
+
+ been_there = FALSE;
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate_assert_once),
+ &been_there);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+
+ if (!been_there) {
+ debug_printf (1, " authenticate not emitted?\n");
+ errors++;
+ }
+
+ soup_test_session_abort_unref (session);
+ g_object_unref (msg1);
+
+ /* Test that giving no password doesn't cause multiple
+ * authenticate signals the second time.
+ */
+ debug_printf (1, "\nTesting async auth with no password (#583462):\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ remaining = 0;
+
+ /* Send a message that doesn't actually authenticate
+ */
+ msg1 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1));
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate), NULL);
+ g_object_ref (msg1);
+ remaining++;
+ soup_session_queue_message (session, msg1, async_finished, &remaining);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+ soup_session_unpause_message (session, msg1);
+ g_main_loop_run (loop);
+ g_object_unref(msg1);
+
+ /* Now send a second message */
+ msg1 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (2));
+ g_object_ref (msg1);
+ been_there = FALSE;
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate_assert_once_and_stop),
+ &been_there);
+ remaining++;
+ soup_session_queue_message (session, msg1, async_finished, &remaining);
+ g_main_loop_run (loop);
+ soup_session_unpause_message (session, msg1);
+
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+
+ soup_test_session_abort_unref (session);
+ g_object_unref (msg1);
+
+ g_free (uri);
+}
+
+typedef struct {
+ const char *password;
+ struct {
+ const char *headers;
+ const char *response;
+ } round[2];
+} SelectAuthData;
+
+static void
+select_auth_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ SelectAuthData *sad = data;
+ const char *header, *basic, *digest;
+ int round = retrying ? 1 : 0;
+
+ header = soup_message_headers_get_list (msg->response_headers,
+ "WWW-Authenticate");
+ basic = strstr (header, "Basic");
+ digest = strstr (header, "Digest");
+ if (basic && digest) {
+ if (basic < digest)
+ sad->round[round].headers = "Basic, Digest";
+ else
+ sad->round[round].headers = "Digest, Basic";
+ } else if (basic)
+ sad->round[round].headers = "Basic";
+ else if (digest)
+ sad->round[round].headers = "Digest";
+
+ sad->round[round].response = soup_auth_get_scheme_name (auth);
+ if (sad->password && !retrying)
+ soup_auth_authenticate (auth, "user", sad->password);
+}
+
+static void
+select_auth_test_one (SoupURI *uri,
+ gboolean disable_digest, const char *password,
+ const char *first_headers, const char *first_response,
+ const char *second_headers, const char *second_response,
+ guint final_status)
+{
+ SelectAuthData sad;
+ SoupMessage *msg;
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ if (disable_digest)
+ soup_session_remove_feature_by_type (session, SOUP_TYPE_AUTH_DIGEST);
+
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (select_auth_authenticate), &sad);
+ memset (&sad, 0, sizeof (sad));
+ sad.password = password;
+
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_session_send_message (session, msg);
+
+ if (strcmp (sad.round[0].headers, first_headers) != 0) {
+ debug_printf (1, " Header order wrong: expected %s, got %s\n",
+ first_headers, sad.round[0].headers);
+ errors++;
+ }
+ if (strcmp (sad.round[0].response, first_response) != 0) {
+ debug_printf (1, " Selected auth type wrong: expected %s, got %s\n",
+ first_response, sad.round[0].response);
+ errors++;
+ }
+
+ if (second_headers && !sad.round[1].headers) {
+ debug_printf (1, " Expected a second round!\n");
+ errors++;
+ } else if (!second_headers && sad.round[1].headers) {
+ debug_printf (1, " Didn't expect a second round!\n");
+ errors++;
+ } else if (second_headers) {
+ if (strcmp (sad.round[1].headers, second_headers) != 0) {
+ debug_printf (1, " Second round header order wrong: expected %s, got %s\n",
+ second_headers, sad.round[1].headers);
+ errors++;
+ }
+ if (strcmp (sad.round[1].response, second_response) != 0) {
+ debug_printf (1, " Second round selected auth type wrong: expected %s, got %s\n",
+ second_response, sad.round[1].response);
+ errors++;
+ }
+ }
+
+ if (msg->status_code != final_status) {
+ debug_printf (1, " Final status wrong: expected %u, got %u\n",
+ final_status, msg->status_code);
+ errors++;
+ }
+
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "OK\r\n", 4);
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static gboolean
+server_basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+ const char *username, const char *password, gpointer data)
+{
+ if (strcmp (username, "user") != 0)
+ return FALSE;
+ return strcmp (password, "good-basic") == 0;
+}
+
+static char *
+server_digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+ const char *username, gpointer data)
+{
+ if (strcmp (username, "user") != 0)
+ return NULL;
+ return soup_auth_domain_digest_encode_password ("user",
+ "auth-test",
+ "good");
+}
+
+static void
+do_select_auth_test (void)
+{
+ SoupServer *server;
+ SoupAuthDomain *basic_auth_domain, *digest_auth_domain;
+ SoupURI *uri;
+
+ debug_printf (1, "\nTesting selection among multiple auths:\n");
+
+ /* It doesn't seem to be possible to configure Apache to serve
+ * multiple auth types for a single URL. So we have to use
+ * SoupServer here. We know that SoupServer handles the server
+ * side of this scenario correctly, because we test it against
+ * curl in server-auth-test.
+ */
+ server = soup_test_server_new (FALSE);
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+
+ uri = soup_uri_new ("http://127.0.0.1/");
+ soup_uri_set_port (uri, soup_server_get_port (server));
+
+ basic_auth_domain = soup_auth_domain_basic_new (
+ SOUP_AUTH_DOMAIN_REALM, "auth-test",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/",
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, server_basic_auth_callback,
+ NULL);
+ soup_server_add_auth_domain (server, basic_auth_domain);
+
+ digest_auth_domain = soup_auth_domain_digest_new (
+ SOUP_AUTH_DOMAIN_REALM, "auth-test",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/",
+ SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, server_digest_auth_callback,
+ NULL);
+ soup_server_add_auth_domain (server, digest_auth_domain);
+
+ debug_printf (1, " Testing with no auth\n");
+ select_auth_test_one (uri, FALSE, NULL,
+ "Basic, Digest", "Digest",
+ NULL, NULL,
+ SOUP_STATUS_UNAUTHORIZED);
+
+ debug_printf (1, " Testing with bad password\n");
+ select_auth_test_one (uri, FALSE, "bad",
+ "Basic, Digest", "Digest",
+ "Basic, Digest", "Digest",
+ SOUP_STATUS_UNAUTHORIZED);
+
+ debug_printf (1, " Testing with good password\n");
+ select_auth_test_one (uri, FALSE, "good",
+ "Basic, Digest", "Digest",
+ NULL, NULL,
+ SOUP_STATUS_OK);
+
+ /* Test with Digest disabled in the client. */
+ debug_printf (1, " Testing without Digest with no auth\n");
+ select_auth_test_one (uri, TRUE, NULL,
+ "Basic, Digest", "Basic",
+ NULL, NULL,
+ SOUP_STATUS_UNAUTHORIZED);
+
+ debug_printf (1, " Testing without Digest with bad password\n");
+ select_auth_test_one (uri, TRUE, "bad",
+ "Basic, Digest", "Basic",
+ "Basic, Digest", "Basic",
+ SOUP_STATUS_UNAUTHORIZED);
+
+ debug_printf (1, " Testing without Digest with good password\n");
+ select_auth_test_one (uri, TRUE, "good-basic",
+ "Basic, Digest", "Basic",
+ NULL, NULL,
+ SOUP_STATUS_OK);
+
+ /* Now flip the order of the domains, verify that this flips
+ * the order of the headers, and make sure that digest auth
+ * *still* gets used.
+ */
+
+ soup_server_remove_auth_domain (server, basic_auth_domain);
+ soup_server_remove_auth_domain (server, digest_auth_domain);
+ soup_server_add_auth_domain (server, digest_auth_domain);
+ soup_server_add_auth_domain (server, basic_auth_domain);
+
+ debug_printf (1, " Testing flipped with no auth\n");
+ select_auth_test_one (uri, FALSE, NULL,
+ "Digest, Basic", "Digest",
+ NULL, NULL,
+ SOUP_STATUS_UNAUTHORIZED);
+
+ debug_printf (1, " Testing flipped with bad password\n");
+ select_auth_test_one (uri, FALSE, "bad",
+ "Digest, Basic", "Digest",
+ "Digest, Basic", "Digest",
+ SOUP_STATUS_UNAUTHORIZED);
+
+ debug_printf (1, " Testing flipped with good password\n");
+ select_auth_test_one (uri, FALSE, "good",
+ "Digest, Basic", "Digest",
+ NULL, NULL,
+ SOUP_STATUS_OK);
+
+ g_object_unref (basic_auth_domain);
+ g_object_unref (digest_auth_domain);
+ soup_uri_free (uri);
+ soup_test_server_quit_unref (server);
+}
+
+static SoupAuthTest relogin_tests[] = {
+ { "Auth provided via URL, should succeed",
+ "Basic/realm12/", "1", TRUE, "01", SOUP_STATUS_OK },
+
+ { "Now should automatically reuse previous auth",
+ "Basic/realm12/", "", FALSE, "1", SOUP_STATUS_OK },
+
+ { "Different auth provided via URL for the same realm, should succeed",
+ "Basic/realm12/", "2", TRUE, "2", SOUP_STATUS_OK },
+
+ { "Subdir should also automatically reuse auth",
+ "Basic/realm12/subdir/", "", FALSE, "2", SOUP_STATUS_OK },
+
+ { "Should fail with no auth",
+ "Basic/realm12/", "4", TRUE, "4", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Make sure we've forgotten it",
+ "Basic/realm12/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Should fail with no auth, fail again with bad password, and give up",
+ "Basic/realm12/", "3", FALSE, "03", SOUP_STATUS_UNAUTHORIZED },
+};
+
+static void
+do_batch_tests (const gchar *base_uri_str, gint ntests)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ char *expected, *uristr;
+ SoupURI *base_uri;
+ int i;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), &i);
+
+ base_uri = soup_uri_new (base_uri_str);
+
+ for (i = 0; i < ntests; i++) {
+ SoupURI *soup_uri = soup_uri_new_with_base (base_uri, current_tests[i].url);
+
+ debug_printf (1, "Test %d: %s\n", i + 1, current_tests[i].explanation);
+
+ if (current_tests[i].url_auth) {
+ gchar *username = g_strdup_printf ("user%c", current_tests[i].provided[0]);
+ gchar *password = g_strdup_printf ("realm%c", current_tests[i].provided[0]);
+ soup_uri_set_user (soup_uri, username);
+ soup_uri_set_password (soup_uri, password);
+ g_free (username);
+ g_free (password);
+ }
+
+ msg = soup_message_new_from_uri (SOUP_METHOD_GET, soup_uri);
+ soup_uri_free (soup_uri);
+ if (!msg) {
+ fprintf (stderr, "auth-test: Could not parse URI\n");
+ exit (1);
+ }
+
+ uristr = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+ debug_printf (1, " GET %s\n", uristr);
+ g_free (uristr);
+
+ expected = g_strdup (current_tests[i].expected);
+ soup_message_add_status_code_handler (
+ msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
+ G_CALLBACK (handler), expected);
+ soup_message_add_status_code_handler (
+ msg, "got_headers", SOUP_STATUS_OK,
+ G_CALLBACK (handler), expected);
+ soup_session_send_message (session, msg);
+ if (msg->status_code != SOUP_STATUS_UNAUTHORIZED &&
+ msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " %d %s !\n", msg->status_code,
+ msg->reason_phrase);
+ errors++;
+ }
+ if (*expected) {
+ debug_printf (1, " expected %d more round(s)\n",
+ (int)strlen (expected));
+ errors++;
+ }
+ g_free (expected);
+
+ if (msg->status_code != current_tests[i].final_status) {
+ debug_printf (1, " expected %d\n",
+ current_tests[i].final_status);
+ }
+
+ debug_printf (1, "\n");
+
+ g_object_unref (msg);
+ }
+ soup_uri_free (base_uri);
+
+ soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ const char *base_uri;
+ char *uri;
+ gboolean authenticated;
+ int i, ntests;
+
+ test_init (argc, argv, NULL);
+ apache_init ();
+
+ base_uri = "http://127.0.0.1:47524/";
+
+ /* Main tests */
+ current_tests = main_tests;
+ ntests = G_N_ELEMENTS (main_tests);
+ do_batch_tests (base_uri, ntests);
+
+ /* Re-login tests */
+ current_tests = relogin_tests;
+ ntests = G_N_ELEMENTS (relogin_tests);
+ do_batch_tests (base_uri, ntests);
+
+ /* And now for some regression tests */
+ loop = g_main_loop_new (NULL, TRUE);
+
+ debug_printf (1, "Testing pipelined auth (bug 271540):\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ authenticated = FALSE;
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (bug271540_authenticate), &authenticated);
+
+ uri = g_strconcat (base_uri, "Basic/realm1/", NULL);
+ for (i = 0; i < 10; i++) {
+ msg = soup_message_new (SOUP_METHOD_GET, uri);
+ g_object_set_data (G_OBJECT (msg), "#", GINT_TO_POINTER (i + 1));
+ g_signal_connect (msg, "wrote_headers",
+ G_CALLBACK (bug271540_sent), &authenticated);
+
+ soup_session_queue_message (session, msg,
+ bug271540_finished, &i);
+ }
+ g_free (uri);
+
+ g_main_loop_run (loop);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, "\nTesting digest nonce expiration:\n");
+
+ /* We test two different things here:
+ *
+ * 1. If we get a 401 response with
+ * "WWW-Authenticate: Digest stale=true...", we should
+ * retry and succeed *without* the session asking for a
+ * password again.
+ *
+ * 2. If we get a successful response with
+ * "Authentication-Info: nextnonce=...", we should update
+ * the nonce automatically so as to avoid getting a
+ * stale nonce error on the next request.
+ *
+ * In our Apache config, /Digest/realm1 and
+ * /Digest/realm1/expire are set up to use the same auth info,
+ * but only the latter has an AuthDigestNonceLifetime (of 2
+ * seconds). The way nonces work in Apache, a nonce received
+ * from /Digest/realm1 will still expire in
+ * /Digest/realm1/expire, but it won't issue a nextnonce for a
+ * request in /Digest/realm1. This lets us test both
+ * behaviors.
+ *
+ * The expected conversation is:
+ *
+ * First message
+ * GET /Digest/realm1
+ *
+ * 401 Unauthorized
+ * WWW-Authenticate: Digest nonce=A
+ *
+ * [emit 'authenticate']
+ *
+ * GET /Digest/realm1
+ * Authorization: Digest nonce=A
+ *
+ * 200 OK
+ * [No Authentication-Info]
+ *
+ * [sleep 2 seconds: nonce A is no longer valid, but we have no
+ * way of knowing that]
+ *
+ * Second message
+ * GET /Digest/realm1/expire/
+ * Authorization: Digest nonce=A
+ *
+ * 401 Unauthorized
+ * WWW-Authenticate: Digest stale=true nonce=B
+ *
+ * GET /Digest/realm1/expire/
+ * Authorization: Digest nonce=B
+ *
+ * 200 OK
+ * Authentication-Info: nextnonce=C
+ *
+ * [sleep 1 second]
+ *
+ * Third message
+ * GET /Digest/realm1/expire/
+ * Authorization: Digest nonce=C
+ * [nonce=B would work here too]
+ *
+ * 200 OK
+ * Authentication-Info: nextnonce=D
+ *
+ * [sleep 1 second; nonces B and C are no longer valid, but D is]
+ *
+ * Fourth message
+ * GET /Digest/realm1/expire/
+ * Authorization: Digest nonce=D
+ *
+ * 200 OK
+ * Authentication-Info: nextnonce=D
+ *
+ */
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ uri = g_strconcat (base_uri, "Digest/realm1/", NULL);
+ do_digest_nonce_test (session, "First", uri, TRUE, TRUE);
+ g_free (uri);
+ sleep (2);
+ uri = g_strconcat (base_uri, "Digest/realm1/expire/", NULL);
+ do_digest_nonce_test (session, "Second", uri, TRUE, FALSE);
+ sleep (1);
+ do_digest_nonce_test (session, "Third", uri, FALSE, FALSE);
+ sleep (1);
+ do_digest_nonce_test (session, "Fourth", uri, FALSE, FALSE);
+ g_free (uri);
+
+ soup_test_session_abort_unref (session);
+
+ /* Async auth */
+ do_async_auth_test (base_uri);
+
+ /* Selecting correct auth when multiple auth types are available */
+ do_select_auth_test ();
+
+ g_main_loop_unref (loop);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+typedef struct {
+ SoupSession *session;
+ SoupBuffer *chunks[3];
+ int next, nwrote, nfreed;
+ gboolean streaming;
+} PutTestData;
+
+static SoupBuffer *
+error_chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
+{
+ /* This should never be called, because there is no response body. */
+ debug_printf (1, " error_chunk_allocator called!\n");
+ errors++;
+ return soup_buffer_new (SOUP_MEMORY_TAKE, g_malloc (100), 100);
+}
+
+static void
+write_next_chunk (SoupMessage *msg, gpointer user_data)
+{
+ PutTestData *ptd = user_data;
+
+ debug_printf (2, " writing chunk %d\n", ptd->next);
+
+ if (ptd->streaming && ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
+ debug_printf (1, " error: next chunk requested before last one freed!\n");
+ errors++;
+ }
+
+ if (ptd->next < G_N_ELEMENTS (ptd->chunks)) {
+ soup_message_body_append_buffer (msg->request_body,
+ ptd->chunks[ptd->next]);
+ soup_buffer_free (ptd->chunks[ptd->next]);
+ ptd->next++;
+ } else
+ soup_message_body_complete (msg->request_body);
+ soup_session_unpause_message (ptd->session, msg);
+}
+
+/* This is not a supported part of the API. Use SOUP_MESSAGE_CAN_REBUILD
+ * instead.
+ */
+static void
+write_next_chunk_streaming_hack (SoupMessage *msg, gpointer user_data)
+{
+ PutTestData *ptd = user_data;
+ SoupBuffer *chunk;
+
+ debug_printf (2, " freeing chunk at %d\n", ptd->nfreed);
+ chunk = soup_message_body_get_chunk (msg->request_body, ptd->nfreed);
+ if (chunk) {
+ ptd->nfreed += chunk->length;
+ soup_message_body_wrote_chunk (msg->request_body, chunk);
+ soup_buffer_free (chunk);
+ } else {
+ debug_printf (1, " error: written chunk does not exist!\n");
+ errors++;
+ }
+ write_next_chunk (msg, user_data);
+}
+
+static void
+wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
+{
+ PutTestData *ptd = user_data;
+
+ debug_printf (2, " wrote_body_data, %d bytes\n",
+ (int)chunk->length);
+ ptd->nwrote += chunk->length;
+}
+
+static void
+clear_buffer_ptr (gpointer data)
+{
+ SoupBuffer **buffer_ptr = data;
+
+ debug_printf (2, " clearing chunk\n");
+ if (*buffer_ptr) {
+ (*buffer_ptr)->length = 0;
+ g_free ((char *)(*buffer_ptr)->data);
+ *buffer_ptr = NULL;
+ } else {
+ debug_printf (2, " chunk is already clear!\n");
+ errors++;
+ }
+}
+
+/* Put a chunk containing @text into *@buffer, set up so that it will
+ * clear out *@buffer when the chunk is freed, allowing us to make sure
+ * the set_accumulate(FALSE) is working.
+ */
+static void
+make_put_chunk (SoupBuffer **buffer, const char *text)
+{
+ *buffer = soup_buffer_new_with_owner (g_strdup (text), strlen (text),
+ buffer, clear_buffer_ptr);
+}
+
+static void
+setup_request_body (PutTestData *ptd)
+{
+ make_put_chunk (&ptd->chunks[0], "one\r\n");
+ make_put_chunk (&ptd->chunks[1], "two\r\n");
+ make_put_chunk (&ptd->chunks[2], "three\r\n");
+ ptd->next = ptd->nwrote = ptd->nfreed = 0;
+}
+
+static void
+restarted_streaming (SoupMessage *msg, gpointer user_data)
+{
+ PutTestData *ptd = user_data;
+
+ debug_printf (2, " --restarting--\n");
+
+ /* We're streaming, and we had to restart. So the data need
+ * to be regenerated.
+ */
+ setup_request_body (ptd);
+
+ /* The 302 redirect will turn it into a GET request and
+ * reset the body encoding back to "NONE". Fix that.
+ */
+ soup_message_headers_set_encoding (msg->request_headers,
+ SOUP_ENCODING_CHUNKED);
+ msg->method = SOUP_METHOD_PUT;
+}
+
+static void
+restarted_streaming_hack (SoupMessage *msg, gpointer user_data)
+{
+ restarted_streaming (msg, user_data);
+ soup_message_body_truncate (msg->request_body);
+}
+
+typedef enum {
+ HACKY_STREAMING = (1 << 0),
+ PROPER_STREAMING = (1 << 1),
+ RESTART = (1 << 2)
+} RequestTestFlags;
+
+static void
+do_request_test (SoupSession *session, SoupURI *base_uri, RequestTestFlags flags)
+{
+ SoupURI *uri = base_uri;
+ PutTestData ptd;
+ SoupMessage *msg;
+ const char *client_md5, *server_md5;
+ GChecksum *check;
+ int i, length;
+
+ debug_printf (1, "PUT");
+ if (flags & HACKY_STREAMING)
+ debug_printf (1, " w/ hacky streaming");
+ else if (flags & PROPER_STREAMING)
+ debug_printf (1, " w/ proper streaming");
+ if (flags & RESTART) {
+ debug_printf (1, " and restart");
+ uri = soup_uri_copy (base_uri);
+ soup_uri_set_path (uri, "/redirect");
+ }
+ debug_printf (1, "\n");
+
+ ptd.session = session;
+ setup_request_body (&ptd);
+ ptd.streaming = flags & (HACKY_STREAMING | PROPER_STREAMING);
+
+ check = g_checksum_new (G_CHECKSUM_MD5);
+ length = 0;
+ for (i = 0; i < 3; i++) {
+ g_checksum_update (check, (guchar *)ptd.chunks[i]->data,
+ ptd.chunks[i]->length);
+ length += ptd.chunks[i]->length;
+ }
+ client_md5 = g_checksum_get_string (check);
+
+ msg = soup_message_new_from_uri ("PUT", uri);
+ soup_message_headers_set_encoding (msg->request_headers, SOUP_ENCODING_CHUNKED);
+ soup_message_body_set_accumulate (msg->request_body, FALSE);
+ soup_message_set_chunk_allocator (msg, error_chunk_allocator, NULL, NULL);
+ if (flags & HACKY_STREAMING) {
+ g_signal_connect (msg, "wrote_chunk",
+ G_CALLBACK (write_next_chunk_streaming_hack), &ptd);
+ if (flags & RESTART) {
+ g_signal_connect (msg, "restarted",
+ G_CALLBACK (restarted_streaming_hack), &ptd);
+ }
+ } else {
+ g_signal_connect (msg, "wrote_chunk",
+ G_CALLBACK (write_next_chunk), &ptd);
+ }
+
+ if (flags & PROPER_STREAMING) {
+ soup_message_set_flags (msg, SOUP_MESSAGE_CAN_REBUILD);
+ if (flags & RESTART) {
+ g_signal_connect (msg, "restarted",
+ G_CALLBACK (restarted_streaming), &ptd);
+ }
+ }
+
+ g_signal_connect (msg, "wrote_headers",
+ G_CALLBACK (write_next_chunk), &ptd);
+ g_signal_connect (msg, "wrote_body_data",
+ G_CALLBACK (wrote_body_data), &ptd);
+ soup_session_send_message (session, msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " message failed: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+
+ if (msg->request_body->data) {
+ debug_printf (1, " msg->request_body set!\n");
+ errors++;
+ }
+ if (msg->request_body->length != length || length != ptd.nwrote) {
+ debug_printf (1, " sent length mismatch: %d vs %d vs %d\n",
+ (int)msg->request_body->length, length, ptd.nwrote);
+ errors++;
+ }
+
+ server_md5 = soup_message_headers_get_one (msg->response_headers,
+ "Content-MD5");
+ if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
+ debug_printf (1, " client/server data mismatch: %s vs %s\n",
+ client_md5, server_md5 ? server_md5 : "(null)");
+ errors++;
+ }
+
+ g_object_unref (msg);
+ g_checksum_free (check);
+
+ if (uri != base_uri)
+ soup_uri_free (uri);
+}
+
+typedef struct {
+ SoupBuffer *current_chunk;
+ GChecksum *check;
+ int length;
+} GetTestData;
+
+static SoupBuffer *
+chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
+{
+ GetTestData *gtd = user_data;
+
+ debug_printf (2, " allocating chunk\n");
+
+ if (gtd->current_chunk) {
+ debug_printf (1, " error: next chunk allocated before last one freed!\n");
+ errors++;
+ }
+ gtd->current_chunk = soup_buffer_new_with_owner (g_malloc (6), 6,
+ >d->current_chunk,
+ clear_buffer_ptr);
+ return gtd->current_chunk;
+}
+
+static void
+got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
+{
+ GetTestData *gtd = user_data;
+
+ debug_printf (2, " got chunk, %d bytes\n",
+ (int)chunk->length);
+ if (chunk != gtd->current_chunk) {
+ debug_printf (1, "chunk mismatch! %p vs %p\n",
+ chunk, gtd->current_chunk);
+ }
+
+ g_checksum_update (gtd->check, (guchar *)chunk->data, chunk->length);
+ gtd->length += chunk->length;
+}
+
+static void
+do_response_test (SoupSession *session, SoupURI *base_uri)
+{
+ GetTestData gtd;
+ SoupMessage *msg;
+ const char *client_md5, *server_md5;
+
+ debug_printf (1, "GET\n");
+
+ gtd.current_chunk = NULL;
+ gtd.length = 0;
+ gtd.check = g_checksum_new (G_CHECKSUM_MD5);
+
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ soup_message_body_set_accumulate (msg->response_body, FALSE);
+ soup_message_set_chunk_allocator (msg, chunk_allocator, >d, NULL);
+ g_signal_connect (msg, "got_chunk",
+ G_CALLBACK (got_chunk), >d);
+ soup_session_send_message (session, msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " message failed: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+
+ if (msg->response_body->data) {
+ debug_printf (1, " msg->response_body set!\n");
+ errors++;
+ }
+ if (soup_message_headers_get_content_length (msg->response_headers) != gtd.length) {
+ debug_printf (1, " received length mismatch: %d vs %d\n",
+ (int)soup_message_headers_get_content_length (msg->response_headers), gtd.length);
+ errors++;
+ }
+
+ client_md5 = g_checksum_get_string (gtd.check);
+ server_md5 = soup_message_headers_get_one (msg->response_headers,
+ "Content-MD5");
+ if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
+ debug_printf (1, " client/server data mismatch: %s vs %s\n",
+ client_md5, server_md5 ? server_md5 : "(null)");
+ errors++;
+ }
+
+ g_object_unref (msg);
+ g_checksum_free (gtd.check);
+}
+
+/* Make sure TEMPORARY buffers are handled properly with non-accumulating
+ * message bodies. Part of https://bugs.webkit.org/show_bug.cgi?id=18343
+ */
+
+static void
+temp_test_wrote_chunk (SoupMessage *msg, gpointer session)
+{
+ SoupBuffer *chunk;
+
+ chunk = soup_message_body_get_chunk (msg->request_body, 5);
+
+ /* When the bug is present, the second chunk will also be
+ * discarded after the first is written, which will cause
+ * the I/O to stall since soup-message-io will think it's
+ * done, but it hasn't written Content-Length bytes yet.
+ */
+ if (!chunk) {
+ debug_printf (1, " Lost second chunk!\n");
+ errors++;
+ soup_session_abort (session);
+ } else
+ soup_buffer_free (chunk);
+
+ g_signal_handlers_disconnect_by_func (msg, temp_test_wrote_chunk, session);
+}
+
+static void
+do_temporary_test (SoupSession *session, SoupURI *base_uri)
+{
+ SoupMessage *msg;
+ char *client_md5;
+ const char *server_md5;
+
+ debug_printf (1, "PUT w/ temporary buffers\n");
+
+ msg = soup_message_new_from_uri ("PUT", base_uri);
+ soup_message_body_append (msg->request_body, SOUP_MEMORY_TEMPORARY,
+ "one\r\n", 5);
+ soup_message_body_append (msg->request_body, SOUP_MEMORY_STATIC,
+ "two\r\n", 5);
+ soup_message_body_set_accumulate (msg->request_body, FALSE);
+
+ client_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5,
+ "one\r\ntwo\r\n", 10);
+ g_signal_connect (msg, "wrote_chunk",
+ G_CALLBACK (temp_test_wrote_chunk), session);
+ soup_session_send_message (session, msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " message failed: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+
+ server_md5 = soup_message_headers_get_one (msg->response_headers,
+ "Content-MD5");
+ if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
+ debug_printf (1, " client/server data mismatch: %s vs %s\n",
+ client_md5, server_md5 ? server_md5 : "(null)");
+ errors++;
+ }
+
+ g_free (client_md5);
+ g_object_unref (msg);
+}
+
+static void
+do_chunk_tests (SoupURI *base_uri)
+{
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_request_test (session, base_uri, 0);
+ debug_printf (2, "\n\n");
+ do_request_test (session, base_uri, PROPER_STREAMING);
+ debug_printf (2, "\n\n");
+ do_request_test (session, base_uri, PROPER_STREAMING | RESTART);
+ debug_printf (2, "\n\n");
+ do_request_test (session, base_uri, HACKY_STREAMING);
+ debug_printf (2, "\n\n");
+ do_request_test (session, base_uri, HACKY_STREAMING | RESTART);
+ debug_printf (2, "\n\n");
+ do_response_test (session, base_uri);
+ debug_printf (2, "\n\n");
+ do_temporary_test (session, base_uri);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ SoupMessageBody *md5_body;
+ char *md5;
+
+ if (g_str_has_prefix (path, "/redirect")) {
+ soup_message_set_status (msg, SOUP_STATUS_FOUND);
+ soup_message_headers_replace (msg->response_headers,
+ "Location", "/");
+ return;
+ }
+
+ if (msg->method == SOUP_METHOD_GET) {
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "three\r\ntwo\r\none\r\n",
+ strlen ("three\r\ntwo\r\none\r\n"));
+ soup_buffer_free (soup_message_body_flatten (msg->response_body));
+ md5_body = msg->response_body;
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ } else if (msg->method == SOUP_METHOD_PUT) {
+ soup_message_set_status (msg, SOUP_STATUS_CREATED);
+ md5_body = msg->request_body;
+ } else {
+ soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+ return;
+ }
+
+ md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+ (guchar *)md5_body->data,
+ md5_body->length);
+ soup_message_headers_append (msg->response_headers,
+ "Content-MD5", md5);
+ g_free (md5);
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ SoupServer *server;
+ guint port;
+ SoupURI *base_uri;
+
+ test_init (argc, argv, NULL);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+ port = soup_server_get_port (server);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ base_uri = soup_uri_new ("http://127.0.0.1");
+ soup_uri_set_port (base_uri, port);
+ do_chunk_tests (base_uri);
+ soup_uri_free (base_uri);
+
+ g_main_loop_unref (loop);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+SoupServer *server;
+SoupURI *base_uri;
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ const char *accept_encoding, *options;
+ GSList *codings;
+ char *file = NULL, *contents;
+ gsize length;
+
+ options = soup_message_headers_get_one (msg->request_headers,
+ "X-Test-Options");
+ if (!options)
+ options = "";
+
+ accept_encoding = soup_message_headers_get_list (msg->request_headers,
+ "Accept-Encoding");
+ if (accept_encoding && !soup_header_contains (options, "force-encode"))
+ codings = soup_header_parse_quality_list (accept_encoding, NULL);
+ else
+ codings = NULL;
+
+ if (codings && g_slist_find_custom (codings, "gzip", (GCompareFunc)g_ascii_strcasecmp)) {
+ file = g_strdup_printf (SRCDIR "/resources%s.gz", path);
+ if (g_file_test (file, G_FILE_TEST_EXISTS)) {
+ soup_message_headers_append (msg->response_headers,
+ "Content-Encoding",
+ "gzip");
+ } else {
+ g_free (file);
+ file = NULL;
+ }
+ }
+ soup_header_free_list (codings);
+
+ if (!file)
+ file = g_strdup_printf (SRCDIR "/resources%s", path);
+ if (!g_file_get_contents (file, &contents, &length, NULL)) {
+ /* If path.gz exists but can't be read, we'll send back
+ * the error with "Content-Encoding: gzip" but there's
+ * no body, so, eh.
+ */
+ g_free (file);
+ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+ return;
+ }
+ g_free (file);
+
+ if (soup_header_contains (options, "force-encode")) {
+ soup_message_headers_replace (msg->response_headers,
+ "Content-Encoding",
+ "gzip");
+ }
+
+ /* Content-Type matches the "real" format, not the sent format */
+ if (g_str_has_suffix (path, ".gz")) {
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type",
+ "application/gzip");
+ } else {
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type",
+ "text/plain");
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_body_append (msg->response_body,
+ SOUP_MEMORY_TAKE, contents, length);
+
+ if (soup_header_contains (options, "trailing-junk")) {
+ soup_message_body_append (msg->response_body, SOUP_MEMORY_COPY,
+ options, strlen (options));
+ }
+}
+
+static void
+do_coding_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg, *msgz, *msgj, *msge;
+ SoupURI *uri;
+ const char *coding, *type;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ uri = soup_uri_new_with_base (base_uri, "/mbox");
+
+
+ debug_printf (1, "GET /mbox, plain\n");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_session_send_message (session, msg);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " Unexpected status %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ coding = soup_message_headers_get_one (msg->response_headers, "Content-Encoding");
+ if (coding) {
+ debug_printf (1, " Unexpected Content-Encoding: %s\n",
+ coding);
+ errors++;
+ }
+ if (soup_message_get_flags (msg) & SOUP_MESSAGE_CONTENT_DECODED) {
+ debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED set!\n");
+ errors++;
+ }
+ type = soup_message_headers_get_one (msg->response_headers, "Content-Type");
+ if (!type || g_ascii_strcasecmp (type, "text/plain") != 0) {
+ debug_printf (1, " Unexpected Content-Type: %s\n",
+ type ? type : "(none)");
+ errors++;
+ }
+
+ debug_printf (1, "GET /mbox, Accept-Encoding: gzip\n");
+ soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
+ msgz = soup_message_new_from_uri ("GET", uri);
+ soup_session_send_message (session, msgz);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msgz->status_code)) {
+ debug_printf (1, " Unexpected status %d %s\n",
+ msgz->status_code, msgz->reason_phrase);
+ errors++;
+ }
+ coding = soup_message_headers_get_one (msgz->response_headers, "Content-Encoding");
+ if (!coding || g_ascii_strcasecmp (coding, "gzip") != 0) {
+ debug_printf (1, " Unexpected Content-Encoding: %s\n",
+ coding ? coding : "(none)");
+ errors++;
+ }
+ if (!(soup_message_get_flags (msgz) & SOUP_MESSAGE_CONTENT_DECODED)) {
+ debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED not set!\n");
+ errors++;
+ }
+ type = soup_message_headers_get_one (msgz->response_headers, "Content-Type");
+ if (!type || g_ascii_strcasecmp (type, "text/plain") != 0) {
+ debug_printf (1, " Unexpected Content-Type: %s\n",
+ type ? type : "(none)");
+ errors++;
+ }
+
+ if (msg->response_body->length != msgz->response_body->length) {
+ debug_printf (1, " Message length mismatch: %lu (plain) vs %lu (compressed)\n",
+ (gulong)msg->response_body->length,
+ (gulong)msgz->response_body->length);
+ errors++;
+ } else if (memcmp (msg->response_body->data,
+ msgz->response_body->data,
+ msg->response_body->length) != 0) {
+ debug_printf (1, " Message data mismatch (plain/compressed)\n");
+ errors++;
+ }
+
+
+ debug_printf (1, "GET /mbox, Accept-Encoding: gzip, plus trailing junk\n");
+ msgj = soup_message_new_from_uri ("GET", uri);
+ soup_message_headers_append (msgj->request_headers,
+ "X-Test-Options", "trailing-junk");
+ soup_session_send_message (session, msgj);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msgj->status_code)) {
+ debug_printf (1, " Unexpected status %d %s\n",
+ msgj->status_code, msgj->reason_phrase);
+ errors++;
+ }
+ coding = soup_message_headers_get_one (msgj->response_headers, "Content-Encoding");
+ if (!coding || g_ascii_strcasecmp (coding, "gzip") != 0) {
+ debug_printf (1, " Unexpected Content-Encoding: %s\n",
+ coding ? coding : "(none)");
+ errors++;
+ }
+ if (!(soup_message_get_flags (msgj) & SOUP_MESSAGE_CONTENT_DECODED)) {
+ debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED not set!\n");
+ errors++;
+ }
+ type = soup_message_headers_get_one (msgj->response_headers, "Content-Type");
+ if (!type || g_ascii_strcasecmp (type, "text/plain") != 0) {
+ debug_printf (1, " Unexpected Content-Type: %s\n",
+ type ? type : "(none)");
+ errors++;
+ }
+
+ if (msg->response_body->length != msgj->response_body->length) {
+ debug_printf (1, " Message length mismatch: %lu (plain) vs %lu (compressed w/ junk)\n",
+ (gulong)msg->response_body->length,
+ (gulong)msgj->response_body->length);
+ errors++;
+ } else if (memcmp (msg->response_body->data,
+ msgj->response_body->data,
+ msg->response_body->length) != 0) {
+ debug_printf (1, " Message data mismatch (plain/compressed w/ junk)\n");
+ errors++;
+ }
+
+
+ debug_printf (1, "GET /mbox, Accept-Encoding: gzip, with server error\n");
+ msge = soup_message_new_from_uri ("GET", uri);
+ soup_message_headers_append (msge->request_headers,
+ "X-Test-Options", "force-encode");
+ soup_session_send_message (session, msge);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msge->status_code)) {
+ debug_printf (1, " Unexpected status %d %s\n",
+ msge->status_code, msge->reason_phrase);
+ errors++;
+ }
+ coding = soup_message_headers_get_one (msge->response_headers, "Content-Encoding");
+ if (!coding || g_ascii_strcasecmp (coding, "gzip") != 0) {
+ debug_printf (1, " Unexpected Content-Encoding: %s\n",
+ coding ? coding : "(none)");
+ errors++;
+ }
+ /* Since the content wasn't actually gzip-encoded, decoding it
+ * should have failed and so the flag won't be set.
+ */
+ if (soup_message_get_flags (msge) & SOUP_MESSAGE_CONTENT_DECODED) {
+ debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED set!\n");
+ errors++;
+ }
+ type = soup_message_headers_get_one (msge->response_headers, "Content-Type");
+ if (!type || g_ascii_strcasecmp (type, "text/plain") != 0) {
+ debug_printf (1, " Unexpected Content-Type: %s\n",
+ type ? type : "(none)");
+ errors++;
+ }
+
+ /* Failed content-decoding should have left the body untouched
+ * from what the server sent... which happens to be the
+ * uncompressed data.
+ */
+ if (msg->response_body->length != msge->response_body->length) {
+ debug_printf (1, " Message length mismatch: %lu (plain) vs %lu (mis-encoded)\n",
+ (gulong)msg->response_body->length,
+ (gulong)msge->response_body->length);
+ errors++;
+ } else if (memcmp (msg->response_body->data,
+ msge->response_body->data,
+ msg->response_body->length) != 0) {
+ debug_printf (1, " Message data mismatch (plain/misencoded)\n");
+ errors++;
+ }
+
+
+ g_object_unref (msg);
+ g_object_unref (msgz);
+ g_object_unref (msgj);
+ g_object_unref (msge);
+ soup_uri_free (uri);
+
+
+ uri = soup_uri_new_with_base (base_uri, "/mbox.gz");
+
+ debug_printf (1, "GET /mbox.gz, Accept-Encoding: gzip\n");
+ soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
+ msgz = soup_message_new_from_uri ("GET", uri);
+ soup_session_send_message (session, msgz);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msgz->status_code)) {
+ debug_printf (1, " Unexpected status %d %s\n",
+ msgz->status_code, msgz->reason_phrase);
+ errors++;
+ }
+ coding = soup_message_headers_get_one (msgz->response_headers, "Content-Encoding");
+ if (coding) {
+ debug_printf (1, " Unexpected Content-Encoding: %s\n", coding);
+ errors++;
+ }
+ type = soup_message_headers_get_one (msgz->response_headers, "Content-Type");
+ if (!type || g_ascii_strcasecmp (type, "application/gzip") != 0) {
+ debug_printf (1, " Unexpected Content-Type: %s\n",
+ type ? type : "(none)");
+ errors++;
+ }
+
+
+ debug_printf (1, "GET /mbox.gz, Accept-Encoding: gzip, with server error\n");
+ msge = soup_message_new_from_uri ("GET", uri);
+ soup_message_headers_append (msge->request_headers,
+ "X-Test-Options", "force-encode");
+ soup_session_send_message (session, msge);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msge->status_code)) {
+ debug_printf (1, " Unexpected status %d %s\n",
+ msge->status_code, msge->reason_phrase);
+ errors++;
+ }
+ coding = soup_message_headers_get_one (msge->response_headers, "Content-Encoding");
+ if (!coding || g_ascii_strcasecmp (coding, "gzip") != 0) {
+ debug_printf (1, " Unexpected Content-Encoding: %s\n",
+ coding ? coding : "(none)");
+ errors++;
+ }
+ /* SoupContentDecoder should have recognized the bug and thus
+ * not decoded it
+ */
+ if (soup_message_get_flags (msge) & SOUP_MESSAGE_CONTENT_DECODED) {
+ debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED set!\n");
+ errors++;
+ }
+ type = soup_message_headers_get_one (msge->response_headers, "Content-Type");
+ if (!type || g_ascii_strcasecmp (type, "application/gzip") != 0) {
+ debug_printf (1, " Unexpected Content-Type: %s\n",
+ type ? type : "(none)");
+ errors++;
+ }
+
+ if (msgz->response_body->length != msge->response_body->length) {
+ debug_printf (1, " Message length mismatch: %lu (.gz) vs %lu (mis-encoded)\n",
+ (gulong)msgz->response_body->length,
+ (gulong)msge->response_body->length);
+ errors++;
+ } else if (memcmp (msgz->response_body->data,
+ msge->response_body->data,
+ msgz->response_body->length) != 0) {
+ debug_printf (1, " Message data mismatch (gz/misencoded)\n");
+ errors++;
+ }
+
+
+ g_object_unref (msgz);
+ g_object_unref (msge);
+ soup_uri_free (uri);
+
+ soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ test_init (argc, argv, NULL);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ base_uri = soup_uri_new ("http://127.0.0.1/");
+ soup_uri_set_port (base_uri, soup_server_get_port (server));
+
+ do_coding_test ();
+
+ soup_uri_free (base_uri);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-misc.h>
+#include <libsoup/soup-server.h>
+#include <libsoup/soup-session-async.h>
+#include <libsoup/soup-session-sync.h>
+
+#include "test-utils.h"
+
+static char *base_uri;
+
+typedef struct {
+ SoupServer *server;
+ SoupMessage *msg;
+ GSource *timeout;
+} SlowData;
+
+static void
+request_failed (SoupMessage *msg, gpointer data)
+{
+ SlowData *sd = data;
+
+ if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
+ g_source_destroy (sd->timeout);
+ g_free (sd);
+}
+
+static gboolean
+add_body_chunk (gpointer data)
+{
+ SlowData *sd = data;
+
+ soup_message_body_append (sd->msg->response_body,
+ SOUP_MEMORY_STATIC, "OK\r\n", 4);
+ soup_message_body_complete (sd->msg->response_body);
+ soup_server_unpause_message (sd->server, sd->msg);
+ g_object_unref (sd->msg);
+
+ return FALSE;
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ SlowData *sd;
+
+ if (msg->method != SOUP_METHOD_GET) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ if (!strcmp (path, "/fast")) {
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC, "OK\r\n", 4);
+ return;
+ }
+
+ soup_message_headers_set_encoding (msg->response_headers,
+ SOUP_ENCODING_CHUNKED);
+ g_object_ref (msg);
+ soup_server_pause_message (server, msg);
+
+ sd = g_new (SlowData, 1);
+ sd->server = server;
+ sd->msg = msg;
+ sd->timeout = soup_add_timeout (
+ soup_server_get_async_context (server),
+ 200, add_body_chunk, sd);
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (request_failed), sd);
+}
+
+/* Test 1: An async session in another thread with its own
+ * async_context can complete a request while the main thread's main
+ * loop is stopped.
+ */
+
+static gboolean idle_start_test1_thread (gpointer loop);
+static gpointer test1_thread (gpointer user_data);
+
+static GCond *test1_cond;
+static GMutex *test1_mutex;
+
+static void
+do_test1 (void)
+{
+ GMainLoop *loop;
+
+ debug_printf (1, "Test 1: blocking the main thread does not block other thread\n");
+
+ test1_cond = g_cond_new ();
+ test1_mutex = g_mutex_new ();
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_idle_add (idle_start_test1_thread, loop);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ g_mutex_free (test1_mutex);
+ g_cond_free (test1_cond);
+}
+
+static gboolean
+idle_start_test1_thread (gpointer loop)
+{
+ GTimeVal time;
+ GThread *thread;
+
+ g_mutex_lock (test1_mutex);
+ thread = g_thread_create (test1_thread, base_uri, TRUE, NULL);
+
+ g_get_current_time (&time);
+ time.tv_sec += 5;
+ if (g_cond_timed_wait (test1_cond, test1_mutex, &time))
+ g_thread_join (thread);
+ else {
+ debug_printf (1, " timeout!\n");
+ errors++;
+ }
+
+ g_mutex_unlock (test1_mutex);
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static void
+test1_finished (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+ g_main_loop_quit (loop);
+}
+
+static gpointer
+test1_thread (gpointer user_data)
+{
+ SoupSession *session;
+ GMainContext *async_context;
+ char *uri;
+ SoupMessage *msg;
+ GMainLoop *loop;
+
+ /* Wait for main thread to be waiting on test1_cond */
+ g_mutex_lock (test1_mutex);
+ g_mutex_unlock (test1_mutex);
+
+ async_context = g_main_context_new ();
+ session = soup_test_session_new (
+ SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_ASYNC_CONTEXT, async_context,
+ NULL);
+ g_main_context_unref (async_context);
+
+ uri = g_build_filename (base_uri, "slow", NULL);
+
+ debug_printf (1, " send_message\n");
+ msg = soup_message_new ("GET", uri);
+ soup_session_send_message (session, msg);
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " unexpected status: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_object_unref (msg);
+
+ debug_printf (1, " queue_message\n");
+ msg = soup_message_new ("GET", uri);
+ loop = g_main_loop_new (async_context, FALSE);
+ g_object_ref (msg);
+ soup_session_queue_message (session, msg, test1_finished, loop);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " unexpected status: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
+ g_free (uri);
+
+ g_cond_signal (test1_cond);
+ return NULL;
+}
+
+/* Test 2: An async session in the main thread with its own
+ * async_context runs independently of the default main loop.
+ */
+
+static gboolean idle_test2_fail (gpointer user_data);
+
+static void
+do_test2 (void)
+{
+ guint idle;
+ GMainContext *async_context;
+ SoupSession *session;
+ char *uri;
+ SoupMessage *msg;
+
+ debug_printf (1, "Test 2: a session with its own context is independent of the main loop.\n");
+
+ idle = g_idle_add_full (G_PRIORITY_HIGH, idle_test2_fail, NULL, NULL);
+
+ async_context = g_main_context_new ();
+ session = soup_test_session_new (
+ SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_ASYNC_CONTEXT, async_context,
+ NULL);
+ g_main_context_unref (async_context);
+
+ uri = g_build_filename (base_uri, "slow", NULL);
+
+ debug_printf (1, " send_message\n");
+ msg = soup_message_new ("GET", uri);
+ soup_session_send_message (session, msg);
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " unexpected status: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
+ g_free (uri);
+
+ g_source_remove (idle);
+}
+
+static gboolean
+idle_test2_fail (gpointer user_data)
+{
+ debug_printf (1, " idle ran!\n");
+ errors++;
+ return FALSE;
+}
+
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+
+ test_init (argc, argv, NULL);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ base_uri = g_strdup_printf ("http://127.0.0.1:%u/",
+ soup_server_get_port (server));
+
+ do_test1 ();
+ do_test2 ();
+
+ g_free (base_uri);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-auth-domain-basic.h>
+#include <libsoup/soup-server.h>
+
+#include "test-utils.h"
+
+#define SHORT_BODY "This is a test.\r\n"
+#define LONG_BODY (SHORT_BODY SHORT_BODY)
+
+#define MAX_POST_LENGTH (sizeof (SHORT_BODY))
+
+static int port;
+static GSList *events;
+
+static void
+event (SoupMessage *msg, const char *side, const char *message)
+{
+ char *data = g_strdup_printf ("%s-%s", side, message);
+ gboolean record_status =
+ (!strcmp (data, "server-wrote_headers") ||
+ !strcmp (data, "server-wrote_informational"));
+
+ debug_printf (2, " %s", data);
+ if (record_status)
+ debug_printf (2, " (%s)", msg->reason_phrase);
+ debug_printf (2, "\n");
+
+ events = g_slist_append (events, data);
+ if (record_status)
+ events = g_slist_append (events, GUINT_TO_POINTER (msg->status_code));
+}
+
+#define EVENT_HANDLER(name) \
+static void \
+name (SoupMessage *msg, gpointer side) \
+{ \
+ event (msg, side, #name); \
+}
+
+EVENT_HANDLER (got_informational)
+EVENT_HANDLER (got_headers)
+EVENT_HANDLER (got_body)
+EVENT_HANDLER (wrote_informational)
+EVENT_HANDLER (wrote_headers)
+EVENT_HANDLER (wrote_body)
+EVENT_HANDLER (finished)
+
+static void
+do_message (const char *path, gboolean long_body,
+ gboolean expect_continue, gboolean auth,
+ ...)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ const char *body;
+ char *uri;
+ va_list ap;
+ const char *expected_event;
+ char *actual_event;
+ int expected_status, actual_status;
+ static int count = 1;
+
+ debug_printf (1, "%d. /%s, %s body, %sExpect, %s password\n",
+ count++, path,
+ long_body ? "long" : "short",
+ expect_continue ? "" : "no ",
+ auth ? "with" : "without");
+
+ uri = g_strdup_printf ("http://%s127.0.0.1:%d/%s",
+ auth ? "user:pass@" : "",
+ port, path);
+ msg = soup_message_new ("POST", uri);
+ g_free (uri);
+
+ body = long_body ? LONG_BODY : SHORT_BODY;
+ soup_message_set_request (msg, "text/plain", SOUP_MEMORY_STATIC,
+ body, strlen (body));
+ soup_message_headers_append (msg->request_headers, "Connection", "close");
+ if (expect_continue) {
+ soup_message_headers_set_expectations (msg->request_headers,
+ SOUP_EXPECTATION_CONTINUE);
+ }
+
+ g_signal_connect (msg, "got_informational",
+ G_CALLBACK (got_informational), "client");
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (got_headers), "client");
+ g_signal_connect (msg, "got_body",
+ G_CALLBACK (got_body), "client");
+ g_signal_connect (msg, "wrote_informational",
+ G_CALLBACK (wrote_informational), "client");
+ g_signal_connect (msg, "wrote_headers",
+ G_CALLBACK (wrote_headers), "client");
+ g_signal_connect (msg, "wrote_body",
+ G_CALLBACK (wrote_body), "client");
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (finished), "client");
+
+ events = NULL;
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ soup_session_send_message (session, msg);
+ soup_test_session_abort_unref (session);
+
+ va_start (ap, auth);
+ while ((expected_event = va_arg (ap, const char *))) {
+
+ if (!events) {
+ actual_event = g_strdup ("");
+ debug_printf (1, " Expected '%s', got end of list\n",
+ expected_event);
+ errors++;
+ } else {
+ actual_event = events->data;
+ if (strcmp (expected_event, actual_event) != 0) {
+ debug_printf (1, " Expected '%s', got '%s'\n",
+ expected_event, actual_event);
+ errors++;
+ }
+ events = g_slist_delete_link (events, events);
+ }
+
+ if (!strcmp (expected_event, "server-wrote_headers") ||
+ !strcmp (expected_event, "server-wrote_informational"))
+ expected_status = va_arg (ap, int);
+ else
+ expected_status = -1;
+ if (!strcmp (actual_event, "server-wrote_headers") ||
+ !strcmp (actual_event, "server-wrote_informational")) {
+ actual_status = GPOINTER_TO_INT (events->data);
+ events = g_slist_delete_link (events, events);
+ } else
+ expected_status = -1;
+
+ if (expected_status != -1 && actual_status != -1 &&
+ expected_status != actual_status) {
+ debug_printf (1, " Expected status '%s', got '%s'\n",
+ soup_status_get_phrase (expected_status),
+ soup_status_get_phrase (actual_status));
+ errors++;
+ }
+
+ g_free (actual_event);
+ }
+ va_end (ap);
+ while (events) {
+ actual_event = events->data;
+ debug_printf (1, " Expected to be done, got '%s'\n", actual_event);
+ errors++;
+ events = g_slist_delete_link (events, events);
+
+ if (!strcmp (actual_event, "server-wrote_headers") ||
+ !strcmp (actual_event, "server-wrote_informational"))
+ events = g_slist_delete_link (events, events);
+ }
+ g_object_unref (msg);
+}
+
+static void
+run_tests (void)
+{
+ do_message ("unauth", FALSE, FALSE, FALSE,
+ "client-wrote_headers",
+ "client-wrote_body",
+ "server-got_headers",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_CREATED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("unauth", TRUE, FALSE, FALSE,
+ "client-wrote_headers",
+ "client-wrote_body",
+ "server-got_headers",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("unauth", FALSE, TRUE, FALSE,
+ "client-wrote_headers",
+ "server-got_headers",
+ "server-wrote_informational", SOUP_STATUS_CONTINUE,
+ "client-got_informational",
+ "client-wrote_body",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_CREATED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("unauth", TRUE, TRUE, FALSE,
+ "client-wrote_headers",
+ "server-got_headers",
+ "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+
+ do_message ("auth", FALSE, FALSE, FALSE,
+ "client-wrote_headers",
+ "client-wrote_body",
+ "server-got_headers",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("auth", TRUE, FALSE, FALSE,
+ "client-wrote_headers",
+ "client-wrote_body",
+ "server-got_headers",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("auth", FALSE, TRUE, FALSE,
+ "client-wrote_headers",
+ "server-got_headers",
+ "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("auth", TRUE, TRUE, FALSE,
+ "client-wrote_headers",
+ "server-got_headers",
+ "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+
+ do_message ("auth", FALSE, FALSE, TRUE,
+ "client-wrote_headers",
+ "client-wrote_body",
+ "server-got_headers",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-wrote_headers",
+ "client-wrote_body",
+ "server-got_headers",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_CREATED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("auth", TRUE, FALSE, TRUE,
+ "client-wrote_headers",
+ "client-wrote_body",
+ "server-got_headers",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-wrote_headers",
+ "client-wrote_body",
+ "server-got_headers",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("auth", FALSE, TRUE, TRUE,
+ "client-wrote_headers",
+ "server-got_headers",
+ "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-wrote_headers",
+ "server-got_headers",
+ "server-wrote_informational", SOUP_STATUS_CONTINUE,
+ "client-got_informational",
+ "client-wrote_body",
+ "server-got_body",
+ "server-wrote_headers", SOUP_STATUS_CREATED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+ do_message ("auth", TRUE, TRUE, TRUE,
+ "client-wrote_headers",
+ "server-got_headers",
+ "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-wrote_headers",
+ "server-got_headers",
+ "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
+ "server-wrote_body",
+ "server-finished",
+ "client-got_headers",
+ "client-got_body",
+ "client-finished",
+ NULL);
+}
+
+
+/* SERVER */
+
+static void
+server_got_headers (SoupMessage *msg, gpointer server)
+{
+ /* FIXME */
+ if (msg->status_code != SOUP_STATUS_CONTINUE &&
+ msg->status_code != 0)
+ return;
+
+ if (soup_message_headers_get_expectations (msg->request_headers) &
+ SOUP_EXPECTATION_CONTINUE) {
+ const char *length;
+
+ length = soup_message_headers_get_one (msg->request_headers,
+ "Content-Length");
+ if (length && atoi (length) > MAX_POST_LENGTH) {
+ soup_message_set_status (msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE);
+ soup_message_headers_append (msg->response_headers, "Connection", "close");
+ }
+ }
+}
+
+static void
+request_started (SoupServer *server, SoupMessage *msg,
+ SoupClientContext *client, gpointer user_data)
+{
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (server_got_headers), server);
+
+ g_signal_connect (msg, "got_informational",
+ G_CALLBACK (got_informational), "server");
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (got_headers), "server");
+ g_signal_connect (msg, "got_body",
+ G_CALLBACK (got_body), "server");
+ g_signal_connect (msg, "wrote_informational",
+ G_CALLBACK (wrote_informational), "server");
+ g_signal_connect (msg, "wrote_headers",
+ G_CALLBACK (wrote_headers), "server");
+ g_signal_connect (msg, "wrote_body",
+ G_CALLBACK (wrote_body), "server");
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (finished), "server");
+}
+
+static gboolean
+auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+ const char *username, const char *password, gpointer user_data)
+{
+ return !strcmp (username, "user") && !strcmp (password, "pass");
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ if (msg->method != SOUP_METHOD_POST) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ soup_message_headers_append (msg->response_headers, "Connection", "close");
+ } else if (msg->request_body->length > MAX_POST_LENGTH) {
+ soup_message_set_status (msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE);
+ soup_message_headers_append (msg->response_headers, "Connection", "close");
+ } else
+ soup_message_set_status (msg, SOUP_STATUS_CREATED);
+}
+
+static SoupServer *
+setup_server (void)
+{
+ SoupServer *server;
+ SoupAuthDomain *auth_domain;
+
+ server = soup_test_server_new (FALSE);
+
+ g_signal_connect (server, "request-started",
+ G_CALLBACK (request_started), NULL);
+
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+
+ auth_domain = soup_auth_domain_basic_new (
+ SOUP_AUTH_DOMAIN_REALM, "continue-test",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/auth",
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
+ NULL);
+ soup_server_add_auth_domain (server, auth_domain);
+ g_object_unref (auth_domain);
+
+ return server;
+}
+
+/* MAIN */
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+
+ test_init (argc, argv, NULL);
+
+ server = setup_server ();
+ port = soup_server_get_port (server);
+
+ run_tests ();
+
+ soup_test_server_quit_unref (server);
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ */
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+SoupServer *server;
+SoupURI *first_party_uri, *third_party_uri;
+const char *first_party = "http://127.0.0.1/";
+const char *third_party = "http://localhost/";
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ if (g_str_equal(path, "/index.html"))
+ soup_message_headers_replace (msg->response_headers,
+ "Set-Cookie",
+ "foo=bar");
+ else if (g_str_equal (path, "/foo.jpg"))
+ soup_message_headers_replace (msg->response_headers,
+ "Set-Cookie",
+ "baz=qux");
+ else
+ g_return_if_reached ();
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+typedef struct {
+ SoupCookieJarAcceptPolicy policy;
+ int n_cookies;
+} CookiesForPolicy;
+
+static const CookiesForPolicy validResults[] = {
+ { SOUP_COOKIE_JAR_ACCEPT_ALWAYS, 2 },
+ { SOUP_COOKIE_JAR_ACCEPT_NEVER, 0 },
+ { SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, 1 }
+};
+
+static void
+do_cookies_accept_policy_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupURI *uri;
+ SoupCookieJar *jar;
+ GSList *l, *p;
+ int i;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
+ jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR));
+
+ for (i = 0; i < G_N_ELEMENTS (validResults); i++) {
+ soup_cookie_jar_set_accept_policy (jar, validResults[i].policy);
+
+ uri = soup_uri_new_with_base (first_party_uri, "/index.html");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_message_set_first_party (msg, first_party_uri);
+ soup_session_send_message (session, msg);
+ soup_uri_free (uri);
+ g_object_unref (msg);
+
+ /* We can't use to servers due to limitations in
+ * test_server, so let's swap first and third party here
+ * to simulate a cookie coming from a third party.
+ */
+ uri = soup_uri_new_with_base (first_party_uri, "/foo.jpg");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_message_set_first_party (msg, third_party_uri);
+ soup_session_send_message (session, msg);
+ soup_uri_free (uri);
+ g_object_unref (msg);
+
+ l = soup_cookie_jar_all_cookies (jar);
+ if (g_slist_length (l) < validResults[i].n_cookies) {
+ debug_printf (1, " accepted less cookies than it should have\n");
+ errors++;
+ } else if (g_slist_length (l) > validResults[i].n_cookies) {
+ debug_printf (1, " accepted more cookies than it should have\n");
+ errors++;
+ }
+
+ for (p = l; p; p = p->next) {
+ soup_cookie_jar_delete_cookie (jar, p->data);
+ soup_cookie_free (p->data);
+ }
+
+ g_slist_free (l);
+ }
+
+ soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ test_init (argc, argv, NULL);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ first_party_uri = soup_uri_new (first_party);
+ third_party_uri = soup_uri_new (third_party);
+ soup_uri_set_port (first_party_uri, soup_server_get_port (server));
+ soup_uri_set_port (third_party_uri, soup_server_get_port (server));
+
+ do_cookies_accept_policy_test ();
+
+ soup_uri_free (first_party_uri);
+ soup_uri_free (third_party_uri);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005 Novell, Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <libsoup/soup-date.h>
+#include <glib.h>
+
+#include "test-utils.h"
+
+static gboolean check_ok (const char *strdate, SoupDate *date);
+
+static SoupDate *
+make_date (const char *strdate)
+{
+ char *dup;
+ SoupDate *date;
+
+ /* We do it this way so that if soup_date_new_from_string()
+ * reads off the end of the string, it will trigger an error
+ * when valgrinding, rather than just reading the start of the
+ * next const string.
+ */
+ dup = g_strdup (strdate);
+ date = soup_date_new_from_string (dup);
+ g_free (dup);
+ return date;
+}
+
+static const struct {
+ SoupDateFormat format;
+ const char *date;
+} good_dates[] = {
+ { SOUP_DATE_HTTP, "Sat, 06 Nov 2004 08:09:07 GMT" },
+ { SOUP_DATE_COOKIE, "Sat, 06-Nov-2004 08:09:07 GMT" },
+ { SOUP_DATE_RFC2822, "Sat, 6 Nov 2004 08:09:07 -0430" },
+ { SOUP_DATE_ISO8601_COMPACT, "20041106T080907" },
+ { SOUP_DATE_ISO8601_FULL, "2004-11-06T08:09:07" },
+ { SOUP_DATE_ISO8601_XMLRPC, "20041106T08:09:07" }
+};
+
+static void
+check_good (SoupDateFormat format, const char *strdate)
+{
+ SoupDate *date;
+ char *strdate2;
+
+ date = make_date (strdate);
+ if (date)
+ strdate2 = soup_date_to_string (date, format);
+ if (!check_ok (strdate, date))
+ return;
+
+ if (strcmp (strdate, strdate2) != 0) {
+ debug_printf (1, " restringification failed: '%s' -> '%s'\n",
+ strdate, strdate2);
+ errors++;
+ }
+ g_free (strdate2);
+}
+
+static const char *ok_dates[] = {
+ /* rfc1123-date, and broken variants */
+ "Sat, 06 Nov 2004 08:09:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 GMT",
+ "Sat, 06 Nov 2004 08:09:07",
+ "06 Nov 2004 08:09:07 GMT",
+ "SAT, 06 NOV 2004 08:09:07 +1000",
+
+ /* rfc850-date, and broken variants */
+ "Saturday, 06-Nov-04 08:09:07 GMT",
+ "Saturday, 6-Nov-04 08:09:07 GMT",
+ "Saturday, 6-Nov-04 08:09:07 GMT",
+ "Saturday, 06-Nov-104 08:09:07 GMT",
+ "Saturday, 06-Nov-04 08:09:07",
+ "06-Nov-04 08:09:07 GMT",
+
+ /* asctime-date, and broken variants */
+ "Sat Nov 6 08:09:07 2004",
+ "Sat Nov 06 08:09:07 2004",
+ "Sat Nov 6 08:09:07 2004",
+ "Sat Nov 6 08:09:07 2004 GMT",
+
+ /* ISO 8601 */
+ "2004-11-06T08:09:07Z",
+ "20041106T08:09:07Z",
+ "20041106T08:09:07+00:00",
+ "20041106T080907+00:00",
+
+ /* Netscape cookie spec date, and broken variants */
+ "Sat, 06-Nov-2004 08:09:07 GMT",
+ "Sat, 6-Nov-2004 08:09:07 GMT",
+ "Sat, 6-Nov-2004 08:09:07 GMT",
+ "Sat, 06-Nov-2004 08:09:07",
+
+ /* Original version of Netscape cookie spec, and broken variants */
+ "Sat, 06-Nov-04 08:09:07 GMT",
+ "Sat, 6-Nov-04 08:09:07 GMT",
+ "Sat, 6-Nov-04 08:09:07 GMT",
+ "Sat, 06-Nov-104 08:09:07 GMT",
+ "Sat, 06-Nov-04 08:09:07",
+
+ /* Netscape cookie spec example syntax, and broken variants */
+ "Saturday, 06-Nov-04 08:09:07 GMT",
+ "Saturday, 6-Nov-04 08:09:07 GMT",
+ "Saturday, 6-Nov-04 08:09:07 GMT",
+ "Saturday, 06-Nov-104 08:09:07 GMT",
+ "Saturday, 06-Nov-2004 08:09:07 GMT",
+ "Saturday, 6-Nov-2004 08:09:07 GMT",
+ "Saturday, 6-Nov-2004 08:09:07 GMT",
+ "Saturday, 06-Nov-04 08:09:07",
+
+ /* Miscellaneous broken formats seen on the web */
+ "Sat 06-Nov-2004 08:9:07",
+ "Saturday, 06-Nov-04 8:9:07 GMT",
+ "Sat, 06 Nov 2004 08:09:7 GMT"
+};
+
+#define TIME_T 1099728547L
+#define TIME_T_STRING "1099728547"
+
+static gboolean
+check_ok (const char *strdate, SoupDate *date)
+{
+ debug_printf (2, "%s\n", strdate);
+
+ if (date &&
+ date->year == 2004 && date->month == 11 && date->day == 6 &&
+ date->hour == 8 && date->minute == 9 && date->second == 7) {
+ soup_date_free (date);
+ return TRUE;
+ }
+
+ debug_printf (1, " date parsing failed for '%s'.\n", strdate);
+ if (date) {
+ debug_printf (1, " got: %d %d %d - %d %d %d\n\n",
+ date->year, date->month, date->day,
+ date->hour, date->minute, date->second);
+ soup_date_free (date);
+ }
+ errors++;
+ return FALSE;
+}
+
+static const char *bad_dates[] = {
+ /* broken rfc1123-date */
+ ", 06 Nov 2004 08:09:07 GMT",
+ "Sat, Nov 2004 08:09:07 GMT",
+ "Sat, 06 2004 08:09:07 GMT",
+ "Sat, 06 Nov 08:09:07 GMT",
+ "Sat, 06 Nov 2004 :09:07 GMT",
+ "Sat, 06 Nov 2004 09:07 GMT",
+ "Sat, 06 Nov 2004 08::07 GMT",
+ "Sat, 06 Nov 2004 08:09: GMT",
+
+ /* broken rfc850-date */
+ ", 06-Nov-04 08:09:07 GMT",
+ "Saturday, -Nov-04 08:09:07 GMT",
+ "Saturday, Nov-04 08:09:07 GMT",
+ "Saturday, 06-04 08:09:07 GMT",
+ "Saturday, 06--04 08:09:07 GMT",
+ "Saturday, 06-Nov- 08:09:07 GMT",
+ "Saturday, 06-Nov 08:09:07 GMT",
+ "Saturday, 06-Nov-04 :09:07 GMT",
+ "Saturday, 06-Nov-04 09:07 GMT",
+ "Saturday, 06-Nov-04 08::07 GMT",
+ "Saturday, 06-Nov-04 08:09: GMT",
+
+ /* broken asctime-date */
+ "Nov 6 08:09:07 2004",
+ "Sat 6 08:09:07 2004",
+ "Sat Nov 08:09:07 2004",
+ "Sat Nov 6 :09:07 2004",
+ "Sat Nov 6 09:07 2004",
+ "Sat Nov 6 08::07 2004",
+ "Sat Nov 6 08:09: 2004",
+ "Sat Nov 6 08:09:07",
+ "Sat Nov 6 08:09:07 GMT 2004"
+};
+
+static void
+check_bad (const char *strdate, SoupDate *date)
+{
+ debug_printf (2, "%s\n", strdate);
+
+ if (!date)
+ return;
+ errors++;
+
+ debug_printf (1, " date parsing succeeded for '%s'!\n", strdate);
+ debug_printf (1, " got: %d %d %d - %d %d %d\n\n",
+ date->year, date->month, date->day,
+ date->hour, date->minute, date->second);
+ soup_date_free (date);
+}
+
+static const struct conversion {
+ const char *source;
+ const char *http, *cookie, *rfc2822, *compact, *full, *xmlrpc;
+} conversions[] = {
+ /* SOUP_DATE_HTTP */
+ { "Sat, 06 Nov 2004 08:09:07 GMT",
+
+ "Sat, 06 Nov 2004 08:09:07 GMT",
+ "Sat, 06-Nov-2004 08:09:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 +0000",
+ "20041106T080907Z",
+ "2004-11-06T08:09:07Z",
+ "20041106T08:09:07" },
+
+ /* RFC2822 GMT */
+ { "Sat, 6 Nov 2004 08:09:07 +0000",
+
+ "Sat, 06 Nov 2004 08:09:07 GMT",
+ "Sat, 06-Nov-2004 08:09:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 +0000",
+ "20041106T080907Z",
+ "2004-11-06T08:09:07Z",
+ "20041106T08:09:07" },
+
+ /* RFC2822 with positive offset */
+ { "Sat, 6 Nov 2004 08:09:07 +0430",
+
+ "Sat, 06 Nov 2004 04:39:07 GMT",
+ "Sat, 06-Nov-2004 04:39:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 +0430",
+ "20041106T080907+0430",
+ "2004-11-06T08:09:07+04:30",
+ "20041106T08:09:07" },
+
+ /* RFC2822 with negative offset */
+ { "Sat, 6 Nov 2004 08:09:07 -0430",
+
+ "Sat, 06 Nov 2004 12:39:07 GMT",
+ "Sat, 06-Nov-2004 12:39:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 -0430",
+ "20041106T080907-0430",
+ "2004-11-06T08:09:07-04:30",
+ "20041106T08:09:07" },
+
+ /* RFC2822 floating */
+ { "Sat, 6 Nov 2004 08:09:07 -0000",
+
+ "Sat, 06 Nov 2004 08:09:07 GMT",
+ "Sat, 06-Nov-2004 08:09:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 -0000",
+ "20041106T080907",
+ "2004-11-06T08:09:07",
+ "20041106T08:09:07" },
+
+ /* ISO GMT */
+ { "2004-11-06T08:09:07Z",
+
+ "Sat, 06 Nov 2004 08:09:07 GMT",
+ "Sat, 06-Nov-2004 08:09:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 +0000",
+ "20041106T080907Z",
+ "2004-11-06T08:09:07Z",
+ "20041106T08:09:07" },
+
+ /* ISO with positive offset */
+ { "2004-11-06T08:09:07+04:30",
+
+ "Sat, 06 Nov 2004 04:39:07 GMT",
+ "Sat, 06-Nov-2004 04:39:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 +0430",
+ "20041106T080907+0430",
+ "2004-11-06T08:09:07+04:30",
+ "20041106T08:09:07" },
+
+ /* ISO with negative offset */
+ { "2004-11-06T08:09:07-04:30",
+
+ "Sat, 06 Nov 2004 12:39:07 GMT",
+ "Sat, 06-Nov-2004 12:39:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 -0430",
+ "20041106T080907-0430",
+ "2004-11-06T08:09:07-04:30",
+ "20041106T08:09:07" },
+
+ /* ISO floating */
+ { "2004-11-06T08:09:07",
+
+ "Sat, 06 Nov 2004 08:09:07 GMT",
+ "Sat, 06-Nov-2004 08:09:07 GMT",
+ "Sat, 6 Nov 2004 08:09:07 -0000",
+ "20041106T080907",
+ "2004-11-06T08:09:07",
+ "20041106T08:09:07" }
+};
+
+static void
+check_conversion (const struct conversion *conv)
+{
+ SoupDate *date;
+ char *str;
+
+ debug_printf (2, "%s\n", conv->source);
+ date = make_date (conv->source);
+ if (!date) {
+ debug_printf (1, " date parsing failed for '%s'.\n", conv->source);
+ errors++;
+ return;
+ }
+
+ str = soup_date_to_string (date, SOUP_DATE_HTTP);
+ if (!str || strcmp (str, conv->http) != 0) {
+ debug_printf (1, " conversion of '%s' to HTTP failed:\n"
+ " wanted: %s\n got: %s\n",
+ conv->source, conv->http, str ? str : "(null)");
+ errors++;
+ }
+ g_free (str);
+
+ str = soup_date_to_string (date, SOUP_DATE_COOKIE);
+ if (!str || strcmp (str, conv->cookie) != 0) {
+ debug_printf (1, " conversion of '%s' to COOKIE failed:\n"
+ " wanted: %s\n got: %s\n",
+ conv->source, conv->cookie, str ? str : "(null)");
+ errors++;
+ }
+ g_free (str);
+
+ str = soup_date_to_string (date, SOUP_DATE_RFC2822);
+ if (!str || strcmp (str, conv->rfc2822) != 0) {
+ debug_printf (1, " conversion of '%s' to RFC2822 failed:\n"
+ " wanted: %s\n got: %s\n",
+ conv->source, conv->rfc2822, str ? str : "(null)");
+ errors++;
+ }
+ g_free (str);
+
+ str = soup_date_to_string (date, SOUP_DATE_ISO8601_COMPACT);
+ if (!str || strcmp (str, conv->compact) != 0) {
+ debug_printf (1, " conversion of '%s' to COMPACT failed:\n"
+ " wanted: %s\n got: %s\n",
+ conv->source, conv->compact, str ? str : "(null)");
+ errors++;
+ }
+ g_free (str);
+
+ str = soup_date_to_string (date, SOUP_DATE_ISO8601_FULL);
+ if (!str || strcmp (str, conv->full) != 0) {
+ debug_printf (1, " conversion of '%s' to FULL failed:\n"
+ " wanted: %s\n got: %s\n",
+ conv->source, conv->full, str ? str : "(null)");
+ errors++;
+ }
+ g_free (str);
+
+ str = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
+ if (!str || strcmp (str, conv->xmlrpc) != 0) {
+ debug_printf (1, " conversion of '%s' to XMLRPC failed:\n"
+ " wanted: %s\n got: %s\n",
+ conv->source, conv->xmlrpc, str ? str : "(null)");
+ errors++;
+ }
+ g_free (str);
+
+ soup_date_free (date);
+}
+
+int
+main (int argc, char **argv)
+{
+ int i;
+
+ test_init (argc, argv, NULL);
+
+ debug_printf (1, "Good dates:\n");
+ for (i = 0; i < G_N_ELEMENTS (good_dates); i++)
+ check_good (good_dates[i].format, good_dates[i].date);
+
+ debug_printf (1, "\nOK dates:\n");
+ for (i = 0; i < G_N_ELEMENTS (ok_dates); i++)
+ check_ok (ok_dates[i], make_date (ok_dates[i]));
+ check_ok (TIME_T_STRING, soup_date_new_from_time_t (TIME_T));
+
+ debug_printf (1, "\nBad dates:\n");
+ for (i = 0; i < G_N_ELEMENTS (bad_dates); i++)
+ check_bad (bad_dates[i], make_date (bad_dates[i]));
+
+ debug_printf (1, "\nConversions:\n");
+ for (i = 0; i < G_N_ELEMENTS (conversions); i++)
+ check_conversion (&conversions[i] );
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libsoup/soup-address.h"
+
+static GMainLoop *loop;
+static int nlookups = 0;
+
+static void
+resolve_callback (SoupAddress *addr, guint status, gpointer data)
+{
+ if (status == SOUP_STATUS_OK) {
+ printf ("Name: %s\n", soup_address_get_name (addr));
+ printf ("Address: %s\n", soup_address_get_physical (addr));
+ } else {
+ printf ("Name: %s\n", soup_address_get_name (addr));
+ printf ("Error: %s\n", soup_status_get_phrase (status));
+ }
+ printf ("\n");
+
+ g_object_unref (addr);
+
+ nlookups--;
+ if (nlookups == 0)
+ g_main_loop_quit (loop);
+}
+
+static void
+usage (void)
+{
+ fprintf (stderr, "Usage: dns hostname ...\n");
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupAddress *addr;
+ int i;
+
+ if (argc < 2)
+ usage ();
+
+ g_thread_init (NULL);
+ g_type_init ();
+
+ for (i = 1; i < argc; i++) {
+ addr = soup_address_new (argv[i], 0);
+ if (!addr) {
+ fprintf (stderr, "Could not parse address %s\n", argv[1]);
+ exit (1);
+ }
+
+ soup_address_resolve_async (addr, NULL, NULL,
+ resolve_callback, NULL);
+ nlookups++;
+ }
+
+ loop = g_main_loop_new (NULL, TRUE);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ return 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007, 2008 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+static struct {
+ const char *title, *name;
+ const char *result;
+} tests[] = {
+ /* Both fields must be filled in */
+ { NULL, "Name", "" },
+ { "Mr.", NULL, "" },
+
+ /* Filled-in but empty is OK */
+ { "", "", "Hello, " },
+ { "", "Name", "Hello, Name" },
+ { "Mr.", "", "Hello, MR. " },
+
+ /* Simple */
+ { "Mr.", "Name", "Hello, MR. Name" },
+
+ /* Encoding of spaces */
+ { "Mr.", "Full Name", "Hello, MR. Full Name" },
+ { "Mr. and Mrs.", "Full Name", "Hello, MR. AND MRS. Full Name" },
+
+ /* Encoding of "+" */
+ { "Mr.+Mrs.", "Full Name", "Hello, MR.+MRS. Full Name" },
+
+ /* Encoding of non-ASCII. */
+ { "Se\xC3\xB1or", "Nombre", "Hello, SE\xC3\xB1OR Nombre" },
+
+ /* Encoding of '%' */
+ { "Mr.", "Foo %2f Bar", "Hello, MR. Foo %2f Bar" },
+};
+
+static void
+do_hello_test (int n, gboolean extra, const char *uri)
+{
+ GPtrArray *args;
+ char *title_arg = NULL, *name_arg = NULL;
+ char *str_stdout = NULL;
+
+ debug_printf (1, "%2d. '%s' '%s'%s: ", n * 2 + (extra ? 2 : 1),
+ tests[n].title ? tests[n].title : "(null)",
+ tests[n].name ? tests[n].name : "(null)",
+ extra ? " + extra" : "");
+
+ args = g_ptr_array_new ();
+ g_ptr_array_add (args, "curl");
+ g_ptr_array_add (args, "-G");
+ if (tests[n].title) {
+ title_arg = soup_form_encode ("title", tests[n].title, NULL);
+ g_ptr_array_add (args, "-d");
+ g_ptr_array_add (args, title_arg);
+ }
+ if (tests[n].name) {
+ name_arg = soup_form_encode ("n@me", tests[n].name, NULL);
+ g_ptr_array_add (args, "-d");
+ g_ptr_array_add (args, name_arg);
+ }
+ if (extra) {
+ g_ptr_array_add (args, "-d");
+ g_ptr_array_add (args, "extra=something");
+ }
+ g_ptr_array_add (args, (char *)uri);
+ g_ptr_array_add (args, NULL);
+
+ if (g_spawn_sync (NULL, (char **)args->pdata, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL,
+ &str_stdout, NULL, NULL, NULL)) {
+ if (str_stdout && !strcmp (str_stdout, tests[n].result))
+ debug_printf (1, "OK!\n");
+ else {
+ debug_printf (1, "WRONG!\n");
+ debug_printf (1, " expected '%s', got '%s'\n",
+ tests[n].result,
+ str_stdout ? str_stdout : "(error)");
+ errors++;
+ }
+ g_free (str_stdout);
+ } else {
+ debug_printf (1, "ERROR!\n");
+ errors++;
+ }
+ g_ptr_array_free (args, TRUE);
+ g_free (title_arg);
+ g_free (name_arg);
+}
+
+static void
+do_hello_tests (const char *uri)
+{
+ int n;
+
+ debug_printf (1, "Hello tests (GET, application/x-www-form-urlencoded)\n");
+ for (n = 0; n < G_N_ELEMENTS (tests); n++) {
+ do_hello_test (n, FALSE, uri);
+ do_hello_test (n, TRUE, uri);
+ }
+}
+
+static void
+do_md5_test_curl (const char *uri, const char *file, const char *md5)
+{
+ GPtrArray *args;
+ char *file_arg, *str_stdout;
+
+ debug_printf (1, " via curl: ");
+
+ args = g_ptr_array_new ();
+ g_ptr_array_add (args, "curl");
+ g_ptr_array_add (args, "-L");
+ g_ptr_array_add (args, "-F");
+ file_arg = g_strdup_printf ("file=@%s", file);
+ g_ptr_array_add (args, file_arg);
+ g_ptr_array_add (args, "-F");
+ g_ptr_array_add (args, "fmt=txt");
+ g_ptr_array_add (args, (char *)uri);
+ g_ptr_array_add (args, NULL);
+
+ if (g_spawn_sync (NULL, (char **)args->pdata, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL,
+ &str_stdout, NULL, NULL, NULL)) {
+ if (str_stdout && !strcmp (str_stdout, md5))
+ debug_printf (1, "OK!\n");
+ else {
+ debug_printf (1, "WRONG!\n");
+ debug_printf (1, " expected '%s', got '%s'\n",
+ md5, str_stdout ? str_stdout : "(error)");
+ errors++;
+ }
+ g_free (str_stdout);
+ } else {
+ debug_printf (1, "ERROR!\n");
+ errors++;
+ }
+ g_ptr_array_free (args, TRUE);
+ g_free (file_arg);
+}
+
+#define MD5_TEST_FILE SRCDIR "/resources/home.gif"
+#define MD5_TEST_FILE_BASENAME "home.gif"
+#define MD5_TEST_FILE_MIME_TYPE "image/gif"
+
+static void
+do_md5_test_libsoup (const char *uri, const char *contents,
+ gsize length, const char *md5)
+{
+ SoupMultipart *multipart;
+ SoupBuffer *buffer;
+ SoupMessage *msg;
+ SoupSession *session;
+
+ debug_printf (1, " via libsoup: ");
+
+ multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
+ buffer = soup_buffer_new (SOUP_MEMORY_COPY, contents, length);
+ soup_multipart_append_form_file (multipart, "file",
+ MD5_TEST_FILE_BASENAME,
+ MD5_TEST_FILE_MIME_TYPE,
+ buffer);
+ soup_buffer_free (buffer);
+ soup_multipart_append_form_string (multipart, "fmt", "text");
+
+ msg = soup_form_request_new_from_multipart (uri, multipart);
+ soup_multipart_free (multipart);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ soup_session_send_message (session, msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, "ERROR: Unexpected status %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ } else if (strcmp (msg->response_body->data, md5) != 0) {
+ debug_printf (1, "ERROR: Incorrect response: expected '%s' got '%s'\n",
+ md5, msg->response_body->data);
+ errors++;
+ } else
+ debug_printf (1, "OK!\n");
+
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_md5_tests (const char *uri)
+{
+ char *contents, *md5;
+ gsize length;
+ GError *error = NULL;
+
+ debug_printf (1, "\nMD5 tests (POST, multipart/form-data)\n");
+
+ if (!g_file_get_contents (MD5_TEST_FILE, &contents, &length, &error)) {
+ debug_printf (1, " ERROR: Could not read " MD5_TEST_FILE ": %s\n", error->message);
+ g_error_free (error);
+ errors++;
+ return;
+ }
+
+ md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, contents, length);
+
+ do_md5_test_curl (uri, MD5_TEST_FILE, md5);
+ do_md5_test_libsoup (uri, contents, length, md5);
+
+ g_free (contents);
+ g_free (md5);
+}
+
+
+static void
+do_form_decode_test (void)
+{
+ GHashTable *table;
+ const gchar *value;
+ gchar *tmp;
+
+ debug_printf (1, "\nDecode tests\n");
+
+ /* Test that the code handles multiple values with the same key. */
+ table = soup_form_decode ("foo=first&foo=second&foo=third");
+
+ /* Allocate some memory. We do this to test for a bug in
+ * soup_form_decode() that resulted in values from the hash
+ * table pointing to memory that is already released.
+ */
+ tmp = g_strdup ("other");
+
+ value = g_hash_table_lookup (table, "foo");
+ if (g_strcmp0 (value, "third") != 0) {
+ debug_printf (1, " ERROR: expected '%s', got '%s'\n",
+ "third", value ? value : "(null)");
+ errors++;
+ }
+
+ g_free (tmp);
+ g_hash_table_destroy (table);
+}
+
+static void
+hello_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ char *title, *name, *fmt;
+ const char *content_type;
+ GString *buf;
+
+ if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (query) {
+ title = g_hash_table_lookup (query, "title");
+ name = g_hash_table_lookup (query, "n@me");
+ fmt = g_hash_table_lookup (query, "fmt");
+ } else
+ title = name = fmt = NULL;
+
+ buf = g_string_new (NULL);
+ if (!query || (fmt && !strcmp (fmt, "html"))) {
+ content_type = "text/html";
+ g_string_append (buf, "<html><head><title>forms-test: hello</title></head><body>\r\n");
+ if (title && name) {
+ /* mumble mumble html-escape... */
+ g_string_append_printf (buf, "<p>Hello, <b><em>%s</em> %s</b></p>\r\n",
+ title, name);
+ }
+ g_string_append (buf, "<form action='/hello' method='get'>"
+ "<p>Title: <input name='title'></p>"
+ "<p>Name: <input name='n@me'></p>"
+ "<p><input type=hidden name='fmt' value='html'></p>"
+ "<p><input type=submit></p>"
+ "</form>\r\n");
+ g_string_append (buf, "</body></html>\r\n");
+ } else {
+ content_type = "text/plain";
+ if (title && name) {
+ char *uptitle = g_ascii_strup (title, -1);
+ g_string_append_printf (buf, "Hello, %s %s",
+ uptitle, name);
+ g_free (uptitle);
+ }
+ }
+
+ soup_message_set_response (msg, content_type,
+ SOUP_MEMORY_TAKE,
+ buf->str, buf->len);
+ g_string_free (buf, FALSE);
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static void
+md5_get_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ const char *file = NULL, *md5sum = NULL, *fmt;
+ const char *content_type;
+ GString *buf;
+
+ if (query) {
+ file = g_hash_table_lookup (query, "file");
+ md5sum = g_hash_table_lookup (query, "md5sum");
+ fmt = g_hash_table_lookup (query, "fmt");
+ } else
+ fmt = "html";
+
+ buf = g_string_new (NULL);
+ if (!strcmp (fmt, "html")) {
+ content_type = "text/html";
+ g_string_append (buf, "<html><head><title>forms-test: md5</title></head><body>\r\n");
+ if (file && md5sum) {
+ /* mumble mumble html-escape... */
+ g_string_append_printf (buf, "<p>File: %s<br>MD5: <b>%s</b></p>\r\n",
+ file, md5sum);
+ }
+ g_string_append (buf, "<form action='/md5' method='post' enctype='multipart/form-data'>"
+ "<p>File: <input type='file' name='file'></p>"
+ "<p><input type=hidden name='fmt' value='html'></p>"
+ "<p><input type=submit></p>"
+ "</form>\r\n");
+ g_string_append (buf, "</body></html>\r\n");
+ } else {
+ content_type = "text/plain";
+ if (md5sum)
+ g_string_append_printf (buf, "%s", md5sum);
+ }
+
+ soup_message_set_response (msg, content_type,
+ SOUP_MEMORY_TAKE,
+ buf->str, buf->len);
+ g_string_free (buf, FALSE);
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static void
+md5_post_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ const char *content_type;
+ GHashTable *params;
+ const char *fmt;
+ char *filename, *md5sum, *redirect_uri;
+ SoupBuffer *file;
+ SoupURI *uri;
+
+ content_type = soup_message_headers_get_content_type (msg->request_headers, NULL);
+ if (!content_type || strcmp (content_type, "multipart/form-data") != 0) {
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+ return;
+ }
+
+ params = soup_form_decode_multipart (msg, "file",
+ &filename, NULL, &file);
+ if (!params) {
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+ return;
+ }
+ fmt = g_hash_table_lookup (params, "fmt");
+
+ md5sum = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+ (gpointer)file->data,
+ file->length);
+ soup_buffer_free (file);
+
+ uri = soup_uri_copy (soup_message_get_uri (msg));
+ soup_uri_set_query_from_fields (uri,
+ "file", filename ? filename : "",
+ "md5sum", md5sum,
+ "fmt", fmt ? fmt : "html",
+ NULL);
+ redirect_uri = soup_uri_to_string (uri, FALSE);
+
+ soup_message_set_status (msg, SOUP_STATUS_SEE_OTHER);
+ soup_message_headers_replace (msg->response_headers, "Location",
+ redirect_uri);
+
+ g_free (redirect_uri);
+ soup_uri_free (uri);
+ g_free (md5sum);
+ g_free (filename);
+ g_hash_table_destroy (params);
+}
+
+static void
+md5_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
+ md5_get_callback (server, msg, path, query, context, data);
+ else if (msg->method == SOUP_METHOD_POST)
+ md5_post_callback (server, msg, path, query, context, data);
+ else
+ soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+}
+
+static gboolean run_tests = TRUE;
+
+static GOptionEntry no_test_entry[] = {
+ { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &run_tests,
+ "Don't run tests, just run the test server", NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ SoupServer *server;
+ guint port;
+ char *uri_str;
+
+ test_init (argc, argv, no_test_entry);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, "/hello",
+ hello_callback, NULL, NULL);
+ soup_server_add_handler (server, "/md5",
+ md5_callback, NULL, NULL);
+ port = soup_server_get_port (server);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ if (run_tests) {
+ uri_str = g_strdup_printf ("http://127.0.0.1:%u/hello", port);
+ do_hello_tests (uri_str);
+ g_free (uri_str);
+
+ uri_str = g_strdup_printf ("http://127.0.0.1:%u/md5", port);
+ do_md5_tests (uri_str);
+ g_free (uri_str);
+
+ do_form_decode_test ();
+ } else {
+ printf ("Listening on port %d\n", port);
+ g_main_loop_run (loop);
+ }
+
+ g_main_loop_unref (loop);
+
+ soup_test_server_quit_unref (server);
+ if (run_tests)
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef HAVE_GNOME
+#include <libsoup/soup-gnome.h>
+#else
+#include <libsoup/soup.h>
+#endif
+
+static SoupSession *session;
+static GMainLoop *loop;
+static gboolean debug = FALSE, quiet = FALSE;
+static const char *method;
+
+static void
+get_url (const char *url)
+{
+ const char *name;
+ SoupMessage *msg;
+ const char *header;
+
+ msg = soup_message_new (method, url);
+ soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
+
+ soup_session_send_message (session, msg);
+
+ name = soup_message_get_uri (msg)->path;
+
+ if (debug) {
+ SoupMessageHeadersIter iter;
+ const char *hname, *value;
+ char *path = soup_uri_to_string (soup_message_get_uri (msg), TRUE);
+
+ printf ("%s %s HTTP/1.%d\n", method, path,
+ soup_message_get_http_version (msg));
+ soup_message_headers_iter_init (&iter, msg->request_headers);
+ while (soup_message_headers_iter_next (&iter, &hname, &value))
+ printf ("%s: %s\r\n", hname, value);
+ printf ("\n");
+
+ printf ("HTTP/1.%d %d %s\n",
+ soup_message_get_http_version (msg),
+ msg->status_code, msg->reason_phrase);
+
+ soup_message_headers_iter_init (&iter, msg->response_headers);
+ while (soup_message_headers_iter_next (&iter, &hname, &value))
+ printf ("%s: %s\r\n", hname, value);
+ printf ("\n");
+ } else if (!quiet || SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
+ printf ("%s: %d %s\n", name, msg->status_code, msg->reason_phrase);
+
+ if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
+ header = soup_message_headers_get_one (msg->response_headers,
+ "Location");
+ if (header) {
+ SoupURI *uri;
+ char *uri_string;
+
+ if (!debug && !quiet)
+ printf (" -> %s\n", header);
+
+ uri = soup_uri_new_with_base (soup_message_get_uri (msg), header);
+ uri_string = soup_uri_to_string (uri, FALSE);
+ get_url (uri_string);
+ g_free (uri_string);
+ soup_uri_free (uri);
+ }
+ } else if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ fwrite (msg->response_body->data, 1,
+ msg->response_body->length, stdout);
+ }
+}
+
+static void
+usage (void)
+{
+ fprintf (stderr, "Usage: get [-c CAfile] [-p proxy URL] [-h] [-d] URL\n");
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ const char *cafile = NULL, *url;
+ SoupURI *proxy = NULL, *parsed;
+ gboolean synchronous = FALSE;
+ int opt;
+
+ g_thread_init (NULL);
+ g_type_init ();
+
+ method = SOUP_METHOD_GET;
+
+ while ((opt = getopt (argc, argv, "c:dhp:qs")) != -1) {
+ switch (opt) {
+ case 'c':
+ cafile = optarg;
+ break;
+
+ case 'd':
+ debug = TRUE;
+ break;
+
+ case 'h':
+ method = SOUP_METHOD_HEAD;
+ debug = TRUE;
+ break;
+
+ case 'p':
+ proxy = soup_uri_new (optarg);
+ if (!proxy) {
+ fprintf (stderr, "Could not parse %s as URI\n",
+ optarg);
+ exit (1);
+ }
+ break;
+
+ case 'q':
+ quiet = TRUE;
+ break;
+
+ case 's':
+ synchronous = TRUE;
+ break;
+
+ case '?':
+ usage ();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage ();
+ url = argv[0];
+ parsed = soup_uri_new (url);
+ if (!parsed) {
+ fprintf (stderr, "Could not parse '%s' as a URL\n", url);
+ exit (1);
+ }
+ soup_uri_free (parsed);
+
+ if (synchronous) {
+ session = soup_session_sync_new_with_options (
+ SOUP_SESSION_SSL_CA_FILE, cafile,
+#ifdef HAVE_GNOME
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+#endif
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+ SOUP_SESSION_USER_AGENT, "get ",
+ SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
+ NULL);
+ } else {
+ session = soup_session_async_new_with_options (
+ SOUP_SESSION_SSL_CA_FILE, cafile,
+#ifdef HAVE_GNOME
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+#endif
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+ SOUP_SESSION_USER_AGENT, "get ",
+ SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
+ NULL);
+ }
+
+ /* Need to do this after creating the session, since adding
+ * SOUP_TYPE_GNOME_FEATURE_2_26 will add a proxy resolver, thereby
+ * bashing over the manually-set proxy.
+ */
+ if (proxy) {
+ g_object_set (G_OBJECT (session),
+ SOUP_SESSION_PROXY_URI, proxy,
+ NULL);
+ }
+
+ if (!synchronous)
+ loop = g_main_loop_new (NULL, TRUE);
+
+ get_url (url);
+
+ if (!synchronous)
+ g_main_loop_unref (loop);
+
+ return 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <libsoup/soup.h>
+
+static GMainLoop *loop;
+
+static void
+print_value (GValue *value)
+{
+ if (G_VALUE_HOLDS_STRING (value))
+ printf ("%s", g_value_get_string (value));
+ else if (G_VALUE_HOLDS_INT (value))
+ printf ("%d", g_value_get_int (value));
+ else if (G_VALUE_HOLDS_DOUBLE (value))
+ printf ("%f", g_value_get_double (value));
+ else if (G_VALUE_TYPE (value) == G_TYPE_VALUE_ARRAY) {
+ GValueArray *array = g_value_get_boxed (value);
+ int i;
+ printf ("[ ");
+ for (i = 0; i < array->n_values; i++) {
+ if (i != 0)
+ printf (", ");
+ print_value (&array->values[i]);
+ }
+ printf (" ]");
+ } else
+ printf ("(%s)", g_type_name (G_VALUE_TYPE (value)));
+}
+
+static void
+print_struct_field (gpointer key, gpointer value, gpointer data)
+{
+ printf ("%s: ", (char *)key);
+ print_value (value);
+ printf ("\n");
+}
+
+static void
+got_response (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ GHashTable *hash;
+ GError *error = NULL;
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ fprintf (stderr, "%d %s\n", msg->status_code, msg->reason_phrase);
+ exit (1);
+ }
+
+ if (!soup_xmlrpc_extract_method_response (msg->response_body->data,
+ msg->response_body->length,
+ &error,
+ G_TYPE_HASH_TABLE, &hash)) {
+ if (!error) {
+ fprintf (stderr, "Could not parse XMLRPC response:\n%d %s\n\n",
+ msg->status_code, msg->reason_phrase);
+ fprintf (stderr, "%s\n", msg->response_body->data);
+ } else {
+ fprintf (stderr, "XML-RPC error: %d %s",
+ error->code, error->message);
+ }
+ exit (1);
+ }
+
+ g_hash_table_foreach (hash, print_struct_field, NULL);
+ g_hash_table_destroy (hash);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+usage (void)
+{
+ fprintf (stderr, "Usage: getbug [-p proxy_uri] [bugzilla-uri] bug-number\n");
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupSession *session;
+ SoupURI *proxy = NULL;
+ SoupMessage *msg;
+ const char *uri = "http://bugzilla.redhat.com/bugzilla/xmlrpc.cgi";
+ int opt, bug;
+
+ g_thread_init (NULL);
+ g_type_init ();
+
+ while ((opt = getopt (argc, argv, "p:")) != -1) {
+ switch (opt) {
+ case 'p':
+ proxy = soup_uri_new (optarg);
+ if (!proxy) {
+ fprintf (stderr, "Could not parse %s as URI\n",
+ optarg);
+ exit (1);
+ }
+ break;
+
+ case '?':
+ usage ();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1) {
+ uri = argv[0];
+ argc--;
+ argv++;
+ }
+
+ if (argc != 1 || (bug = atoi (argv[0])) == 0)
+ usage ();
+
+ session = soup_session_async_new_with_options (
+ SOUP_SESSION_PROXY_URI, proxy,
+ NULL);
+
+ msg = soup_xmlrpc_request_new (uri, "bugzilla.getBug",
+ G_TYPE_INT, bug,
+ G_TYPE_INVALID);
+ if (!msg) {
+ fprintf (stderr, "Could not create web service request to '%s'\n", uri);
+ exit (1);
+ }
+ soup_session_queue_message (session, SOUP_MESSAGE (msg),
+ got_response, NULL);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ return 0;
+}
--- /dev/null
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+typedef struct {
+ const char *name, *value;
+} Header;
+
+static struct RequestTest {
+ const char *description;
+ const char *request;
+ int length;
+ guint status;
+ const char *method, *path;
+ SoupHTTPVersion version;
+ Header headers[4];
+} reqtests[] = {
+ /**********************/
+ /*** VALID REQUESTS ***/
+ /**********************/
+
+ { "HTTP 1.0 request with no headers",
+ "GET / HTTP/1.0\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_0,
+ { { NULL } }
+ },
+
+ { "Req w/ 1 header",
+ "GET / HTTP/1.1\r\nHost: example.com\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 1 header, no leading whitespace",
+ "GET / HTTP/1.1\r\nHost:example.com\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 1 header including trailing whitespace",
+ "GET / HTTP/1.1\r\nHost: example.com \r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 1 header, wrapped",
+ "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Foo", "bar baz" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 1 header, wrapped with additional whitespace",
+ "GET / HTTP/1.1\r\nFoo: bar \r\n baz\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Foo", "bar baz" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 1 header, wrapped with tab",
+ "GET / HTTP/1.1\r\nFoo: bar\r\n\tbaz\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Foo", "bar baz" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 1 header, wrapped before value",
+ "GET / HTTP/1.1\r\nFoo:\r\n bar baz\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Foo", "bar baz" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 1 header with empty value",
+ "GET / HTTP/1.1\r\nHost:\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 2 headers",
+ "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { "Connection", "close" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 3 headers",
+ "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\nBlah: blah\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { "Connection", "close" },
+ { "Blah", "blah" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 3 headers, 1st wrapped",
+ "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\nConnection: close\r\nBlah: blah\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Foo", "bar baz" },
+ { "Connection", "close" },
+ { "Blah", "blah" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 3 headers, 2nd wrapped",
+ "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Connection", "close" },
+ { "Blah", "blah" },
+ { "Foo", "bar baz" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ 3 headers, 3rd wrapped",
+ "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Connection", "close" },
+ { "Blah", "blah" },
+ { "Foo", "bar baz" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ same header multiple times",
+ "GET / HTTP/1.1\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Foo", "bar, baz, quux" },
+ { NULL }
+ }
+ },
+
+ { "Connection header on HTTP/1.0 message",
+ "GET / HTTP/1.0\r\nFoo: bar\r\nConnection: Bar, Quux\r\nBar: baz\r\nQuux: foo\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_0,
+ { { "Foo", "bar" },
+ { "Connection", "Bar, Quux" },
+ { NULL }
+ }
+ },
+
+ /****************************/
+ /*** RECOVERABLE REQUESTS ***/
+ /****************************/
+
+ /* RFC 2616 section 4.1 says we SHOULD accept this */
+
+ { "Spurious leading CRLF",
+ "\r\nGET / HTTP/1.1\r\nHost: example.com\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { NULL }
+ }
+ },
+
+ /* RFC 2616 section 3.1 says we MUST accept this */
+
+ { "HTTP/01.01 request",
+ "GET / HTTP/01.01\r\nHost: example.com\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { NULL }
+ }
+ },
+
+ /* RFC 2616 section 19.3 says we SHOULD accept these */
+
+ { "LF instead of CRLF after header",
+ "GET / HTTP/1.1\nHost: example.com\nConnection: close\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { "Connection", "close" },
+ { NULL }
+ }
+ },
+
+ { "LF instead of CRLF after Request-Line",
+ "GET / HTTP/1.1\nHost: example.com\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ incorrect whitespace in Request-Line",
+ "GET /\tHTTP/1.1\r\nHost: example.com\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { NULL }
+ }
+ },
+
+ { "Req w/ incorrect whitespace after Request-Line",
+ "GET / HTTP/1.1 \r\nHost: example.com\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { NULL }
+ }
+ },
+
+ /* qv bug 579318, do_bad_header_tests() below */
+ { "Req w/ mangled header",
+ "GET / HTTP/1.1\r\nHost: example.com\r\nFoo one\r\nBar: two\r\n", -1,
+ SOUP_STATUS_OK,
+ "GET", "/", SOUP_HTTP_1_1,
+ { { "Host", "example.com" },
+ { "Bar", "two" },
+ { NULL }
+ }
+ },
+
+ /************************/
+ /*** INVALID REQUESTS ***/
+ /************************/
+
+ { "HTTP 0.9 request; not supported",
+ "GET /\r\n", -1,
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "HTTP 1.2 request (no such thing)",
+ "GET / HTTP/1.2\r\n", -1,
+ SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "HTTP 2000 request (no such thing)",
+ "GET / HTTP/2000.0\r\n", -1,
+ SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "Non-HTTP request",
+ "GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "Junk after Request-Line",
+ "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1,
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "NUL in Method",
+ "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37,
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "NUL in Path",
+ "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38,
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "NUL in Header",
+ "GET / HTTP/1.1\r\nHost: example\x00com\r\n", 37,
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "No terminating CRLF",
+ "GET / HTTP/1.1\r\nHost: example.com", -1,
+ SOUP_STATUS_BAD_REQUEST,
+ NULL, NULL, -1,
+ { { NULL } }
+ },
+
+ { "Unrecognized expectation",
+ "GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1,
+ SOUP_STATUS_EXPECTATION_FAILED,
+ NULL, NULL, -1,
+ { { NULL } }
+ }
+};
+static const int num_reqtests = G_N_ELEMENTS (reqtests);
+
+static struct ResponseTest {
+ const char *description;
+ const char *response;
+ int length;
+ SoupHTTPVersion version;
+ guint status_code;
+ const char *reason_phrase;
+ Header headers[4];
+} resptests[] = {
+ /***********************/
+ /*** VALID RESPONSES ***/
+ /***********************/
+
+ { "HTTP 1.0 response w/ no headers",
+ "HTTP/1.0 200 ok\r\n", -1,
+ SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
+ { { NULL } }
+ },
+
+ { "HTTP 1.1 response w/ no headers",
+ "HTTP/1.1 200 ok\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { NULL } }
+ },
+
+ { "Response w/ multi-word Reason-Phrase",
+ "HTTP/1.1 400 bad request\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_BAD_REQUEST, "bad request",
+ { { NULL } }
+ },
+
+ { "Response w/ 1 header",
+ "HTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar" },
+ { NULL }
+ }
+ },
+
+ { "Response w/ 2 headers",
+ "HTTP/1.1 200 ok\r\nFoo: bar\r\nBaz: quux\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar" },
+ { "Baz", "quux" },
+ { NULL }
+ }
+ },
+
+ { "Response w/ same header multiple times",
+ "HTTP/1.1 200 ok\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar, baz, quux" },
+ { NULL }
+ }
+ },
+
+ { "Response w/ no reason phrase",
+ "HTTP/1.1 200 \r\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
+ { { "Foo", "bar" },
+ { NULL }
+ }
+ },
+
+ { "Connection header on HTTP/1.0 message",
+ "HTTP/1.0 200 ok\r\nFoo: bar\r\nConnection: Bar\r\nBar: quux\r\n", -1,
+ SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar" },
+ { "Connection", "Bar" },
+ { NULL }
+ }
+ },
+
+ /*****************************/
+ /*** RECOVERABLE RESPONSES ***/
+ /*****************************/
+
+ /* RFC 2616 section 3.1 says we MUST accept this */
+
+ { "HTTP/01.01 response",
+ "HTTP/01.01 200 ok\r\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar" },
+ { NULL }
+ }
+ },
+
+ /* RFC 2616 section 19.3 says we SHOULD accept these */
+
+ { "Response w/ LF instead of CRLF after Status-Line",
+ "HTTP/1.1 200 ok\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar" },
+ { NULL }
+ }
+ },
+
+ { "Response w/ incorrect spacing in Status-Line",
+ "HTTP/1.1 200\tok\r\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar" },
+ { NULL }
+ }
+ },
+
+ { "Response w/ no reason phrase or preceding SP",
+ "HTTP/1.1 200\r\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
+ { { "Foo", "bar" },
+ { NULL }
+ }
+ },
+
+ { "Response w/ no whitespace after status code",
+ "HTTP/1.1 200ok\r\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar" },
+ { NULL }
+ }
+ },
+
+ /* Shoutcast support */
+ { "Shoutcast server not-quite-HTTP",
+ "ICY 200 OK\r\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_0, SOUP_STATUS_OK, "OK",
+ { { "Foo", "bar" },
+ { NULL }
+ }
+ },
+
+ /* qv bug 579318, do_bad_header_tests() below */
+ { "Response w/ mangled header",
+ "HTTP/1.1 200 ok\r\nFoo: one\r\nBar two:2\r\nBaz: three\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "one" },
+ { "Baz", "three" },
+ { NULL }
+ }
+ },
+
+ /* qv bug 602863 */
+ { "HTTP 1.1 response with leading line break",
+ "\nHTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
+ SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
+ { { "Foo", "bar" },
+ { NULL } }
+ },
+
+ /*************************/
+ /*** INVALID RESPONSES ***/
+ /*************************/
+
+ { "Invalid HTTP version",
+ "HTTP/1.2 200 OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "Non-HTTP response",
+ "SOUP/1.1 200 OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "Non-numeric status code",
+ "HTTP/1.1 XXX OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "No status code",
+ "HTTP/1.1 OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "One-digit status code",
+ "HTTP/1.1 2 OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "Two-digit status code",
+ "HTTP/1.1 20 OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "Four-digit status code",
+ "HTTP/1.1 2000 OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "Status code < 100",
+ "HTTP/1.1 001 OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "Status code > 599",
+ "HTTP/1.1 600 OK\r\nFoo: bar\r\n", -1,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "NUL in Reason Phrase",
+ "HTTP/1.1 200 O\x00K\r\nFoo: bar\r\n", 28,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+
+ { "NUL in Header",
+ "HTTP/1.1 200 OK\r\nFoo: b\x00ar\r\n", 28,
+ -1, 0, NULL,
+ { { NULL } }
+ },
+};
+static const int num_resptests = G_N_ELEMENTS (resptests);
+
+static struct QValueTest {
+ const char *header_value;
+ const char *acceptable[7];
+ const char *unacceptable[2];
+} qvaluetests[] = {
+ { "text/plain; q=0.5, text/html,\t text/x-dvi; q=0.8, text/x-c",
+ { "text/html", "text/x-c", "text/x-dvi", "text/plain", NULL },
+ { NULL },
+ },
+
+ { "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
+ { "text/html;level=1", "text/html", "*/*", "text/html;level=2",
+ "text/*", NULL },
+ { NULL }
+ },
+
+ { "gzip;q=1.0, identity; q=0.5, *;q=0",
+ { "gzip", "identity", NULL },
+ { "*", NULL },
+ }
+};
+static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests);
+
+static void
+print_header (const char *name, const char *value, gpointer data)
+{
+ debug_printf (1, " '%s': '%s'\n", name, value);
+}
+
+static gboolean
+check_headers (Header *headers, SoupMessageHeaders *hdrs)
+{
+ GSList *header_names, *h;
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+ gboolean ok = TRUE;
+ int i;
+
+ header_names = NULL;
+ soup_message_headers_iter_init (&iter, hdrs);
+ while (soup_message_headers_iter_next (&iter, &name, &value)) {
+ if (!g_slist_find_custom (header_names, name,
+ (GCompareFunc)strcmp))
+ header_names = g_slist_append (header_names, (char *)name);
+ }
+
+ for (i = 0, h = header_names; headers[i].name && h; i++, h = h->next) {
+ if (strcmp (h->data, headers[i].name) != 0) {
+ ok = FALSE;
+ break;
+ }
+ value = soup_message_headers_get_list (hdrs, headers[i].name);
+ if (strcmp (value, headers[i].value) != 0) {
+ ok = FALSE;
+ break;
+ }
+ }
+ if (headers[i].name || h)
+ ok = FALSE;
+ g_slist_free (header_names);
+ return ok;
+}
+
+static void
+do_request_tests (void)
+{
+ int i, len, h;
+ char *method, *path;
+ SoupHTTPVersion version;
+ SoupMessageHeaders *headers;
+ guint status;
+
+ debug_printf (1, "Request tests\n");
+ for (i = 0; i < num_reqtests; i++) {
+ gboolean ok = TRUE;
+
+ debug_printf (1, "%2d. %s (%s): ", i + 1, reqtests[i].description,
+ soup_status_get_phrase (reqtests[i].status));
+
+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
+ method = path = NULL;
+
+ if (reqtests[i].length == -1)
+ len = strlen (reqtests[i].request);
+ else
+ len = reqtests[i].length;
+ status = soup_headers_parse_request (reqtests[i].request, len,
+ headers, &method, &path,
+ &version);
+ if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ if ((reqtests[i].method && strcmp (reqtests[i].method, method) != 0) || !reqtests[i].method)
+ ok = FALSE;
+ if ((reqtests[i].path && strcmp (reqtests[i].path, path) != 0) || !reqtests[i].path)
+ ok = FALSE;
+ if (reqtests[i].version != version)
+ ok = FALSE;
+
+ if (!check_headers (reqtests[i].headers, headers))
+ ok = FALSE;
+ } else {
+ if (status != reqtests[i].status)
+ ok = FALSE;
+ }
+
+ if (ok)
+ debug_printf (1, "OK!\n");
+ else {
+ debug_printf (1, "BAD!\n");
+ errors++;
+ if (reqtests[i].method) {
+ debug_printf (1, " expected: '%s' '%s' 'HTTP/1.%d'\n",
+ reqtests[i].method,
+ reqtests[i].path,
+ reqtests[i].version);
+ for (h = 0; reqtests[i].headers[h].name; h++) {
+ debug_printf (1, " '%s': '%s'\n",
+ reqtests[i].headers[h].name,
+ reqtests[i].headers[h].value);
+ }
+ } else {
+ debug_printf (1, " expected: %s\n",
+ soup_status_get_phrase (reqtests[i].status));
+ }
+ if (method) {
+ debug_printf (1, " got: '%s' '%s' 'HTTP/1.%d'\n",
+ method, path, version);
+ soup_message_headers_foreach (headers, print_header, NULL);
+ } else {
+ debug_printf (1, " got: %s\n",
+ soup_status_get_phrase (status));
+ }
+ }
+
+ g_free (method);
+ g_free (path);
+ soup_message_headers_free (headers);
+ }
+ debug_printf (1, "\n");
+}
+
+static void
+do_response_tests (void)
+{
+ int i, len, h;
+ guint status_code;
+ char *reason_phrase;
+ SoupHTTPVersion version;
+ SoupMessageHeaders *headers;
+
+ debug_printf (1, "Response tests\n");
+ for (i = 0; i < num_resptests; i++) {
+ gboolean ok = TRUE;
+
+ debug_printf (1, "%2d. %s (%s): ", i + 1, resptests[i].description,
+ resptests[i].reason_phrase ? "should parse" : "should NOT parse");
+
+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+ reason_phrase = NULL;
+
+ if (resptests[i].length == -1)
+ len = strlen (resptests[i].response);
+ else
+ len = resptests[i].length;
+ if (soup_headers_parse_response (resptests[i].response, len,
+ headers, &version,
+ &status_code, &reason_phrase)) {
+ if (resptests[i].version != version)
+ ok = FALSE;
+ if (resptests[i].status_code != status_code)
+ ok = FALSE;
+ if ((resptests[i].reason_phrase && strcmp (resptests[i].reason_phrase, reason_phrase) != 0) || !resptests[i].reason_phrase)
+ ok = FALSE;
+
+ if (!check_headers (resptests[i].headers, headers))
+ ok = FALSE;
+ } else {
+ if (resptests[i].reason_phrase)
+ ok = FALSE;
+ }
+
+ if (ok)
+ debug_printf (1, "OK!\n");
+ else {
+ debug_printf (1, "BAD!\n");
+ errors++;
+ if (resptests[i].reason_phrase) {
+ debug_printf (1, " expected: 'HTTP/1.%d' '%03d' '%s'\n",
+ resptests[i].version,
+ resptests[i].status_code,
+ resptests[i].reason_phrase);
+ for (h = 0; resptests[i].headers[h].name; h++) {
+ debug_printf (1, " '%s': '%s'\n",
+ resptests[i].headers[h].name,
+ resptests[i].headers[h].value);
+ }
+ } else
+ debug_printf (1, " expected: parse error\n");
+ if (reason_phrase) {
+ debug_printf (1, " got: 'HTTP/1.%d' '%03d' '%s'\n",
+ version, status_code, reason_phrase);
+ soup_message_headers_foreach (headers, print_header, NULL);
+ } else
+ debug_printf (1, " got: parse error\n");
+ }
+
+ g_free (reason_phrase);
+ soup_message_headers_free (headers);
+ }
+ debug_printf (1, "\n");
+}
+
+static void
+do_qvalue_tests (void)
+{
+ int i, j;
+ GSList *acceptable, *unacceptable, *iter;
+ gboolean wrong;
+
+ debug_printf (1, "qvalue tests\n");
+ for (i = 0; i < num_qvaluetests; i++) {
+ debug_printf (1, "%2d. %s:\n", i + 1, qvaluetests[i].header_value);
+
+ unacceptable = NULL;
+ acceptable = soup_header_parse_quality_list (qvaluetests[i].header_value,
+ &unacceptable);
+
+ debug_printf (1, " acceptable: ");
+ wrong = FALSE;
+ if (acceptable) {
+ for (iter = acceptable, j = 0; iter; iter = iter->next, j++) {
+ debug_printf (1, "%s ", (char *)iter->data);
+ if (!qvaluetests[i].acceptable[j] ||
+ strcmp (iter->data, qvaluetests[i].acceptable[j]) != 0)
+ wrong = TRUE;
+ }
+ debug_printf (1, "\n");
+ soup_header_free_list (acceptable);
+ } else
+ debug_printf (1, "(none)\n");
+ if (wrong) {
+ debug_printf (1, " WRONG! expected: ");
+ for (j = 0; qvaluetests[i].acceptable[j]; j++)
+ debug_printf (1, "%s ", qvaluetests[i].acceptable[j]);
+ debug_printf (1, "\n");
+ errors++;
+ }
+
+ debug_printf (1, " unacceptable: ");
+ wrong = FALSE;
+ if (unacceptable) {
+ for (iter = unacceptable, j = 0; iter; iter = iter->next, j++) {
+ debug_printf (1, "%s ", (char *)iter->data);
+ if (!qvaluetests[i].unacceptable[j] ||
+ strcmp (iter->data, qvaluetests[i].unacceptable[j]) != 0)
+ wrong = TRUE;
+ }
+ debug_printf (1, "\n");
+ soup_header_free_list (unacceptable);
+ } else
+ debug_printf (1, "(none)\n");
+ if (wrong) {
+ debug_printf (1, " WRONG! expected: ");
+ for (j = 0; qvaluetests[i].unacceptable[j]; j++)
+ debug_printf (1, "%s ", qvaluetests[i].unacceptable[j]);
+ debug_printf (1, "\n");
+ errors++;
+ }
+
+ debug_printf (1, "\n");
+ }
+}
+
+#define RFC5987_TEST_FILENAME "t\xC3\xA9st.txt"
+#define RFC5987_TEST_FALLBACK_FILENAME "test.txt"
+
+#define RFC5987_TEST_HEADER_ENCODED "attachment; filename*=UTF-8''t%C3%A9st.txt"
+
+#define RFC5987_TEST_HEADER_UTF8 "attachment; filename*=UTF-8''t%C3%A9st.txt; filename=\"test.txt\""
+#define RFC5987_TEST_HEADER_ISO "attachment; filename=\"test.txt\"; filename*=iso-8859-1''t%E9st.txt"
+#define RFC5987_TEST_HEADER_FALLBACK "attachment; filename*=Unknown''t%FF%FF%FFst.txt; filename=\"test.txt\""
+
+static void
+do_content_disposition_tests (void)
+{
+ SoupMessageHeaders *hdrs;
+ GHashTable *params;
+ const char *header, *filename;
+ char *disposition;
+ SoupBuffer *buffer;
+ SoupMultipart *multipart;
+ SoupMessageBody *body;
+
+ debug_printf (1, "Content-Disposition tests\n");
+
+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ params = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (params, "filename", RFC5987_TEST_FILENAME);
+ soup_message_headers_set_content_disposition (hdrs, "attachment", params);
+ g_hash_table_destroy (params);
+
+ header = soup_message_headers_get_one (hdrs, "Content-Disposition");
+ if (!strcmp (header, RFC5987_TEST_HEADER_ENCODED))
+ debug_printf (1, " encoded OK\n");
+ else {
+ debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
+ RFC5987_TEST_HEADER_ENCODED, header);
+ errors++;
+ }
+
+ /* UTF-8 decoding */
+ soup_message_headers_clear (hdrs);
+ soup_message_headers_append (hdrs, "Content-Disposition",
+ RFC5987_TEST_HEADER_UTF8);
+ if (!soup_message_headers_get_content_disposition (hdrs,
+ &disposition,
+ ¶ms)) {
+ debug_printf (1, " UTF-8 decoding FAILED!\n could not parse\n");
+ errors++;
+ return;
+ }
+ g_free (disposition);
+
+ filename = g_hash_table_lookup (params, "filename");
+ if (!filename) {
+ debug_printf (1, " UTF-8 decoding FAILED!\n could not find filename\n");
+ errors++;
+ } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
+ debug_printf (1, " UTF-8 decoding FAILED!\n expected: %s\n got: %s\n",
+ RFC5987_TEST_FILENAME, filename);
+ errors++;
+ } else
+ debug_printf (1, " UTF-8 decoded OK\n");
+ g_hash_table_destroy (params);
+
+ /* ISO-8859-1 decoding */
+ soup_message_headers_clear (hdrs);
+ soup_message_headers_append (hdrs, "Content-Disposition",
+ RFC5987_TEST_HEADER_ISO);
+ if (!soup_message_headers_get_content_disposition (hdrs,
+ &disposition,
+ ¶ms)) {
+ debug_printf (1, " iso-8859-1 decoding FAILED!\n could not parse\n");
+ errors++;
+ return;
+ }
+ g_free (disposition);
+
+ filename = g_hash_table_lookup (params, "filename");
+ if (!filename) {
+ debug_printf (1, " iso-8859-1 decoding FAILED!\n could not find filename\n");
+ errors++;
+ } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
+ debug_printf (1, " iso-8859-1 decoding FAILED!\n expected: %s\n got: %s\n",
+ RFC5987_TEST_FILENAME, filename);
+ errors++;
+ } else
+ debug_printf (1, " iso-8859-1 decoded OK\n");
+ g_hash_table_destroy (params);
+
+ /* Fallback */
+ soup_message_headers_clear (hdrs);
+ soup_message_headers_append (hdrs, "Content-Disposition",
+ RFC5987_TEST_HEADER_FALLBACK);
+ if (!soup_message_headers_get_content_disposition (hdrs,
+ &disposition,
+ ¶ms)) {
+ debug_printf (1, " fallback decoding FAILED!\n could not parse\n");
+ errors++;
+ return;
+ }
+ g_free (disposition);
+
+ filename = g_hash_table_lookup (params, "filename");
+ if (!filename) {
+ debug_printf (1, " fallback decoding FAILED!\n could not find filename\n");
+ errors++;
+ } else if (strcmp (filename, RFC5987_TEST_FALLBACK_FILENAME) != 0) {
+ debug_printf (1, " fallback decoding FAILED!\n expected: %s\n got: %s\n",
+ RFC5987_TEST_FALLBACK_FILENAME, filename);
+ errors++;
+ } else
+ debug_printf (1, " fallback decoded OK\n");
+ g_hash_table_destroy (params);
+
+ soup_message_headers_free (hdrs);
+
+ /* Ensure that soup-multipart always quotes filename (bug 641280) */
+ multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
+ buffer = soup_buffer_new (SOUP_MEMORY_STATIC, "foo", 3);
+ soup_multipart_append_form_file (multipart, "test", "token",
+ "text/plain", buffer);
+ soup_buffer_free (buffer);
+
+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ body = soup_message_body_new ();
+ soup_multipart_to_message (multipart, hdrs, body);
+ soup_message_headers_free (hdrs);
+ soup_multipart_free (multipart);
+
+ buffer = soup_message_body_flatten (body);
+ soup_message_body_free (body);
+
+ if (strstr (buffer->data, "filename=\"token\""))
+ debug_printf (1, " SoupMultipart encoded filename correctly\n");
+ else {
+ debug_printf (1, " SoupMultipart encoded filename incorrectly!\n");
+ errors++;
+ }
+ soup_buffer_free (buffer);
+
+ debug_printf (1, "\n");
+}
+
+#define CONTENT_TYPE_TEST_MIME_TYPE "text/plain"
+#define CONTENT_TYPE_TEST_ATTRIBUTE "charset"
+#define CONTENT_TYPE_TEST_VALUE "US-ASCII"
+#define CONTENT_TYPE_TEST_HEADER "text/plain; charset=US-ASCII"
+
+#define CONTENT_TYPE_BAD_HEADER "plain text, not text/html"
+
+static void
+do_content_type_tests (void)
+{
+ SoupMessageHeaders *hdrs;
+ GHashTable *params;
+ const char *header, *mime_type;
+
+ debug_printf (1, "Content-Type tests\n");
+
+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ params = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (params, CONTENT_TYPE_TEST_ATTRIBUTE,
+ CONTENT_TYPE_TEST_VALUE);
+ soup_message_headers_set_content_type (hdrs, CONTENT_TYPE_TEST_MIME_TYPE, params);
+ g_hash_table_destroy (params);
+
+ header = soup_message_headers_get_one (hdrs, "Content-Type");
+ if (!strcmp (header, CONTENT_TYPE_TEST_HEADER))
+ debug_printf (1, " encoded OK\n");
+ else {
+ debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
+ CONTENT_TYPE_TEST_HEADER, header);
+ errors++;
+ }
+
+ soup_message_headers_clear (hdrs);
+ soup_message_headers_append (hdrs, "Content-Type",
+ CONTENT_TYPE_TEST_MIME_TYPE);
+ /* Add a second Content-Type header: should be ignored */
+ soup_message_headers_append (hdrs, "Content-Type",
+ CONTENT_TYPE_TEST_MIME_TYPE);
+
+ mime_type = soup_message_headers_get_content_type (hdrs, ¶ms);
+ if (!mime_type) {
+ debug_printf (1, " decoding FAILED!\n could not parse\n");
+ errors++;
+ }
+
+ if (mime_type && strcmp (mime_type, CONTENT_TYPE_TEST_MIME_TYPE) != 0) {
+ debug_printf (1, " decoding FAILED!\n bad returned MIME type: %s\n",
+ mime_type);
+ errors++;
+ } else if (params && g_hash_table_size (params) != 0) {
+ debug_printf (1, " decoding FAILED!\n params contained %d params (should be 0)\n",
+ g_hash_table_size (params));
+ errors++;
+ } else
+ debug_printf (1, " decoded OK\n");
+
+ if (params)
+ g_hash_table_destroy (params);
+
+ soup_message_headers_clear (hdrs);
+ soup_message_headers_append (hdrs, "Content-Type",
+ CONTENT_TYPE_BAD_HEADER);
+ mime_type = soup_message_headers_get_content_type (hdrs, ¶ms);
+ if (mime_type) {
+ debug_printf (1, " Bad content rejection FAILED!\n");
+ errors++;
+ } else
+ debug_printf (1, " Bad content rejection OK\n");
+
+ soup_message_headers_free (hdrs);
+
+ debug_printf (1, "\n");
+}
+
+struct {
+ const char *name, *value;
+} test_params[] = {
+ { "one", "foo" },
+ { "two", "test with spaces" },
+ { "three", "test with \"quotes\" and \\s" },
+ { "four", NULL },
+ { "five", "test with \xC3\xA1\xC3\xA7\xC4\x89\xC3\xA8\xC3\xB1\xC5\xA3\xC5\xA1" }
+};
+
+#define TEST_PARAMS_RESULT "one=foo, two=\"test with spaces\", three=\"test with \\\"quotes\\\" and \\\\s\", four, five*=UTF-8''test%20with%20%C3%A1%C3%A7%C4%89%C3%A8%C3%B1%C5%A3%C5%A1"
+
+static void
+do_append_param_tests (void)
+{
+ GString *params;
+ int i;
+
+ debug_printf (1, "soup_header_g_string_append_param() tests\n");
+
+ params = g_string_new (NULL);
+ for (i = 0; i < G_N_ELEMENTS (test_params); i++) {
+ if (i > 0)
+ g_string_append (params, ", ");
+ soup_header_g_string_append_param (params,
+ test_params[i].name,
+ test_params[i].value);
+ }
+ if (strcmp (params->str, TEST_PARAMS_RESULT) != 0) {
+ debug_printf (1, " FAILED!\n expected: %s\n got: %s\n",
+ TEST_PARAMS_RESULT, params->str);
+ errors++;
+ } else
+ debug_printf (1, " OK\n");
+ g_string_free (params, TRUE);
+
+ debug_printf (1, "\n");
+}
+
+static const struct {
+ const char *description, *name, *value;
+} bad_headers[] = {
+ { "Empty name", "", "value" },
+ { "Name with spaces", "na me", "value" },
+ { "Name with colon", "na:me", "value" },
+ { "Name with CR", "na\rme", "value" },
+ { "Name with LF", "na\nme", "value" },
+ { "Name with tab", "na\tme", "value" },
+ { "Value with CR", "name", "val\rue" },
+ { "Value with LF", "name", "val\nue" },
+ { "Value with LWS", "name", "val\r\n ue" }
+};
+
+static void
+do_bad_header_tests (void)
+{
+ SoupMessageHeaders *hdrs;
+ int i;
+
+ debug_printf (1, "bad header rejection tests\n");
+
+ hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ for (i = 0; i < G_N_ELEMENTS (bad_headers); i++) {
+ debug_printf (1, " %s\n", bad_headers[i].description);
+ expect_warning = TRUE;
+ soup_message_headers_append (hdrs, bad_headers[i].name,
+ bad_headers[i].value);
+ if (expect_warning) {
+ expect_warning = FALSE;
+ debug_printf (1, " FAILED: soup_message_headers_append() did not reject it\n");
+ errors++;
+ }
+ }
+ soup_message_headers_free (hdrs);
+}
+
+int
+main (int argc, char **argv)
+{
+ test_init (argc, argv, NULL);
+
+ do_request_tests ();
+ do_response_tests ();
+ do_qvalue_tests ();
+ do_content_disposition_tests ();
+ do_content_type_tests ();
+ do_append_param_tests ();
+ do_bad_header_tests ();
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+user1:realm1:69cb1fa0285304a71f8975aecd027008
+user2:realm2:b67d8ee3c2e271abba78f71d12fe472e
+user3:realm3:601c319693279abbc07d332bd7637239
--- /dev/null
+user1:sTFk2g6n8RsWY
+user2:N.Dlbd.xU4K1w
+user3:c2vSU/3eQHy.w
--- /dev/null
+# http.conf used for testing auth-test
+
+ServerName 127.0.0.1
+Listen 127.0.0.1:47524
+
+PidFile @builddir@/httpd.pid
+DocumentRoot @srcdir@
+
+# The tests shut down apache with "graceful-stop", because that makes
+# it close its listening socket right away. But it seems to sometimes
+# result in apache never fully exiting. This fixes that.
+GracefulShutdownTimeout 1
+
+# Change this to "./error.log" if it's failing and you don't know why
+ErrorLog /dev/null
+
+LoadModule alias_module @APACHE_MODULE_DIR@/mod_alias.so
+LoadModule auth_basic_module @APACHE_MODULE_DIR@/mod_auth_basic.so
+LoadModule auth_digest_module @APACHE_MODULE_DIR@/mod_auth_digest.so
+LoadModule authn_file_module @APACHE_MODULE_DIR@/mod_authn_file.so
+LoadModule authz_host_module @APACHE_MODULE_DIR@/mod_authz_host.so
+LoadModule authz_user_module @APACHE_MODULE_DIR@/mod_authz_user.so
+LoadModule dir_module @APACHE_MODULE_DIR@/mod_dir.so
+LoadModule mime_module @APACHE_MODULE_DIR@/mod_mime.so
+@IF_HAVE_PHP@LoadModule php5_module @APACHE_PHP_MODULE_DIR@/@APACHE_PHP_MODULE@
+LoadModule proxy_module @APACHE_MODULE_DIR@/mod_proxy.so
+LoadModule proxy_http_module @APACHE_MODULE_DIR@/mod_proxy_http.so
+LoadModule proxy_connect_module @APACHE_MODULE_DIR@/mod_proxy_connect.so
+LoadModule ssl_module @APACHE_SSL_MODULE_DIR@/mod_ssl.so
+
+DirectoryIndex index.txt
+TypesConfig /dev/null
+AddType application/x-httpd-php .php
+Redirect permanent /redirected /index.txt
+
+# Proxy #1: unauthenticated
+Listen 127.0.0.1:47526
+<VirtualHost 127.0.0.1:47526>
+ ProxyRequests On
+ AllowCONNECT 47525
+
+ # Deny proxying by default
+ <Proxy *>
+ Order Deny,Allow
+ Deny from all
+ </Proxy>
+
+ # Allow local http connections
+ <Proxy http://127.0.0.1*>
+ Order Allow,Deny
+ Allow from all
+ </Proxy>
+
+ # Allow CONNECT to local https port
+ <Proxy 127.0.0.1:47525>
+ Order Allow,Deny
+ Allow from all
+ </Proxy>
+
+ # Deny non-proxy requests
+ <Directory />
+ Order Deny,Allow
+ Deny from all
+ </Directory>
+</VirtualHost>
+
+# Proxy #2: authenticated
+Listen 127.0.0.1:47527
+<VirtualHost 127.0.0.1:47527>
+ ProxyRequests On
+ AllowCONNECT 47525
+
+ # Deny proxying by default
+ <Proxy *>
+ Order Deny,Allow
+ Deny from all
+ </Proxy>
+
+ # Allow local http connections with authentication
+ <Proxy http://127.0.0.1:47524*>
+ Order Allow,Deny
+ Allow from all
+
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile @srcdir@/htpasswd
+ Require valid-user
+ </Proxy>
+
+ # Allow CONNECT to local https port with authentication
+ <Proxy 127.0.0.1:47525>
+ Order Allow,Deny
+ Allow from all
+
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile @srcdir@/htpasswd
+ Require valid-user
+ </Proxy>
+
+ # Fail non-proxy requests
+ <Directory />
+ Order Deny,Allow
+ Deny from all
+ </Directory>
+</VirtualHost>
+
+# Proxy #3: unauthenticatable-to
+Listen 127.0.0.1:47528
+<VirtualHost 127.0.0.1:47528>
+ ProxyRequests On
+ AllowCONNECT 47525
+
+ # Deny proxying by default
+ <Proxy *>
+ Order Deny,Allow
+ Deny from all
+ </Proxy>
+
+ # Allow local http connections with authentication
+ <Proxy http://127.0.0.1:47524*>
+ Order Allow,Deny
+ Allow from all
+
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile @srcdir@/htpasswd
+ Require user no-such-user
+ </Proxy>
+
+ # Allow CONNECT to local https port with authentication
+ <Proxy 127.0.0.1:47525>
+ Order Allow,Deny
+ Allow from all
+
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile @srcdir@/htpasswd
+ Require user no-such-user
+ </Proxy>
+
+ # Fail non-proxy requests
+ <Directory />
+ Order Deny,Allow
+ Deny from all
+ </Directory>
+</VirtualHost>
+
+
+# SSL setup
+<IfModule mod_ssl.c>
+ Listen 127.0.0.1:47525
+
+ <VirtualHost 127.0.0.1:47525>
+ SSLEngine on
+
+ SSLCertificateFile @srcdir@/test-cert.pem
+ SSLCertificateKeyFile @srcdir@/test-key.pem
+
+ </VirtualHost>
+</IfModule>
+
+
+# Basic auth tests
+Alias /Basic/realm1/realm2/realm1 @srcdir@
+Alias /Basic/realm1/realm2 @srcdir@
+Alias /Basic/realm1/subdir @srcdir@
+Alias /Basic/realm1/not @srcdir@
+Alias /Basic/realm1 @srcdir@
+Alias /Basic/realm12/subdir @srcdir@
+Alias /Basic/realm12 @srcdir@
+Alias /Basic/realm2 @srcdir@
+Alias /Basic/realm3 @srcdir@
+Alias /Basic @srcdir@
+
+<Location /Basic/realm1>
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile @srcdir@/htpasswd
+ Require user user1
+</Location>
+
+<Location /Basic/realm1/not>
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile @srcdir@/htpasswd
+ Require user user2
+</Location>
+
+<Location /Basic/realm12>
+ AuthType Basic
+ AuthName realm12
+ AuthUserFile @srcdir@/htpasswd
+ Require user user1 user2
+</Location>
+
+<Location /Basic/realm1/realm2>
+ AuthType Basic
+ AuthName realm2
+ AuthUserFile @srcdir@/htpasswd
+ Require user user2
+</Location>
+
+<Location /Basic/realm1/realm2/realm1>
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile @srcdir@/htpasswd
+ Require user user1
+</Location>
+
+<Location /Basic/realm2>
+ AuthType Basic
+ AuthName realm2
+ AuthUserFile @srcdir@/htpasswd
+ Require user user2
+</Location>
+
+<Location /Basic/realm3>
+ AuthType Basic
+ AuthName realm3
+ AuthUserFile @srcdir@/htpasswd
+ Require user user3
+</Location>
+
+# Digest auth tests
+Alias /Digest/realm1/realm2/realm1 @srcdir@
+Alias /Digest/realm1/realm2 @srcdir@
+Alias /Digest/realm1/subdir @srcdir@
+Alias /Digest/realm1/expire @srcdir@
+Alias /Digest/realm1/not @srcdir@
+Alias /Digest/realm1 @srcdir@
+Alias /Digest/realm2 @srcdir@
+Alias /Digest/realm3 @srcdir@
+Alias /Digest @srcdir@
+
+<Location /Digest/realm1>
+ AuthType Digest
+ AuthName realm1
+ AuthUserFile @srcdir@/htdigest
+ AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
+ Require valid-user
+</Location>
+
+<Location /Digest/realm1/expire>
+ AuthType Digest
+ AuthName realm1
+ AuthUserFile @srcdir@/htdigest
+ AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
+ AuthDigestNonceLifetime 2
+ Require valid-user
+</Location>
+
+<Location /Digest/realm1/not>
+ AuthType Digest
+ AuthName realm1
+ AuthUserFile @srcdir@/htdigest
+ AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
+ Require user user2
+</Location>
+
+<Location /Digest/realm1/realm2>
+ AuthType Digest
+ AuthName realm2
+ AuthUserFile @srcdir@/htdigest
+ AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2
+ Require valid-user
+</Location>
+
+<Location /Digest/realm1/realm2/realm1>
+ AuthType Digest
+ AuthName realm1
+ AuthUserFile @srcdir@/htdigest
+ AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
+ Require valid-user
+</Location>
+
+<Location /Digest/realm2>
+ AuthType Digest
+ AuthName realm2
+ AuthUserFile @srcdir@/htdigest
+ AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2
+ Require valid-user
+</Location>
+
+<Location /Digest/realm3>
+ AuthType Digest
+ AuthName realm3
+ AuthUserFile @srcdir@/htdigest
+ AuthDigestDomain /Digest/realm3
+ Require valid-user
+ # test RFC2069-style Digest
+ AuthDigestQop none
+</Location>
--- /dev/null
+Directory index file for test httpd.
+
+pull-api wants this file to be larger than 3 times
+soup-message-io.c:RESPONSE_BUFFER_SIZE. Also, the contents of the file
+should not repeat themselves. (There are no duplicated lines here
+other than the two blank lines.)
+
+ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdef
+g hijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN
+OP QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456
+789 :;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
+}~!" #$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd
+efghi jklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL
+MNOPQR STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234
+56789:; <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
+{|}~!"#$ %&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab
+cdefghijk lmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
+KLMNOPQRST UVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012
+3456789:;<= >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwx
+yz{|}~!"#$%& '()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
+abcdefghijklm nopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH
+IJKLMNOPQRSTUV WXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0
+123456789:;<=>? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuv
+wxyz{|}~!"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^
+_`abcdefghijklmno pqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEF
+GHIJKLMNOPQRSTUVWX YZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-.
+/0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrst
+uvwxyz{|}~!"#$%&'()* +,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\
+]^_`abcdefghijklmnopq rstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCD
+EFGHIJKLMNOPQRSTUVWXYZ [\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,
+-./0123456789:;<=>?@ABC DEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr
+stuvwxyz{|}~!"#$%&'()*+, -./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ
+[\]^_`abcdefghijklmnopqrs tuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@AB
+CDEFGHIJKLMNOPQRSTUVWXYZ[\ ]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*
++,-./0123456789:;<=>?@ABCDE FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
+qrstuvwxyz{|}~!"#$%&'()*+,-. /0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX
+YZ[\]^_`abcdefghijklmnopqrstu vwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@
+ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^ _`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'(
+)*+,-./0123456789:;<=>?@ABCDEFG HIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
+opqrstuvwxyz{|}~!"#$%&'()*+,-./0 123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV
+WXYZ[\]^_`abcdefghijklmnopqrstuvw xyz{|}~!"#$%&'()*+,-./0123456789:;<=>
+?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` abcdefghijklmnopqrstuvwxyz{|}~!"#$%&
+'()*+,-./0123456789:;<=>?@ABCDEFGHI JKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
+mnopqrstuvwxyz{|}~!"#$%&'()*+,-./012 3456789:;<=>?@ABCDEFGHIJKLMNOPQRST
+UVWXYZ[\]^_`abcdefghijklmnopqrstuvwxy z{|}~!"#$%&'()*+,-./0123456789:;<
+=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab cdefghijklmnopqrstuvwxyz{|}~!"#$
+%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK LMNOPQRSTUVWXYZ[\]^_`abcdefghij
+klmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234 56789:;<=>?@ABCDEFGHIJKLMNOPQR
+STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{ |}~!"#$%&'()*+,-./0123456789:
+;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd efghijklmnopqrstuvwxyz{|}~!"
+#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLM NOPQRSTUVWXYZ[\]^_`abcdefgh
+ijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456 789:;<=>?@ABCDEFGHIJKLMNOP
+QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|} ~!"#$%&'()*+,-./012345678
+9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdef ghijklmnopqrstuvwxyz{|}~
+!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO PQRSTUVWXYZ[\]^_`abcdef
+ghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012345678 9:;<=>?@ABCDEFGHIJKLMN
+OPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~! "#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh ijklmnopqrstuvwxyz{|
+}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQ RSTUVWXYZ[\]^_`abcd
+efghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789: ;<=>?@ABCDEFGHIJKL
+MNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"# $%&'()*+,-./01234
+56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij klmnopqrstuvwxyz
+{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRS TUVWXYZ[\]^_`ab
+cdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;< =>?@ABCDEFGHIJ
+KLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$% &'()*+,-./012
+3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl mnopqrstuvwx
+yz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU VWXYZ[\]^_`
+abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=> ?@ABCDEFGH
+IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&' ()*+,-./0
+123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn opqrstuv
+wxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW XYZ[\]^
+_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ ABCDEF
+GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'() *+,-.
+/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop qrst
+uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXY Z[\
+]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@AB CD
+EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+ ,
+-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr
+ stuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ
+[ \]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@AB
+CD EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*
++,- ./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
+qrst uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX
+YZ[\] ^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@
+ABCDEF GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'(
+)*+,-./ 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
+opqrstuv wxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV
+WXYZ[\]^_ `abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>
+?@ABCDEFGH IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&
+'()*+,-./01 23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
+mnopqrstuvwx yz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRST
+UVWXYZ[\]^_`a bcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<
+=>?@ABCDEFGHIJ KLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$
+%&'()*+,-./0123 456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
+klmnopqrstuvwxyz {|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQR
+STUVWXYZ[\]^_`abc defghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:
+;<=>?@ABCDEFGHIJKL MNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"
+#$%&'()*+,-./012345 6789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
+ijklmnopqrstuvwxyz{| }~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOP
+QRSTUVWXYZ[\]^_`abcde fghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012345678
+9:;<=>?@ABCDEFGHIJKLMN OPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+!"#$%&'()*+,-./01234567 89:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdef
+ghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN
+OPQRSTUVWXYZ[\]^_`abcdefg hijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMNOP QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
+}~!"#$%&'()*+,-./0123456789 :;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd
+efghijklmnopqrstuvwxyz{|}~!" #$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL
+MNOPQRSTUVWXYZ[\]^_`abcdefghi jklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234
+56789:;<=>?@ABCDEFGHIJKLMNOPQR STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
+{|}~!"#$%&'()*+,-./0123456789:; <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab
+cdefghijklmnopqrstuvwxyz{|}~!"#$ %&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
+KLMNOPQRSTUVWXYZ[\]^_`abcdefghijk lmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012
+3456789:;<=>?@ABCDEFGHIJKLMNOPQRST UVWXYZ[\]^_`abcdefghijklmnopqrstuvwx
+yz{|}~!"#$%&'()*+,-./0123456789:;<= >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
+abcdefghijklmnopqrstuvwxyz{|}~!"#$%& '()*+,-./0123456789:;<=>?@ABCDEFGH
+IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm nopqrstuvwxyz{|}~!"#$%&'()*+,-./0
+123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV WXYZ[\]^_`abcdefghijklmnopqrstuv
+wxyz{|}~!"#$%&'()*+,-./0123456789:;<=>? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^
+_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'( )*+,-./0123456789:;<=>?@ABCDEF
+GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno pqrstuvwxyz{|}~!"#$%&'()*+,-.
+/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX YZ[\]^_`abcdefghijklmnopqrst
+uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\
+]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()* +,-./0123456789:;<=>?@ABCD
+EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopq rstuvwxyz{|}~!"#$%&'()*+,
+-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ [\]^_`abcdefghijklmnopqr
+stuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABC DEFGHIJKLMNOPQRSTUVWXYZ
+[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+, -./0123456789:;<=>?@AB
+CDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrs tuvwxyz{|}~!"#$%&'()*
++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ ]^_`abcdefghijklmnop
+qrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDE FGHIJKLMNOPQRSTUVWX
+YZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-. /0123456789:;<=>?@
+ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu vwxyz{|}~!"#$%&'(
+)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^ _`abcdefghijklmn
+opqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFG HIJKLMNOPQRSTUV
+WXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0 123456789:;<=>
+?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvw xyz{|}~!"#$%&
+'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` abcdefghijkl
+mnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI JKLMNOPQRST
+UVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012 3456789:;<
+=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxy z{|}~!"#$
+%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab cdefghij
+klmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK LMNOPQR
+STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234 56789:
+;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{ |}~!"
+#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd efgh
+ijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLM NOP
+QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456 78
+9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|} ~
+!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdef
+ ghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN
+O PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456
+78 9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
+}~! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd
+efgh ijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL
+MNOPQ RSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234
+56789: ;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
+{|}~!"# $%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab
+cdefghij klmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
+KLMNOPQRS TUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012
+3456789:;< =>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwx
+yz{|}~!"#$% &'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
+abcdefghijkl mnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH
+IJKLMNOPQRSTU VWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0
+123456789:;<=> ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuv
+wxyz{|}~!"#$%&' ()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^
+_`abcdefghijklmn opqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEF
+GHIJKLMNOPQRSTUVW XYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-.
+/0123456789:;<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrst
+uvwxyz{|}~!"#$%&'() *+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\
+]^_`abcdefghijklmnop qrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCD
+EFGHIJKLMNOPQRSTUVWXY Z[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,
+-./0123456789:;<=>?@AB CDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr
+stuvwxyz{|}~!"#$%&'()*+ ,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ
+[\]^_`abcdefghijklmnopqr stuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@AB
+CDEFGHIJKLMNOPQRSTUVWXYZ[ \]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*
++,-./0123456789:;<=>?@ABCD EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
+qrstuvwxyz{|}~!"#$%&'()*+,- ./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX
+YZ[\]^_`abcdefghijklmnopqrst uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@
+ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] ^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'(
+)*+,-./0123456789:;<=>?@ABCDEF GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
+opqrstuvwxyz{|}~!"#$%&'()*+,-./ 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV
+WXYZ[\]^_`abcdefghijklmnopqrstuv wxyz{|}~!"#$%&'()*+,-./0123456789:;<=>
+?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ `abcdefghijklmnopqrstuvwxyz{|}~!"#$%&
+'()*+,-./0123456789:;<=>?@ABCDEFGH IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
+mnopqrstuvwxyz{|}~!"#$%&'()*+,-./01 23456789:;<=>?@ABCDEFGHIJKLMNOPQRST
+UVWXYZ[\]^_`abcdefghijklmnopqrstuvwx yz{|}~!"#$%&'()*+,-./0123456789:;<
+=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a bcdefghijklmnopqrstuvwxyz{|}~!"#$
+%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ KLMNOPQRSTUVWXYZ[\]^_`abcdefghij
+klmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123 456789:;<=>?@ABCDEFGHIJKLMNOPQR
+STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz {|}~!"#$%&'()*+,-./0123456789:
+;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abc defghijklmnopqrstuvwxyz{|}~!"
+#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL MNOPQRSTUVWXYZ[\]^_`abcdefgh
+ijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012345 6789:;<=>?@ABCDEFGHIJKLMNOP
+QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{| }~!"#$%&'()*+,-./012345678
+9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcde fghijklmnopqrstuvwxyz{|}~
+!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN OPQRSTUVWXYZ[\]^_`abcdef
+ghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234567 89:;<=>?@ABCDEFGHIJKLMN
+OPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg hijklmnopqrstuvwxyz{|
+}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOP QRSTUVWXYZ[\]^_`abcd
+efghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789 :;<=>?@ABCDEFGHIJKL
+MNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!" #$%&'()*+,-./01234
+56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi jklmnopqrstuvwxyz
+{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQR STUVWXYZ[\]^_`ab
+cdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:; <=>?@ABCDEFGHIJ
+KLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$ %&'()*+,-./012
+3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk lmnopqrstuvwx
+yz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRST UVWXYZ[\]^_`
+abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<= >?@ABCDEFGH
+IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%& '()*+,-./0
+123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm nopqrstuv
+wxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV WXYZ[\]^
+_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>? @ABCDEF
+GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'( )*+,-.
+/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno pqrst
+uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX YZ[\
+]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@A BCD
+EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()* +,
+-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopq r
+stuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ [\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@AB
+C DEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*
++, -./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
+qrs tuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX
+YZ[\ ]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@
+ABCDE FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'(
+)*+,-. /0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
+opqrstu vwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV
+WXYZ[\]^ _`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>
+?@ABCDEFG HIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&
+'()*+,-./0 123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
+mnopqrstuvw xyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRST
+UVWXYZ[\]^_` abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<
+=>?@ABCDEFGHI JKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$
+%&'()*+,-./012 3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
+klmnopqrstuvwxy z{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQR
+STUVWXYZ[\]^_`ab cdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:
+;<=>?@ABCDEFGHIJK LMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"
+#$%&'()*+,-./01234 56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
+ijklmnopqrstuvwxyz{ |}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOP
+QRSTUVWXYZ[\]^_`abcd efghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012345678
+9:;<=>?@ABCDEFGHIJKLM NOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+!"#$%&'()*+,-./0123456 789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdef
+ghijklmnopqrstuvwxyz{|} ~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN
+OPQRSTUVWXYZ[\]^_`abcdef ghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMNO PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
+}~!"#$%&'()*+,-./012345678 9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd
+efghijklmnopqrstuvwxyz{|}~! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL
+MNOPQRSTUVWXYZ[\]^_`abcdefgh ijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234
+56789:;<=>?@ABCDEFGHIJKLMNOPQ RSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
+{|}~!"#$%&'()*+,-./0123456789: ;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab
+cdefghijklmnopqrstuvwxyz{|}~!"# $%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
+KLMNOPQRSTUVWXYZ[\]^_`abcdefghij klmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012
+3456789:;<=>?@ABCDEFGHIJKLMNOPQRS TUVWXYZ[\]^_`abcdefghijklmnopqrstuvwx
+yz{|}~!"#$%&'()*+,-./0123456789:;< =>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
+abcdefghijklmnopqrstuvwxyz{|}~!"#$% &'()*+,-./0123456789:;<=>?@ABCDEFGH
+IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl mnopqrstuvwxyz{|}~!"#$%&'()*+,-./0
+123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU VWXYZ[\]^_`abcdefghijklmnopqrstuv
+wxyz{|}~!"#$%&'()*+,-./0123456789:;<=> ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^
+_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&' ()*+,-./0123456789:;<=>?@ABCDEF
+GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn opqrstuvwxyz{|}~!"#$%&'()*+,-.
+/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW XYZ[\]^_`abcdefghijklmnopqrst
+uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\
+]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'() *+,-./0123456789:;<=>?@ABCD
+EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop qrstuvwxyz{|}~!"#$%&'()*+,
+-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXY Z[\]^_`abcdefghijklmnopqr
+stuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@AB CDEFGHIJKLMNOPQRSTUVWXYZ
+[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+ ,-./0123456789:;<=>?@AB
+CDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr stuvwxyz{|}~!"#$%&'()*
++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[ \]^_`abcdefghijklmnop
+qrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCD EFGHIJKLMNOPQRSTUVWX
+YZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,- ./0123456789:;<=>?@
+ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrst uvwxyz{|}~!"#$%&'(
+)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] ^_`abcdefghijklmn
+opqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEF GHIJKLMNOPQRSTUV
+WXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./ 0123456789:;<=>
+?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuv wxyz{|}~!"#$%&
+'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ `abcdefghijkl
+mnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH IJKLMNOPQRST
+UVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01 23456789:;<
+=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwx yz{|}~!"#$
+%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a bcdefghij
+klmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ KLMNOPQR
+STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123 456789:
+;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz {|}~!"
+#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abc defgh
+ijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL MNOP
+QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012345 678
+9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{| }~
+!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcde f
+ghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN
+ OPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456
+7 89:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
+}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd
+efg hijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL
+MNOP QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234
+56789 :;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
+{|}~!" #$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab
+cdefghi jklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
+KLMNOPQR STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012
+3456789:; <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwx
+yz{|}~!"#$ %&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
+abcdefghijk lmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH
+IJKLMNOPQRST UVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0
+123456789:;<= >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuv
+wxyz{|}~!"#$%& '()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^
+_`abcdefghijklm nopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEF
+GHIJKLMNOPQRSTUV WXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-.
+/0123456789:;<=>? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrst
+uvwxyz{|}~!"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\
+]^_`abcdefghijklmno pqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCD
+EFGHIJKLMNOPQRSTUVWX YZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,
+-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr
+stuvwxyz{|}~!"#$%&'()* +,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ
+[\]^_`abcdefghijklmnopq rstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@AB
+CDEFGHIJKLMNOPQRSTUVWXYZ [\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*
++,-./0123456789:;<=>?@ABC DEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
+qrstuvwxyz{|}~!"#$%&'()*+, -./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX
+YZ[\]^_`abcdefghijklmnopqrs tuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@
+ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ ]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'(
+)*+,-./0123456789:;<=>?@ABCDE FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
+opqrstuvwxyz{|}~!"#$%&'()*+,-. /0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV
+WXYZ[\]^_`abcdefghijklmnopqrstu vwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>
+?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^ _`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&
+'()*+,-./0123456789:;<=>?@ABCDEFG HIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
+mnopqrstuvwxyz{|}~!"#$%&'()*+,-./0 123456789:;<=>?@ABCDEFGHIJKLMNOPQRST
+UVWXYZ[\]^_`abcdefghijklmnopqrstuvw xyz{|}~!"#$%&'()*+,-./0123456789:;<
+=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` abcdefghijklmnopqrstuvwxyz{|}~!"#$
+%&'()*+,-./0123456789:;<=>?@ABCDEFGHI JKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
+klmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012 3456789:;<=>?@ABCDEFGHIJKLMNOPQR
+STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxy z{|}~!"#$%&'()*+,-./0123456789:
+;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab cdefghijklmnopqrstuvwxyz{|}~!"
+#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK LMNOPQRSTUVWXYZ[\]^_`abcdefgh
+ijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234 56789:;<=>?@ABCDEFGHIJKLMNOP
+QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{ |}~!"#$%&'()*+,-./012345678
+9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd efghijklmnopqrstuvwxyz{|}~
+!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLM NOPQRSTUVWXYZ[\]^_`abcdef
+ghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456 789:;<=>?@ABCDEFGHIJKLMN
+OPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|} ~!"#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdef ghijklmnopqrstuvwxyz{|
+}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO PQRSTUVWXYZ[\]^_`abcd
+efghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012345678 9:;<=>?@ABCDEFGHIJKL
+MNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~! "#$%&'()*+,-./01234
+56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh ijklmnopqrstuvwxyz
+{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQ RSTUVWXYZ[\]^_`ab
+cdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789: ;<=>?@ABCDEFGHIJ
+KLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"# $%&'()*+,-./012
+3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij klmnopqrstuvwx
+yz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRS TUVWXYZ[\]^_`
+abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;< =>?@ABCDEFGH
+IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$% &'()*+,-./0
+123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl mnopqrstuv
+wxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU VWXYZ[\]^
+_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=> ?@ABCDEF
+GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&' ()*+,-.
+/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn opqrst
+uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVW XYZ[\
+]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ ABCD
+EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'() *+,
+-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop qr
+stuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXY Z
+[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@AB
+ CDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*
++ ,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop
+qr stuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX
+YZ[ \]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@
+ABCD EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'(
+)*+,- ./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn
+opqrst uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV
+WXYZ[\] ^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>
+?@ABCDEF GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&
+'()*+,-./ 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
+mnopqrstuv wxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRST
+UVWXYZ[\]^_ `abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<
+=>?@ABCDEFGH IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$
+%&'()*+,-./01 23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
+klmnopqrstuvwx yz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQR
+STUVWXYZ[\]^_`a bcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:
+;<=>?@ABCDEFGHIJ KLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"
+#$%&'()*+,-./0123 456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
+ijklmnopqrstuvwxyz {|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOP
+QRSTUVWXYZ[\]^_`abc defghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012345678
+9:;<=>?@ABCDEFGHIJKL MNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+!"#$%&'()*+,-./012345 6789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdef
+ghijklmnopqrstuvwxyz{| }~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN
+OPQRSTUVWXYZ[\]^_`abcde fghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMN OPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|
+}~!"#$%&'()*+,-./01234567 89:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcd
+efghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKL
+MNOPQRSTUVWXYZ[\]^_`abcdefg hijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234
+56789:;<=>?@ABCDEFGHIJKLMNOP QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
+{|}~!"#$%&'()*+,-./0123456789 :;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab
+cdefghijklmnopqrstuvwxyz{|}~!" #$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ
+KLMNOPQRSTUVWXYZ[\]^_`abcdefghi jklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012
+3456789:;<=>?@ABCDEFGHIJKLMNOPQR STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwx
+yz{|}~!"#$%&'()*+,-./0123456789:; <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
+abcdefghijklmnopqrstuvwxyz{|}~!"#$ %&'()*+,-./0123456789:;<=>?@ABCDEFGH
+IJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk lmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0
+123456789:;<=>?@ABCDEFGHIJKLMNOPQRST UVWXYZ[\]^_`abcdefghijklmnopqrstuv
+wxyz{|}~!"#$%&'()*+,-./0123456789:;<= >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^
+_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%& '()*+,-./0123456789:;<=>?@ABCDEF
+GHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm nopqrstuvwxyz{|}~!"#$%&'()*+,-.
+/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV WXYZ[\]^_`abcdefghijklmnopqrst
+uvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\
+]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'( )*+,-./0123456789:;<=>?@ABCD
+EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno pqrstuvwxyz{|}~!"#$%&'()*+,
+-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWX YZ[\]^_`abcdefghijklmnopqr
+stuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ
+[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()* +,-./0123456789:;<=>?@AB
+CDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopq rstuvwxyz{|}~!"#$%&'()*
++,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ [\]^_`abcdefghijklmnop
+qrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABC DEFGHIJKLMNOPQRSTUVWX
+YZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+, -./0123456789:;<=>?@
+ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrs tuvwxyz{|}~!"#$%&'(
+)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ ]^_`abcdefghijklmn
+opqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDE FGHIJKLMNOPQRSTUV
+WXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-. /0123456789:;<=>
+?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu vwxyz{|}~!"#$%&
+'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^ _`abcdefghijkl
+mnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFG HIJKLMNOPQRST
+UVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0 123456789:;<
+=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvw xyz{|}~!"#$
+%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` abcdefghij
+klmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI JKLMNOPQR
+STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./012 3456789:
+;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxy z{|}~!"
+#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ab cdefgh
+ijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK LMNOP
+QRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./01234 5678
+9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{ |}~
--- /dev/null
+# valgrind suppressions file
+
+{
+ ld.so/map
+ Memcheck:Cond
+ fun:index
+ fun:expand_dynamic_string_token
+ fun:_dl_map_object
+}
+{
+ ld.so/relocate
+ Memcheck:Cond
+ fun:_dl_relocate_object
+ fun:dl_main
+ fun:_dl_sysdep_start
+ fun:_dl_start
+}
+
+{
+ glib/g_type_init
+ Memcheck:Leak
+ ...
+ fun:g_type_init
+}
+{
+ glib/g_type_init_with_debug_flags
+ Memcheck:Leak
+ ...
+ fun:g_type_init_with_debug_flags
+}
+{
+ glib/g_thread_init
+ Memcheck:Leak
+ ...
+ fun:g_thread_init
+}
+{
+ glib/g_thread_init_glib
+ Memcheck:Leak
+ ...
+ fun:g_thread_init_glib
+}
+{
+ glib/g_type_register_static
+ Memcheck:Leak
+ ...
+ fun:g_type_register_static
+}
+{
+ glib/g_boxed_type_register_static
+ Memcheck:Leak
+ ...
+ fun:g_boxed_type_register_static
+}
+{
+ glib/g_type_add_interface_static
+ Memcheck:Leak
+ ...
+ fun:g_type_add_interface_static
+}
+{
+ glib/g_type_interface_add_prerequisite
+ Memcheck:Leak
+ ...
+ fun:g_type_interface_add_prerequisite
+}
+{
+ glib/g_type_class_ref
+ Memcheck:Leak
+ ...
+ fun:g_type_class_ref
+}
+{
+ glib/g_set_prgname
+ Memcheck:Leak
+ ...
+ fun:g_set_prgname
+}
+{
+ glib/g_intern_string
+ Memcheck:Leak
+ ...
+ fun:g_intern_string
+}
+{
+ glib/g_intern_static_string
+ Memcheck:Leak
+ ...
+ fun:g_intern_static_string
+}
+{
+ glib/g_quark_from_string
+ Memcheck:Leak
+ ...
+ fun:g_quark_from_string
+}
+{
+ glib/g_quark_from_static_string
+ Memcheck:Leak
+ ...
+ fun:g_quark_from_static_string
+}
+{
+ glib/get_dispatch
+ Memcheck:Leak
+ ...
+ fun:get_dispatch
+}
+{
+ glib/g_signal_handlers_destroy
+ Memcheck:Leak
+ ...
+ fun:g_signal_handlers_destroy
+}
+{
+ glib/g_data_initialize
+ Memcheck:Leak
+ ...
+ fun:g_data_initialize
+}
+{
+ glib/g_static_private_set
+ Memcheck:Leak
+ ...
+ fun:g_static_private_set
+}
+{
+ glib/g_child_watch_source_init_multi_threaded
+ Memcheck:Leak
+ ...
+ fun:g_child_watch_source_init_multi_threaded
+}
+{
+ glib/xdg_mime_init
+ Memcheck:Leak
+ ...
+ fun:xdg_mime_init
+}
+{
+ glib/GResolver
+ Memcheck:Leak
+ ...
+ fun:g_resolver_get_default
+}
+{
+ glib/g_main_context_push_thread_default
+ Memcheck:Leak
+ ...
+ fun:g_main_context_push_thread_default
+}
+{
+ glib/g_socket_connection_factory
+ Memcheck:Leak
+ ...
+ fun:g_socket_connection_factory_register_type
+}
+{
+ glib/g_get_language_names
+ Memcheck:Leak
+ ...
+ fun:g_get_language_names
+}
+{
+ glib/giomodules
+ Memcheck:Leak
+ ...
+ fun:_g_io_modules_ensure_loaded
+}
+{
+ glib/tlsinit
+ Memcheck:Leak
+ ...
+ fun:get_default_tls_backend
+}
+{
+ glib/tlscrypto
+ Memcheck:Leak
+ ...
+ fun:gcry_pthread_mutex_init
+}
+{
+ glib/tlscache
+ Memcheck:Leak
+ ...
+ fun:g_tls_backend_gnutls_cache_session_data
+}
+{
+ glib/tlspriority
+ Memcheck:Leak
+ ...
+ fun:g_tls_connection_gnutls_init_priorities
+}
+{
+ glib/gfileinfo
+ Memcheck:Leak
+ ...
+ fun:ensure_attribute_hash
+ fun:lookup_attribute
+}
+
+# probably using uninitialized memory as padding or something
+{
+ gnutls/handshake
+ Memcheck:Cond
+ ...
+ fun:gnutls_handshake
+}
+
+{
+ libxml2/xmlInitParser
+ Memcheck:Leak
+ ...
+ fun:xmlInitParser
+}
+{
+ libxml2/xmlInitializeDict
+ Memcheck:Leak
+ ...
+ fun:xmlInitializeDict
+}
+{
+ libxml2/xmlInitCharEncodingHandlers
+ Memcheck:Leak
+ ...
+ fun:xmlInitCharEncodingHandlers
+}
+{
+ libxml2/xmlNewCharEncodingHandler
+ Memcheck:Leak
+ ...
+ fun:xmlNewCharEncodingHandler
+}
+
+{
+ libsoup/interned_uri_schemes
+ Memcheck:Leak
+ ...
+ fun:g_ascii_strdown
+ fun:soup_uri_parse_scheme
+}
+{
+ libsoup/interned_headers
+ Memcheck:Leak
+ ...
+ fun:intern_header_name
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+SoupServer *server;
+SoupURI *base_uri;
+GMutex *server_mutex;
+
+static gboolean
+auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+ const char *username, const char *password, gpointer data)
+{
+ return !strcmp (username, "user") && !strcmp (password, "password");
+}
+
+static void
+forget_close (SoupMessage *msg, gpointer user_data)
+{
+ soup_message_headers_remove (msg->response_headers, "Connection");
+}
+
+static void
+close_socket (SoupMessage *msg, gpointer user_data)
+{
+ SoupSocket *sock = user_data;
+
+ soup_socket_disconnect (sock);
+}
+
+static void
+timeout_socket (SoupSocket *sock, gpointer user_data)
+{
+ soup_socket_disconnect (sock);
+}
+
+static void
+timeout_request_started (SoupServer *server, SoupMessage *msg,
+ SoupClientContext *client, gpointer user_data)
+{
+ SoupSocket *sock;
+ GMainContext *context = soup_server_get_async_context (server);
+ guint readable;
+
+ sock = soup_client_context_get_socket (client);
+ readable = g_signal_connect (sock, "readable",
+ G_CALLBACK (timeout_socket), NULL);
+ while (soup_socket_is_connected (sock))
+ g_main_context_iteration (context, TRUE);
+ g_signal_handler_disconnect (sock, readable);
+ g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL);
+}
+
+static void
+setup_timeout_persistent (SoupServer *server, SoupSocket *sock)
+{
+ char buf[1];
+ gsize nread;
+
+ /* In order for the test to work correctly, we have to
+ * close the connection *after* the client side writes
+ * the request. To ensure that this happens reliably,
+ * regardless of thread scheduling, we:
+ *
+ * 1. Try to read off the socket now, knowing it will
+ * fail (since the client is waiting for us to
+ * return a response). This will cause it to
+ * emit "readable" later.
+ * 2. Connect to the server's request-started signal.
+ * 3. Run an inner main loop from that signal handler
+ * until the socket emits "readable". (If we don't
+ * do this then it's possible the client's next
+ * request would be ready before we returned to
+ * the main loop, and so the signal would never be
+ * emitted.)
+ * 4. Close the socket.
+ */
+
+ soup_socket_read (sock, buf, 1, &nread, NULL, NULL);
+ g_signal_connect (server, "request-started",
+ G_CALLBACK (timeout_request_started), NULL);
+}
+
+static gboolean
+timeout_finish_message (gpointer msg)
+{
+ SoupServer *server = g_object_get_data (G_OBJECT (msg), "server");
+
+ soup_server_unpause_message (server, msg);
+ return FALSE;
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ SoupURI *uri = soup_message_get_uri (msg);
+
+ /* The way this gets used in the tests, we don't actually
+ * need to hold it through the whole function, so it's simpler
+ * to just release it right away.
+ */
+ g_mutex_lock (server_mutex);
+ g_mutex_unlock (server_mutex);
+
+ soup_message_headers_append (msg->response_headers,
+ "X-Handled-By", "server_callback");
+
+ if (!strcmp (path, "*")) {
+ debug_printf (1, " default server_callback got request for '*'!\n");
+ errors++;
+ soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (msg->method != SOUP_METHOD_GET) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (!strcmp (path, "/redirect")) {
+ soup_message_set_status (msg, SOUP_STATUS_FOUND);
+ soup_message_headers_append (msg->response_headers,
+ /* Kids: don't try this at home!
+ * RFC2616 says to use an
+ * absolute URI!
+ */
+ "Location", "/");
+ return;
+ }
+
+ if (g_str_has_prefix (path, "/content-length/")) {
+ gboolean too_long = strcmp (path, "/content-length/long") == 0;
+ gboolean no_close = strcmp (path, "/content-length/noclose") == 0;
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC, "foobar", 6);
+ if (too_long)
+ soup_message_headers_set_content_length (msg->response_headers, 9);
+ soup_message_headers_append (msg->response_headers,
+ "Connection", "close");
+
+ if (too_long) {
+ SoupSocket *sock;
+
+ /* soup-message-io will wait for us to add
+ * another chunk after the first, to fill out
+ * the declared Content-Length. Instead, we
+ * forcibly close the socket at that point.
+ */
+ sock = soup_client_context_get_socket (context);
+ g_signal_connect (msg, "wrote-chunk",
+ G_CALLBACK (close_socket), sock);
+ } else if (no_close) {
+ /* Remove the 'Connection: close' after writing
+ * the headers, so that when we check it after
+ * writing the body, we'll think we aren't
+ * supposed to close it.
+ */
+ g_signal_connect (msg, "wrote-headers",
+ G_CALLBACK (forget_close), NULL);
+ }
+ return;
+ }
+
+ if (!strcmp (path, "/timeout-persistent")) {
+ SoupSocket *sock;
+
+ sock = soup_client_context_get_socket (context);
+ setup_timeout_persistent (server, sock);
+ }
+
+ if (!strcmp (path, "/slow")) {
+ soup_server_pause_message (server, msg);
+ g_object_set_data (G_OBJECT (msg), "server", server);
+ soup_add_timeout (soup_server_get_async_context (server),
+ 1000, timeout_finish_message, msg);
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ if (!strcmp (uri->host, "foo")) {
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC, "foo-index", 9);
+ return;
+ } else {
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC, "index", 5);
+ return;
+ }
+}
+
+static void
+server_star_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ soup_message_headers_append (msg->response_headers,
+ "X-Handled-By", "star_callback");
+
+ if (strcmp (path, "*") != 0) {
+ debug_printf (1, " server_star_callback got request for '%s'!\n", path);
+ errors++;
+ soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (msg->method != SOUP_METHOD_OPTIONS) {
+ soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+/* Host header handling: client must be able to override the default
+ * value, server must be able to recognize different Host values.
+ * #539803.
+ */
+static void
+do_host_test (void)
+{
+ SoupSession *session;
+ SoupMessage *one, *two;
+
+ debug_printf (1, "Host handling\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+
+ one = soup_message_new_from_uri ("GET", base_uri);
+ two = soup_message_new_from_uri ("GET", base_uri);
+ soup_message_headers_replace (two->request_headers, "Host", "foo");
+
+ soup_session_send_message (session, one);
+ soup_session_send_message (session, two);
+
+ soup_test_session_abort_unref (session);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (one->status_code)) {
+ debug_printf (1, " Message 1 failed: %d %s\n",
+ one->status_code, one->reason_phrase);
+ errors++;
+ } else if (strcmp (one->response_body->data, "index") != 0) {
+ debug_printf (1, " Unexpected response to message 1: '%s'\n",
+ one->response_body->data);
+ errors++;
+ }
+ g_object_unref (one);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (two->status_code)) {
+ debug_printf (1, " Message 2 failed: %d %s\n",
+ two->status_code, two->reason_phrase);
+ errors++;
+ } else if (strcmp (two->response_body->data, "foo-index") != 0) {
+ debug_printf (1, " Unexpected response to message 2: '%s'\n",
+ two->response_body->data);
+ errors++;
+ }
+ g_object_unref (two);
+}
+
+/* Dropping the application's ref on the session from a callback
+ * should not cause the session to be freed at an incorrect time.
+ * (This test will crash if it fails.) #533473
+ */
+static void
+cu_one_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+ debug_printf (2, " Message 1 completed\n");
+ if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
+ debug_printf (1, " Unexpected status on Message 1: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_object_unref (session);
+}
+
+static gboolean
+cu_idle_quit (gpointer loop)
+{
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static void
+cu_two_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+ debug_printf (2, " Message 2 completed\n");
+ if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
+ debug_printf (1, " Unexpected status on Message 2: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_idle_add (cu_idle_quit, loop);
+}
+
+static void
+do_callback_unref_test (void)
+{
+ SoupServer *bad_server;
+ SoupAddress *addr;
+ SoupSession *session;
+ SoupMessage *one, *two;
+ GMainLoop *loop;
+ char *bad_uri;
+
+ debug_printf (1, "\nCallback unref handling\n");
+
+ /* Get a guaranteed-bad URI */
+ addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
+ soup_address_resolve_sync (addr, NULL);
+ bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
+ NULL);
+ g_object_unref (addr);
+
+ bad_uri = g_strdup_printf ("http://127.0.0.1:%u/",
+ soup_server_get_port (bad_server));
+ g_object_unref (bad_server);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ one = soup_message_new ("GET", bad_uri);
+ g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
+ two = soup_message_new ("GET", bad_uri);
+ g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
+ g_free (bad_uri);
+
+ soup_session_queue_message (session, one, cu_one_completed, loop);
+ soup_session_queue_message (session, two, cu_two_completed, loop);
+
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ if (session) {
+ g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+ debug_printf (1, " Session not destroyed?\n");
+ errors++;
+ g_object_unref (session);
+ }
+ if (one) {
+ g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
+ debug_printf (1, " Message 1 not destroyed?\n");
+ errors++;
+ g_object_unref (one);
+ }
+ if (two) {
+ g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
+ debug_printf (1, " Message 2 not destroyed?\n");
+ errors++;
+ g_object_unref (two);
+ }
+
+ /* Otherwise, if we haven't crashed, we're ok. */
+}
+
+/* SoupSession should clean up all signal handlers on a message after
+ * it is finished, allowing the message to be reused if desired.
+ * #559054
+ */
+static void
+ensure_no_signal_handlers (SoupMessage *msg, guint *signal_ids, guint n_signal_ids)
+{
+ int i;
+
+ for (i = 0; i < n_signal_ids; i++) {
+ if (g_signal_handler_find (msg, G_SIGNAL_MATCH_ID, signal_ids[i],
+ 0, NULL, NULL, NULL)) {
+ debug_printf (1, " Message has handler for '%s'\n",
+ g_signal_name (signal_ids[i]));
+ errors++;
+ }
+ }
+}
+
+static void
+reuse_test_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying)
+{
+ /* Get it wrong the first time, then succeed */
+ if (!retrying)
+ soup_auth_authenticate (auth, "user", "wrong password");
+ else
+ soup_auth_authenticate (auth, "user", "password");
+}
+
+static void
+do_msg_reuse_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupURI *uri;
+ guint *signal_ids, n_signal_ids;
+
+ debug_printf (1, "\nSoupMessage reuse\n");
+
+ signal_ids = g_signal_list_ids (SOUP_TYPE_MESSAGE, &n_signal_ids);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (reuse_test_authenticate), NULL);
+
+ debug_printf (1, " First message\n");
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ soup_session_send_message (session, msg);
+ ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
+
+ debug_printf (1, " Redirect message\n");
+ uri = soup_uri_new_with_base (base_uri, "/redirect");
+ soup_message_set_uri (msg, uri);
+ soup_uri_free (uri);
+ soup_session_send_message (session, msg);
+ if (!soup_uri_equal (soup_message_get_uri (msg), base_uri)) {
+ debug_printf (1, " Message did not get redirected!\n");
+ errors++;
+ }
+ ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
+
+ debug_printf (1, " Auth message\n");
+ uri = soup_uri_new_with_base (base_uri, "/auth");
+ soup_message_set_uri (msg, uri);
+ soup_uri_free (uri);
+ soup_session_send_message (session, msg);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " Message did not get authenticated!\n");
+ errors++;
+ }
+ ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
+
+ /* One last try to make sure the auth stuff got cleaned up */
+ debug_printf (1, " Last message\n");
+ soup_message_set_uri (msg, base_uri);
+ soup_session_send_message (session, msg);
+ ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
+
+ soup_test_session_abort_unref (session);
+ g_object_unref (msg);
+ g_free (signal_ids);
+}
+
+/* Server handlers for "*" work but are separate from handlers for
+ * all other URIs. #590751
+ */
+static void
+do_star_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupURI *star_uri;
+ const char *handled_by;
+
+ debug_printf (1, "\nOPTIONS *\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ star_uri = soup_uri_copy (base_uri);
+ soup_uri_set_path (star_uri, "*");
+
+ debug_printf (1, " Testing with no handler\n");
+ msg = soup_message_new_from_uri ("OPTIONS", star_uri);
+ soup_session_send_message (session, msg);
+
+ if (msg->status_code != SOUP_STATUS_NOT_FOUND) {
+ debug_printf (1, " Unexpected response: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ handled_by = soup_message_headers_get_one (msg->response_headers,
+ "X-Handled-By");
+ if (handled_by) {
+ /* Should have been rejected by SoupServer directly */
+ debug_printf (1, " Message reached handler '%s'\n",
+ handled_by);
+ errors++;
+ }
+ g_object_unref (msg);
+
+ soup_server_add_handler (server, "*", server_star_callback, NULL, NULL);
+
+ debug_printf (1, " Testing with handler\n");
+ msg = soup_message_new_from_uri ("OPTIONS", star_uri);
+ soup_session_send_message (session, msg);
+
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " Unexpected response: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ handled_by = soup_message_headers_get_one (msg->response_headers,
+ "X-Handled-By");
+ if (!handled_by) {
+ debug_printf (1, " Message did not reach handler!\n");
+ errors++;
+ } else if (strcmp (handled_by, "star_callback") != 0) {
+ debug_printf (1, " Message reached incorrect handler '%s'\n",
+ handled_by);
+ errors++;
+ }
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
+ soup_uri_free (star_uri);
+}
+
+/* Handle unexpectedly-early aborts. #596074, #618641 */
+static void
+ea_msg_completed_one (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+ debug_printf (2, " Message 1 completed\n");
+ if (msg->status_code != SOUP_STATUS_CANCELLED) {
+ debug_printf (1, " Unexpected status on Message 1: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_main_loop_quit (loop);
+}
+
+static gboolean
+ea_abort_session (gpointer session)
+{
+ soup_session_abort (session);
+ return FALSE;
+}
+
+static void
+ea_connection_state_changed (GObject *conn, GParamSpec *pspec, gpointer session)
+{
+ SoupConnectionState state;
+
+ g_object_get (conn, "state", &state, NULL);
+ if (state == SOUP_CONNECTION_CONNECTING) {
+ g_idle_add_full (G_PRIORITY_HIGH,
+ ea_abort_session,
+ session, NULL);
+ g_signal_handlers_disconnect_by_func (conn, ea_connection_state_changed, session);
+ }
+}
+
+static void
+ea_connection_created (SoupSession *session, GObject *conn, gpointer user_data)
+{
+ g_signal_connect (conn, "notify::state",
+ G_CALLBACK (ea_connection_state_changed), session);
+ g_signal_handlers_disconnect_by_func (session, ea_connection_created, user_data);
+}
+
+static void
+do_early_abort_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ GMainContext *context;
+ GMainLoop *loop;
+
+ debug_printf (1, "\nAbort with pending connection\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ msg = soup_message_new_from_uri ("GET", base_uri);
+
+ context = g_main_context_default ();
+ loop = g_main_loop_new (context, TRUE);
+ soup_session_queue_message (session, msg, ea_msg_completed_one, loop);
+ g_main_context_iteration (context, FALSE);
+
+ soup_session_abort (session);
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+ g_main_loop_unref (loop);
+ soup_test_session_abort_unref (session);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ msg = soup_message_new_from_uri ("GET", base_uri);
+
+ g_signal_connect (session, "connection-created",
+ G_CALLBACK (ea_connection_created), NULL);
+ soup_session_send_message (session, msg);
+ debug_printf (2, " Message 2 completed\n");
+
+ if (msg->status_code != SOUP_STATUS_CANCELLED) {
+ debug_printf (1, " Unexpected response: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_object_unref (msg);
+
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_content_length_framing_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupURI *request_uri;
+ goffset declared_length;
+
+ debug_printf (1, "\nInvalid Content-Length framing tests\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ debug_printf (1, " Content-Length larger than message body length\n");
+ request_uri = soup_uri_new_with_base (base_uri, "/content-length/long");
+ msg = soup_message_new_from_uri ("GET", request_uri);
+ soup_session_send_message (session, msg);
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " Unexpected response: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ } else {
+ declared_length = soup_message_headers_get_content_length (msg->response_headers);
+ debug_printf (2, " Content-Length: %lu, body: %s\n",
+ (gulong)declared_length, msg->response_body->data);
+ if (msg->response_body->length >= declared_length) {
+ debug_printf (1, " Body length %lu >= declared length %lu\n",
+ (gulong)msg->response_body->length,
+ (gulong)declared_length);
+ errors++;
+ }
+ }
+ soup_uri_free (request_uri);
+ g_object_unref (msg);
+
+ debug_printf (1, " Server claims 'Connection: close' but doesn't\n");
+ request_uri = soup_uri_new_with_base (base_uri, "/content-length/noclose");
+ msg = soup_message_new_from_uri ("GET", request_uri);
+ soup_session_send_message (session, msg);
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " Unexpected response: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ } else {
+ declared_length = soup_message_headers_get_content_length (msg->response_headers);
+ debug_printf (2, " Content-Length: %lu, body: %s\n",
+ (gulong)declared_length, msg->response_body->data);
+ if (msg->response_body->length != declared_length) {
+ debug_printf (1, " Body length %lu != declared length %lu\n",
+ (gulong)msg->response_body->length,
+ (gulong)declared_length);
+ errors++;
+ }
+ }
+ soup_uri_free (request_uri);
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_one_accept_language_test (const char *language, const char *expected_header)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ const char *val;
+
+ debug_printf (1, " LANGUAGE=%s\n", language);
+ g_setenv ("LANGUAGE", language, TRUE);
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
+ SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
+ NULL);
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ soup_session_send_message (session, msg);
+ soup_test_session_abort_unref (session);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " Message failed? %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ val = soup_message_headers_get_list (msg->request_headers,
+ "Accept-Language");
+ if (!val) {
+ debug_printf (1, " No Accept-Language set!\n");
+ errors++;
+ } else if (strcmp (val, expected_header) != 0) {
+ debug_printf (1, " Wrong Accept-Language: expected '%s', got '%s'\n",
+ expected_header, val);
+ errors++;
+ }
+
+ g_object_unref (msg);
+}
+
+static void
+do_accept_language_test (void)
+{
+ const char *orig_language;
+
+ debug_printf (1, "\nAutomatic Accept-Language processing\n");
+
+ orig_language = g_getenv ("LANGUAGE");
+ do_one_accept_language_test ("C", "en");
+ do_one_accept_language_test ("fr_FR", "fr-fr, fr;q=0.9");
+ do_one_accept_language_test ("fr_FR:de:en_US", "fr-fr, fr;q=0.9, de;q=0.8, en-us;q=0.7, en;q=0.6");
+
+ if (orig_language)
+ g_setenv ("LANGUAGE", orig_language, TRUE);
+ else
+ g_unsetenv ("LANGUAGE");
+}
+
+static void
+timeout_test_request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket, gpointer user_data)
+{
+ SoupSocket **sockets = user_data;
+ int i;
+
+ debug_printf (2, " msg %p => socket %p\n", msg, socket);
+ for (i = 0; i < 4; i++) {
+ if (!sockets[i]) {
+ /* We ref the socket to make sure that even if
+ * it gets disconnected, it doesn't get freed,
+ * since our checks would get messed up if the
+ * slice allocator reused the same address for
+ * two consecutive sockets.
+ */
+ sockets[i] = g_object_ref (socket);
+ return;
+ }
+ }
+
+ debug_printf (1, " socket queue overflowed!\n");
+ errors++;
+ soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
+}
+
+static void
+do_timeout_test_for_session (SoupSession *session)
+{
+ SoupMessage *msg;
+ SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL };
+ SoupURI *timeout_uri;
+ int i;
+
+ g_signal_connect (session, "request-started",
+ G_CALLBACK (timeout_test_request_started),
+ &sockets);
+
+ debug_printf (1, " First message\n");
+ timeout_uri = soup_uri_new_with_base (base_uri, "/timeout-persistent");
+ msg = soup_message_new_from_uri ("GET", timeout_uri);
+ soup_uri_free (timeout_uri);
+ soup_session_send_message (session, msg);
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " Unexpected response: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ if (sockets[1]) {
+ debug_printf (1, " Message was retried??\n");
+ errors++;
+ sockets[1] = sockets[2] = sockets[3] = NULL;
+ }
+ g_object_unref (msg);
+
+ debug_printf (1, " Second message\n");
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ soup_session_send_message (session, msg);
+ if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " Unexpected response: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ if (sockets[1] != sockets[0]) {
+ debug_printf (1, " Message was not retried on existing connection\n");
+ errors++;
+ } else if (!sockets[2]) {
+ debug_printf (1, " Message was not retried after disconnect\n");
+ errors++;
+ } else if (sockets[2] == sockets[1]) {
+ debug_printf (1, " Message was retried on closed connection??\n");
+ errors++;
+ } else if (sockets[3]) {
+ debug_printf (1, " Message was retried again??\n");
+ errors++;
+ }
+ g_object_unref (msg);
+
+ for (i = 0; sockets[i]; i++)
+ g_object_unref (sockets[i]);
+}
+
+static void
+do_persistent_connection_timeout_test (void)
+{
+ SoupSession *session;
+
+ debug_printf (1, "\nUnexpected timing out of persistent connections\n");
+
+ debug_printf (1, " Async session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_timeout_test_for_session (session);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " Sync session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ do_timeout_test_for_session (session);
+ soup_test_session_abort_unref (session);
+}
+
+static GMainLoop *max_conns_loop;
+static int msgs_done;
+#define MAX_CONNS 2
+#define TEST_CONNS (MAX_CONNS * 2)
+
+static gboolean
+idle_start_server (gpointer data)
+{
+ g_mutex_unlock (server_mutex);
+ return FALSE;
+}
+
+static gboolean
+quit_loop (gpointer data)
+{
+ g_main_loop_quit (max_conns_loop);
+ return FALSE;
+}
+
+static void
+max_conns_request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket, gpointer user_data)
+{
+ if (++msgs_done == MAX_CONNS)
+ g_timeout_add (100, quit_loop, NULL);
+}
+
+static void
+max_conns_message_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ if (++msgs_done == TEST_CONNS)
+ g_main_loop_quit (max_conns_loop);
+}
+
+static void
+do_max_conns_test_for_session (SoupSession *session)
+{
+ SoupMessage *msgs[TEST_CONNS];
+ int i;
+ guint timeout_id;
+
+ max_conns_loop = g_main_loop_new (NULL, TRUE);
+
+ g_mutex_lock (server_mutex);
+
+ g_signal_connect (session, "request-started",
+ G_CALLBACK (max_conns_request_started), NULL);
+ msgs_done = 0;
+ for (i = 0; i < TEST_CONNS; i++) {
+ msgs[i] = soup_message_new_from_uri ("GET", base_uri);
+ g_object_ref (msgs[i]);
+ soup_session_queue_message (session, msgs[i],
+ max_conns_message_complete, NULL);
+ }
+
+ g_main_loop_run (max_conns_loop);
+ if (msgs_done != MAX_CONNS) {
+ debug_printf (1, " Queued %d connections out of max %d?",
+ msgs_done, MAX_CONNS);
+ errors++;
+ }
+ g_signal_handlers_disconnect_by_func (session, max_conns_request_started, NULL);
+
+ msgs_done = 0;
+ g_idle_add (idle_start_server, NULL);
+ timeout_id = g_timeout_add (1000, quit_loop, NULL);
+ g_main_loop_run (max_conns_loop);
+
+ for (i = 0; i < TEST_CONNS; i++) {
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msgs[i]->status_code)) {
+ debug_printf (1, " Message %d failed? %d %s\n",
+ i, msgs[i]->status_code,
+ msgs[i]->reason_phrase ? msgs[i]->reason_phrase : "-");
+ errors++;
+ }
+ }
+
+ if (msgs_done != TEST_CONNS) {
+ /* Clean up so we don't get a spurious "Leaked
+ * session" error.
+ */
+ for (i = 0; i < TEST_CONNS; i++)
+ soup_session_cancel_message (session, msgs[i], SOUP_STATUS_CANCELLED);
+ g_main_loop_run (max_conns_loop);
+ g_source_remove (timeout_id);
+ }
+
+ g_main_loop_unref (max_conns_loop);
+
+ for (i = 0; i < TEST_CONNS; i++)
+ g_object_unref (msgs[i]);
+}
+
+static void
+do_max_conns_test (void)
+{
+ SoupSession *session;
+
+ debug_printf (1, "\nExceeding max-conns\n");
+
+ debug_printf (1, " Async session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_MAX_CONNS, MAX_CONNS,
+ NULL);
+ do_max_conns_test_for_session (session);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " Sync session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
+ SOUP_SESSION_MAX_CONNS, MAX_CONNS,
+ NULL);
+ do_max_conns_test_for_session (session);
+ soup_test_session_abort_unref (session);
+}
+
+static gboolean
+cancel_message_timeout (gpointer msg)
+{
+ SoupSession *session = g_object_get_data (G_OBJECT (msg), "session");
+
+ soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
+ g_object_unref (msg);
+ g_object_unref (session);
+ return FALSE;
+}
+
+static gpointer
+cancel_message_thread (gpointer msg)
+{
+ SoupSession *session = g_object_get_data (G_OBJECT (msg), "session");
+
+ g_usleep (100000); /* .1s */
+ soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
+ g_object_unref (msg);
+ g_object_unref (session);
+ return NULL;
+}
+
+static void
+do_cancel_while_reading_test_for_session (SoupSession *session)
+{
+ SoupMessage *msg;
+ GThread *thread = NULL;
+ SoupURI *uri;
+
+ uri = soup_uri_new_with_base (base_uri, "/slow");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ g_object_set_data (G_OBJECT (msg), "session", session);
+ g_object_ref (msg);
+ g_object_ref (session);
+ if (SOUP_IS_SESSION_ASYNC (session))
+ g_timeout_add (100, cancel_message_timeout, msg);
+ else
+ thread = g_thread_create (cancel_message_thread, msg, TRUE, NULL);
+
+ soup_session_send_message (session, msg);
+
+ if (msg->status_code != SOUP_STATUS_CANCELLED) {
+ debug_printf (1, " FAILED: %d %s (expected Cancelled)\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_object_unref (msg);
+
+ if (thread)
+ g_thread_join (thread);
+}
+
+static void
+do_cancel_while_reading_test (void)
+{
+ SoupSession *session;
+
+ debug_printf (1, "\nCancelling message while reading response\n");
+
+ debug_printf (1, " Async session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_cancel_while_reading_test_for_session (session);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " Sync session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ do_cancel_while_reading_test_for_session (session);
+ soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupAuthDomain *auth_domain;
+
+ test_init (argc, argv, NULL);
+
+ server_mutex = g_mutex_new ();
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ base_uri = soup_uri_new ("http://127.0.0.1/");
+ soup_uri_set_port (base_uri, soup_server_get_port (server));
+
+ auth_domain = soup_auth_domain_basic_new (
+ SOUP_AUTH_DOMAIN_REALM, "misc-test",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/auth",
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
+ NULL);
+ soup_server_add_auth_domain (server, auth_domain);
+ g_object_unref (auth_domain);
+
+ do_host_test ();
+ do_callback_unref_test ();
+ do_msg_reuse_test ();
+ do_star_test ();
+ do_early_abort_test ();
+ do_content_length_framing_test ();
+ do_accept_language_test ();
+ do_persistent_connection_timeout_test ();
+ do_max_conns_test ();
+ do_cancel_while_reading_test ();
+
+ soup_uri_free (base_uri);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ */
+
+/* This doesn't implement full server-side NTLM, and it mostly doesn't
+ * even test that the client is doing the crypto/encoding/etc parts of
+ * NTLM correctly. It only tests that the right message headers get
+ * set in the right messages.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-auth.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-server.h>
+#include <libsoup/soup-session-async.h>
+
+#include "test-utils.h"
+
+typedef enum {
+ NTLM_UNAUTHENTICATED,
+ NTLM_RECEIVED_REQUEST,
+ NTLM_SENT_CHALLENGE,
+ NTLM_AUTHENTICATED_ALICE,
+ NTLM_AUTHENTICATED_BOB
+} NTLMServerState;
+
+#define NTLM_REQUEST_START "TlRMTVNTUAABAAAA"
+#define NTLM_RESPONSE_START "TlRMTVNTUAADAAAA"
+
+#define NTLM_CHALLENGE "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="
+
+#define NTLM_RESPONSE_USER(response) ((response)[87] == 'h' ? NTLM_AUTHENTICATED_ALICE : NTLM_AUTHENTICATED_BOB)
+
+static void
+clear_state (gpointer connections, GObject *ex_connection)
+{
+ g_hash_table_remove (connections, ex_connection);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *client, gpointer data)
+{
+ GHashTable *connections = data;
+ SoupSocket *socket;
+ const char *auth;
+ NTLMServerState state, required_user = 0;
+ gboolean auth_required = FALSE, not_found = FALSE;
+ gboolean basic_allowed = FALSE, ntlm_allowed = FALSE;
+
+ if (msg->method != SOUP_METHOD_GET) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (!strncmp (path, "/alice", 6)) {
+ auth_required = TRUE;
+ ntlm_allowed = TRUE;
+ required_user = NTLM_AUTHENTICATED_ALICE;
+ } else if (!strncmp (path, "/bob", 4)) {
+ auth_required = TRUE;
+ ntlm_allowed = TRUE;
+ required_user = NTLM_AUTHENTICATED_BOB;
+ } else if (!strncmp (path, "/either", 7)) {
+ auth_required = TRUE;
+ ntlm_allowed = basic_allowed = TRUE;
+ } else if (!strncmp (path, "/basic", 6)) {
+ auth_required = TRUE;
+ basic_allowed = TRUE;
+ }
+
+ if (strstr (path, "/404"))
+ not_found = TRUE;
+
+ socket = soup_client_context_get_socket (client);
+ state = GPOINTER_TO_INT (g_hash_table_lookup (connections, socket));
+ auth = soup_message_headers_get_one (msg->request_headers,
+ "Authorization");
+
+ if (auth) {
+ if (!strncmp (auth, "NTLM ", 5)) {
+ if (!strncmp (auth + 5, NTLM_REQUEST_START,
+ strlen (NTLM_REQUEST_START))) {
+ state = NTLM_RECEIVED_REQUEST;
+ /* If they start, they must finish */
+ auth_required = ntlm_allowed = TRUE;
+ basic_allowed = FALSE;
+ } else if (state == NTLM_SENT_CHALLENGE &&
+ !strncmp (auth + 5, NTLM_RESPONSE_START,
+ strlen (NTLM_RESPONSE_START))) {
+ state = NTLM_RESPONSE_USER (auth + 5);
+ } else
+ state = NTLM_UNAUTHENTICATED;
+ } else if (!strncmp (auth, "Basic ", 6) && basic_allowed) {
+ gsize len;
+ char *decoded = (char *)g_base64_decode (auth + 6, &len);
+
+ if (!strncmp (decoded, "alice:password", len) ||
+ !strncmp (decoded, "bob:password", len))
+ auth_required = FALSE;
+ g_free (decoded);
+ }
+ }
+
+ if (ntlm_allowed && state > NTLM_SENT_CHALLENGE &&
+ (!required_user || required_user == state))
+ auth_required = FALSE;
+
+ if (auth_required) {
+ soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
+
+ if (basic_allowed) {
+ soup_message_headers_append (msg->response_headers,
+ "WWW-Authenticate",
+ "Basic realm=\"ntlm-test\"");
+ }
+
+ if (state == NTLM_RECEIVED_REQUEST) {
+ soup_message_headers_append (msg->response_headers,
+ "WWW-Authenticate",
+ "NTLM " NTLM_CHALLENGE);
+ state = NTLM_SENT_CHALLENGE;
+ } else if (ntlm_allowed) {
+ soup_message_headers_append (msg->response_headers,
+ "WWW-Authenticate", "NTLM");
+ soup_message_headers_append (msg->response_headers,
+ "Connection", "close");
+ }
+ } else {
+ if (not_found)
+ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+ else {
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "OK\r\n", 4);
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ }
+ }
+
+ g_hash_table_insert (connections, socket, GINT_TO_POINTER (state));
+ g_object_weak_ref (G_OBJECT (socket), clear_state, connections);
+}
+
+static void
+authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer user)
+{
+ soup_auth_authenticate (auth, user, "password");
+}
+
+typedef struct {
+ gboolean got_ntlm_prompt;
+ gboolean got_basic_prompt;
+ gboolean sent_ntlm_request;
+ gboolean got_ntlm_challenge;
+ gboolean sent_ntlm_response;
+ gboolean sent_basic_response;
+} NTLMState;
+
+static void
+prompt_check (SoupMessage *msg, gpointer user_data)
+{
+ NTLMState *state = user_data;
+ const char *header;
+
+ header = soup_message_headers_get_list (msg->response_headers,
+ "WWW-Authenticate");
+ if (header && strstr (header, "Basic "))
+ state->got_basic_prompt = TRUE;
+ if (!state->sent_ntlm_request) {
+ if (header && strstr (header, "NTLM") &&
+ !strstr (header, NTLM_CHALLENGE))
+ state->got_ntlm_prompt = TRUE;
+ }
+}
+
+static void
+challenge_check (SoupMessage *msg, gpointer user_data)
+{
+ NTLMState *state = user_data;
+ const char *header;
+
+ header = soup_message_headers_get_list (msg->response_headers,
+ "WWW-Authenticate");
+ if (header && !strncmp (header, "NTLM ", 5))
+ state->got_ntlm_challenge = TRUE;
+}
+
+static void
+request_check (SoupMessage *msg, gpointer user_data)
+{
+ NTLMState *state = user_data;
+ const char *header;
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Authorization");
+ if (header && !strncmp (header, "NTLM " NTLM_REQUEST_START,
+ strlen ("NTLM " NTLM_REQUEST_START)))
+ state->sent_ntlm_request = TRUE;
+}
+
+static void
+response_check (SoupMessage *msg, gpointer user_data)
+{
+ NTLMState *state = user_data;
+ const char *header;
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Authorization");
+ if (header && !strncmp (header, "NTLM " NTLM_RESPONSE_START,
+ strlen ("NTLM " NTLM_RESPONSE_START)))
+ state->sent_ntlm_response = TRUE;
+ if (header && !strncmp (header, "Basic ", 6))
+ state->sent_basic_response = TRUE;
+}
+
+static void
+do_message (SoupSession *session, SoupURI *base_uri, const char *path,
+ gboolean get_ntlm_prompt, gboolean do_ntlm,
+ gboolean get_basic_prompt, gboolean do_basic,
+ guint status_code)
+{
+ SoupURI *uri;
+ SoupMessage *msg;
+ NTLMState state = { FALSE, FALSE, FALSE, FALSE };
+
+ uri = soup_uri_new_with_base (base_uri, path);
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (prompt_check), &state);
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (challenge_check), &state);
+ g_signal_connect (msg, "wrote-headers",
+ G_CALLBACK (request_check), &state);
+ g_signal_connect (msg, "wrote-headers",
+ G_CALLBACK (response_check), &state);
+
+ soup_session_send_message (session, msg);
+ debug_printf (1, " %-10s -> ", path);
+
+ if (state.got_ntlm_prompt) {
+ debug_printf (1, " NTLM_PROMPT");
+ if (!get_ntlm_prompt) {
+ debug_printf (1, "???");
+ errors++;
+ }
+ } else if (get_ntlm_prompt) {
+ debug_printf (1, " no-ntlm-prompt???");
+ errors++;
+ }
+
+ if (state.got_basic_prompt) {
+ debug_printf (1, " BASIC_PROMPT");
+ if (!get_basic_prompt) {
+ debug_printf (1, "???");
+ errors++;
+ }
+ } else if (get_basic_prompt) {
+ debug_printf (1, " no-basic-prompt???");
+ errors++;
+ }
+
+ if (state.sent_ntlm_request) {
+ debug_printf (1, " REQUEST");
+ if (!do_ntlm) {
+ debug_printf (1, "???");
+ errors++;
+ }
+ } else if (do_ntlm) {
+ debug_printf (1, " no-request???");
+ errors++;
+ }
+
+ if (state.got_ntlm_challenge) {
+ debug_printf (1, " CHALLENGE");
+ if (!do_ntlm) {
+ debug_printf (1, "???");
+ errors++;
+ }
+ } else if (do_ntlm) {
+ debug_printf (1, " no-challenge???");
+ errors++;
+ }
+
+ if (state.sent_ntlm_response) {
+ debug_printf (1, " NTLM_RESPONSE");
+ if (!do_ntlm) {
+ debug_printf (1, "???");
+ errors++;
+ }
+ } else if (do_ntlm) {
+ debug_printf (1, " no-ntlm-response???");
+ errors++;
+ }
+
+ if (state.sent_basic_response) {
+ debug_printf (1, " BASIC_RESPONSE");
+ if (!do_basic) {
+ debug_printf (1, "???");
+ errors++;
+ }
+ } else if (do_basic) {
+ debug_printf (1, " no-basic-response???");
+ errors++;
+ }
+
+ debug_printf (1, " -> %s", msg->reason_phrase);
+ if (msg->status_code != status_code) {
+ debug_printf (1, "???");
+ errors++;
+ }
+ debug_printf (1, "\n");
+
+ g_object_unref (msg);
+}
+
+static void
+do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
+{
+ SoupSession *session;
+ gboolean alice = use_ntlm && !strcmp (user, "alice");
+ gboolean bob = use_ntlm && !strcmp (user, "bob");
+
+ g_return_if_fail (use_ntlm || !alice);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ if (use_ntlm)
+ soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
+
+ if (user) {
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), (char *)user);
+ }
+
+ do_message (session, base_uri, "/noauth",
+ FALSE, use_ntlm,
+ FALSE, FALSE,
+ SOUP_STATUS_OK);
+ do_message (session, base_uri, "/alice",
+ !use_ntlm || bob, FALSE,
+ FALSE, FALSE,
+ alice ? SOUP_STATUS_OK :
+ SOUP_STATUS_UNAUTHORIZED);
+ do_message (session, base_uri, "/alice/404",
+ !use_ntlm, bob,
+ FALSE, FALSE,
+ alice ? SOUP_STATUS_NOT_FOUND :
+ SOUP_STATUS_UNAUTHORIZED);
+ do_message (session, base_uri, "/alice",
+ !use_ntlm, bob,
+ FALSE, FALSE,
+ alice ? SOUP_STATUS_OK :
+ SOUP_STATUS_UNAUTHORIZED);
+ do_message (session, base_uri, "/bob",
+ !use_ntlm || alice, bob,
+ FALSE, FALSE,
+ bob ? SOUP_STATUS_OK :
+ SOUP_STATUS_UNAUTHORIZED);
+ do_message (session, base_uri, "/alice",
+ !use_ntlm || bob, alice,
+ FALSE, FALSE,
+ alice ? SOUP_STATUS_OK :
+ SOUP_STATUS_UNAUTHORIZED);
+ do_message (session, base_uri, "/basic",
+ FALSE, bob,
+ TRUE, user != NULL,
+ user != NULL ? SOUP_STATUS_OK :
+ SOUP_STATUS_UNAUTHORIZED);
+ do_message (session, base_uri, "/either",
+ !use_ntlm, FALSE,
+ !use_ntlm, !use_ntlm && user != NULL,
+ user != NULL ? SOUP_STATUS_OK :
+ SOUP_STATUS_UNAUTHORIZED);
+
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_ntlm_tests (SoupURI *base_uri)
+{
+ debug_printf (1, "Round 1: Non-NTLM Connection, no auth\n");
+ do_ntlm_round (base_uri, FALSE, NULL);
+ debug_printf (1, "Round 2: NTLM Connection, user=alice\n");
+ do_ntlm_round (base_uri, TRUE, "alice");
+ debug_printf (1, "Round 3: NTLM Connection, user=bob\n");
+ do_ntlm_round (base_uri, TRUE, "bob");
+ debug_printf (1, "Round 4: Non-NTLM Connection, user=alice\n");
+ do_ntlm_round (base_uri, FALSE, "alice");
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ SoupServer *server;
+ GHashTable *connections;
+ SoupURI *uri;
+
+ test_init (argc, argv, NULL);
+
+ server = soup_test_server_new (FALSE);
+ connections = g_hash_table_new (NULL, NULL);
+ soup_server_add_handler (server, NULL,
+ server_callback, connections, NULL);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ uri = soup_uri_new ("http://127.0.0.1/");
+ soup_uri_set_port (uri, soup_server_get_port (server));
+ do_ntlm_tests (uri);
+ soup_uri_free (uri);
+
+ g_main_loop_unref (loop);
+
+ soup_test_server_quit_unref (server);
+ test_cleanup ();
+ g_hash_table_destroy (connections);
+
+ return errors != 0;
+}
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libsoup/soup.h"
+#include "test-utils.h"
+
+typedef struct {
+ const char *explanation;
+ const char *url;
+ const guint final_status;
+} SoupProxyTest;
+
+static SoupProxyTest tests[] = {
+ { "GET -> 200", "", SOUP_STATUS_OK },
+ { "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND },
+ { "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK },
+ { "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED },
+ { "GET -> 403", "http://no-such-hostname.xx/", SOUP_STATUS_FORBIDDEN },
+};
+static int ntests = sizeof (tests) / sizeof (tests[0]);
+
+#define HTTP_SERVER "http://127.0.0.1:47524"
+#define HTTPS_SERVER "https://127.0.0.1:47525"
+
+enum {
+ SIMPLE_PROXY,
+ AUTH_PROXY,
+ UNAUTH_PROXY
+};
+static const char *proxies[] = {
+ "http://127.0.0.1:47526",
+ "http://127.0.0.1:47527",
+ "http://127.0.0.1:47528"
+};
+static const char *proxy_names[] = {
+ "simple proxy",
+ "authenticated proxy",
+ "unauthenticatable-to proxy"
+};
+
+static void
+authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
+ if (soup_auth_is_for_proxy (auth)) {
+ debug_printf (1, " got proxy auth object for 401!\n");
+ errors++;
+ }
+ } else if (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
+ if (!soup_auth_is_for_proxy (auth)) {
+ debug_printf (1, " got regular auth object for 407!\n");
+ errors++;
+ }
+ } else {
+ debug_printf (1, " got authenticate signal with status %d\n",
+ msg->status_code);
+ errors++;
+ }
+
+ if (!retrying)
+ soup_auth_authenticate (auth, "user1", "realm1");
+}
+
+static void
+set_close_on_connect (SoupSession *session, SoupMessage *msg,
+ SoupSocket *sock, gpointer user_data)
+{
+ /* This is used to test that we can handle the server closing
+ * the connection when returning a 407 in response to a
+ * CONNECT. (Rude!)
+ */
+ if (msg->method == SOUP_METHOD_CONNECT) {
+ soup_message_headers_append (msg->request_headers,
+ "Connection", "close");
+ }
+}
+
+static void
+test_url (const char *url, int proxy, guint expected,
+ gboolean sync, gboolean close)
+{
+ SoupSession *session;
+ SoupURI *proxy_uri;
+ SoupMessage *msg;
+
+ if (!tls_available && g_str_has_prefix (url, "https:"))
+ return;
+
+ debug_printf (1, " GET %s via %s%s\n", url, proxy_names[proxy],
+ close ? " (with Connection: close)" : "");
+ if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN)
+ expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
+
+ /* We create a new session for each request to ensure that
+ * connections/auth aren't cached between tests.
+ */
+ proxy_uri = soup_uri_new (proxies[proxy]);
+ session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_PROXY_URI, proxy_uri,
+ NULL);
+ soup_uri_free (proxy_uri);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), NULL);
+ if (close) {
+ g_signal_connect (session, "request-started",
+ G_CALLBACK (set_close_on_connect), NULL);
+ }
+
+ msg = soup_message_new (SOUP_METHOD_GET, url);
+ if (!msg) {
+ fprintf (stderr, "proxy-test: Could not parse URI\n");
+ exit (1);
+ }
+
+ soup_session_send_message (session, msg);
+
+ debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase);
+ if (msg->status_code != expected) {
+ debug_printf (1, " EXPECTED %d!\n", expected);
+ errors++;
+ }
+
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+run_test (int i, gboolean sync)
+{
+ char *http_url, *https_url;
+
+ debug_printf (1, "Test %d: %s (%s)\n", i + 1, tests[i].explanation,
+ sync ? "sync" : "async");
+
+ if (!strncmp (tests[i].url, "http", 4)) {
+ http_url = g_strdup (tests[i].url);
+ https_url = g_strdup_printf ("https%s", tests[i].url + 4);
+ } else {
+ http_url = g_strconcat (HTTP_SERVER, tests[i].url, NULL);
+ https_url = g_strconcat (HTTPS_SERVER, tests[i].url, NULL);
+ }
+ test_url (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
+ test_url (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
+ test_url (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
+ test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
+ test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE);
+ test_url (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
+ test_url (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
+
+ g_free (http_url);
+ g_free (https_url);
+
+ debug_printf (1, "\n");
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ SoupURI *uri = soup_message_get_uri (msg);
+
+ soup_message_set_status (msg, uri->fragment ? SOUP_STATUS_BAD_REQUEST : SOUP_STATUS_OK);
+}
+
+static void
+do_proxy_fragment_test (SoupURI *base_uri)
+{
+ SoupSession *session;
+ SoupURI *proxy_uri, *req_uri;
+ SoupMessage *msg;
+
+ debug_printf (1, "\nTesting request with fragment via proxy\n");
+
+ proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_PROXY_URI, proxy_uri,
+ NULL);
+ soup_uri_free (proxy_uri);
+
+ req_uri = soup_uri_new_with_base (base_uri, "/#foo");
+ msg = soup_message_new_from_uri (SOUP_METHOD_GET, req_uri);
+ soup_uri_free (req_uri);
+ soup_session_send_message (session, msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " unexpected status %d %s!\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_proxy_redirect_test (void)
+{
+ SoupSession *session;
+ SoupURI *proxy_uri, *req_uri, *new_uri;
+ SoupMessage *msg;
+
+ debug_printf (1, "\nTesting redirection through proxy\n");
+
+ proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_PROXY_URI, proxy_uri,
+ NULL);
+ soup_uri_free (proxy_uri);
+
+ req_uri = soup_uri_new (HTTPS_SERVER);
+ soup_uri_set_path (req_uri, "/redirected");
+ msg = soup_message_new_from_uri (SOUP_METHOD_GET, req_uri);
+ soup_message_headers_append (msg->request_headers,
+ "Connection", "close");
+ soup_session_send_message (session, msg);
+
+ new_uri = soup_message_get_uri (msg);
+ if (!strcmp (req_uri->path, new_uri->path)) {
+ debug_printf (1, " message was not redirected!\n");
+ errors++;
+ }
+ soup_uri_free (req_uri);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " unexpected status %d %s!\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ SoupURI *base_uri;
+ int i;
+
+ test_init (argc, argv, NULL);
+ apache_init ();
+
+ for (i = 0; i < ntests; i++) {
+ run_test (i, FALSE);
+ run_test (i, TRUE);
+ }
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ base_uri = soup_uri_new ("http://127.0.0.1/");
+ soup_uri_set_port (base_uri, soup_server_get_port (server));
+
+ do_proxy_fragment_test (base_uri);
+ do_proxy_redirect_test ();
+
+ soup_uri_free (base_uri);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libsoup/soup.h"
+#include "libsoup/soup-session.h"
+
+#include "test-utils.h"
+
+static SoupBuffer *correct_response;
+
+static void
+authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ if (!retrying)
+ soup_auth_authenticate (auth, "user2", "realm2");
+}
+
+static void
+get_correct_response (const char *uri)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ msg = soup_message_new (SOUP_METHOD_GET, uri);
+ soup_session_send_message (session, msg);
+ if (msg->status_code != SOUP_STATUS_OK) {
+ fprintf (stderr, "Could not fetch %s: %d %s\n", uri,
+ msg->status_code, msg->reason_phrase);
+ exit (1);
+ }
+
+ correct_response = soup_message_body_flatten (msg->response_body);
+
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
+}
+
+/* Pull API version 1: fully-async. More like a "poke" API. Rather
+ * than having SoupMessage emit "got_chunk" signals whenever it wants,
+ * we stop it after it finishes reading the message headers, and then
+ * tell it when we want to hear about new chunks.
+ */
+
+typedef struct {
+ GMainLoop *loop;
+ SoupSession *session;
+ SoupMessage *msg;
+ guint timeout;
+ gboolean chunks_ready;
+ gboolean chunk_wanted;
+ gboolean did_first_timeout;
+ gsize read_so_far;
+ guint expected_status;
+} FullyAsyncData;
+
+static void fully_async_got_headers (SoupMessage *msg, gpointer user_data);
+static void fully_async_got_chunk (SoupMessage *msg, SoupBuffer *chunk,
+ gpointer user_data);
+static void fully_async_finished (SoupSession *session, SoupMessage *msg,
+ gpointer user_data);
+static gboolean fully_async_request_chunk (gpointer user_data);
+
+static void
+do_fully_async_test (SoupSession *session,
+ const char *base_uri, const char *sub_uri,
+ gboolean fast_request, guint expected_status)
+{
+ GMainLoop *loop;
+ FullyAsyncData ad;
+ SoupMessage *msg;
+ char *uri;
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ uri = g_build_filename (base_uri, sub_uri, NULL);
+ debug_printf (1, "GET %s\n", uri);
+
+ msg = soup_message_new (SOUP_METHOD_GET, uri);
+ g_free (uri);
+
+ ad.loop = loop;
+ ad.session = session;
+ ad.msg = msg;
+ ad.chunks_ready = FALSE;
+ ad.chunk_wanted = FALSE;
+ ad.did_first_timeout = FALSE;
+ ad.read_so_far = 0;
+ ad.expected_status = expected_status;
+
+ /* Since we aren't going to look at the final value of
+ * msg->response_body, we tell libsoup to not even bother
+ * generating it.
+ */
+ soup_message_body_set_accumulate (msg->response_body, FALSE);
+
+ /* Connect to "got_headers", from which we'll decide where to
+ * go next.
+ */
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (fully_async_got_headers), &ad);
+
+ /* Queue the request */
+ soup_session_queue_message (session, msg, fully_async_finished, &ad);
+
+ /* In a real program, we'd probably just return at this point.
+ * Eventually the caller would return all the way to the main
+ * loop, and then eventually, some event would cause the
+ * application to request a chunk of data from the message
+ * response.
+ *
+ * In our test program, there is no "real" main loop, so we
+ * had to create our own. We use a timeout to represent the
+ * event that causes the app to decide to request another body
+ * chunk. We use short timeouts in one set of tests, and long
+ * ones in another, to test both the
+ * chunk-requested-before-its-been-read and
+ * chunk-read-before-its-been-requested cases.
+ */
+ ad.timeout = g_timeout_add (fast_request ? 0 : 100,
+ fully_async_request_chunk, &ad);
+ g_main_loop_run (ad.loop);
+ g_main_loop_unref (ad.loop);
+}
+
+static gboolean
+fully_async_request_chunk (gpointer user_data)
+{
+ FullyAsyncData *ad = user_data;
+
+ if (!ad->did_first_timeout) {
+ debug_printf (1, " first timeout\n");
+ ad->did_first_timeout = TRUE;
+ } else
+ debug_printf (2, " timeout\n");
+ ad->timeout = 0;
+
+ /* ad->chunks_ready and ad->chunk_wanted are used because
+ * there's a race condition between the application requesting
+ * the first chunk, and the message reaching a point where
+ * it's actually ready to read chunks. If chunks_ready has
+ * been set, we can just call soup_session_unpause_message() to
+ * cause the first chunk to be read. But if it's not, we just
+ * set chunk_wanted, to let the got_headers handler below know
+ * that a chunk has already been requested.
+ */
+ if (ad->chunks_ready)
+ soup_session_unpause_message (ad->session, ad->msg);
+ else
+ ad->chunk_wanted = TRUE;
+
+ return FALSE;
+}
+
+static void
+fully_async_got_headers (SoupMessage *msg, gpointer user_data)
+{
+ FullyAsyncData *ad = user_data;
+
+ debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase);
+ if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
+ /* Let soup handle this one; this got_headers handler
+ * will get called again next time around.
+ */
+ return;
+ } else if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " unexpected status: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ return;
+ }
+
+ /* OK, we're happy with the response. So, we connect to
+ * "got_chunk". If there has already been a chunk requested,
+ * we let I/O continue; but if there hasn't, we pause now
+ * until one is requested.
+ */
+ ad->chunks_ready = TRUE;
+ g_signal_connect (msg, "got_chunk",
+ G_CALLBACK (fully_async_got_chunk), ad);
+ if (!ad->chunk_wanted)
+ soup_session_pause_message (ad->session, msg);
+}
+
+static void
+fully_async_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
+{
+ FullyAsyncData *ad = user_data;
+
+ debug_printf (2, " got chunk from %lu - %lu\n",
+ (unsigned long) ad->read_so_far,
+ (unsigned long) ad->read_so_far + chunk->length);
+
+ /* We've got a chunk, let's process it. In the case of the
+ * test program, that means comparing it against
+ * correct_response to make sure that we got the right data.
+ */
+ if (ad->read_so_far + chunk->length > correct_response->length) {
+ debug_printf (1, " read too far! (%lu > %lu)\n",
+ (unsigned long) (ad->read_so_far + chunk->length),
+ (unsigned long) correct_response->length);
+ errors++;
+ } else if (memcmp (chunk->data,
+ correct_response->data + ad->read_so_far,
+ chunk->length) != 0) {
+ debug_printf (1, " data mismatch in block starting at %lu\n",
+ (unsigned long) ad->read_so_far);
+ errors++;
+ }
+ ad->read_so_far += chunk->length;
+
+ /* Now pause I/O, and prepare to read another chunk later.
+ * (Again, the timeout just abstractly represents the idea of
+ * the application requesting another chunk at some random
+ * point in the future. You wouldn't be using a timeout in a
+ * real program.)
+ */
+ soup_session_pause_message (ad->session, msg);
+ ad->chunk_wanted = FALSE;
+
+ ad->timeout = g_timeout_add (10, fully_async_request_chunk, ad);
+}
+
+static void
+fully_async_finished (SoupSession *session, SoupMessage *msg,
+ gpointer user_data)
+{
+ FullyAsyncData *ad = user_data;
+
+ if (msg->status_code != ad->expected_status) {
+ debug_printf (1, " unexpected final status: %d %s !\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+
+ if (ad->timeout != 0)
+ g_source_remove (ad->timeout);
+
+ /* Since our test program is only running the loop for the
+ * purpose of this one test, we quit the loop once the
+ * test is done.
+ */
+ g_main_loop_quit (ad->loop);
+}
+
+
+/* Pull API version 2: synchronous pull API via async I/O. */
+
+typedef struct {
+ GMainLoop *loop;
+ SoupSession *session;
+ SoupBuffer *chunk;
+} SyncAsyncData;
+
+static void sync_async_send (SoupSession *session,
+ SoupMessage *msg);
+static gboolean sync_async_is_finished(SoupMessage *msg);
+static SoupBuffer *sync_async_read_chunk (SoupMessage *msg);
+static void sync_async_cleanup (SoupMessage *msg);
+
+static void sync_async_got_headers (SoupMessage *msg, gpointer user_data);
+static void sync_async_copy_chunk (SoupMessage *msg, SoupBuffer *chunk,
+ gpointer user_data);
+static void sync_async_finished (SoupSession *session, SoupMessage *msg,
+ gpointer user_data);
+
+static void
+do_synchronously_async_test (SoupSession *session,
+ const char *base_uri, const char *sub_uri,
+ guint expected_status)
+{
+ SoupMessage *msg;
+ char *uri;
+ gsize read_so_far;
+ SoupBuffer *chunk;
+
+ uri = g_build_filename (base_uri, sub_uri, NULL);
+ debug_printf (1, "GET %s\n", uri);
+
+ msg = soup_message_new (SOUP_METHOD_GET, uri);
+ g_free (uri);
+
+ /* As in the fully-async case, we turn off accumulate, as an
+ * optimization.
+ */
+ soup_message_body_set_accumulate (msg->response_body, FALSE);
+
+ /* Send the message, get back headers */
+ sync_async_send (session, msg);
+ if (sync_async_is_finished (msg) &&
+ expected_status == SOUP_STATUS_OK) {
+ debug_printf (1, " finished without reading response!\n");
+ errors++;
+ } else if (!sync_async_is_finished (msg) &&
+ expected_status != SOUP_STATUS_OK) {
+ debug_printf (1, " request failed to fail!\n");
+ errors++;
+ }
+
+ /* Now we're ready to read the response body (though we could
+ * put that off until later if we really wanted).
+ */
+ read_so_far = 0;
+ while ((chunk = sync_async_read_chunk (msg))) {
+ debug_printf (2, " read chunk from %lu - %lu\n",
+ (unsigned long) read_so_far,
+ (unsigned long) read_so_far + chunk->length);
+
+ if (read_so_far + chunk->length > correct_response->length) {
+ debug_printf (1, " read too far! (%lu > %lu)\n",
+ (unsigned long) read_so_far + chunk->length,
+ (unsigned long) correct_response->length);
+ errors++;
+ } else if (memcmp (chunk->data,
+ correct_response->data + read_so_far,
+ chunk->length) != 0) {
+ debug_printf (1, " data mismatch in block starting at %lu\n",
+ (unsigned long) read_so_far);
+ errors++;
+ }
+ read_so_far += chunk->length;
+ soup_buffer_free (chunk);
+ }
+
+ if (!sync_async_is_finished (msg) ||
+ (msg->status_code == SOUP_STATUS_OK &&
+ read_so_far != correct_response->length)) {
+ debug_printf (1, " loop ended before message was fully read!\n");
+ errors++;
+ } else if (msg->status_code != expected_status) {
+ debug_printf (1, " unexpected final status: %d %s !\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+
+ sync_async_cleanup (msg);
+ g_object_unref (msg);
+}
+
+/* Sends @msg on async session @session and returns after the headers
+ * of a successful response (or the complete body of a failed
+ * response) have been read.
+ */
+static void
+sync_async_send (SoupSession *session, SoupMessage *msg)
+{
+ SyncAsyncData *ad;
+
+ ad = g_new0 (SyncAsyncData, 1);
+ g_object_set_data (G_OBJECT (msg), "SyncAsyncData", ad);
+
+ /* In this case, unlike the fully-async case, the loop
+ * actually belongs to us, not the application; it will only
+ * be run when we're waiting for chunks, not at other times.
+ *
+ * If session has an async_context associated with it, we'd
+ * want to pass that, rather than NULL, here.
+ */
+ ad->loop = g_main_loop_new (NULL, FALSE);
+ ad->session = session;
+
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (sync_async_got_headers), ad);
+
+ /* Start the request by queuing it and then running our main
+ * loop. Note: we have to use soup_session_queue_message()
+ * here; soup_session_send_message() won't work, for several
+ * reasons. Also, since soup_session_queue_message() steals a
+ * ref to the message and then unrefs it after invoking the
+ * callback, we have to add an extra ref before calling it.
+ */
+ g_object_ref (msg);
+ soup_session_queue_message (session, msg, sync_async_finished, ad);
+ g_main_loop_run (ad->loop);
+
+ /* At this point, one of two things has happened; either the
+ * got_headers handler got headers it liked, and so stopped
+ * the loop, or else the message was fully processed without
+ * the got_headers handler interrupting it, and so the final
+ * callback (sync_async_finished) was invoked, and stopped the
+ * loop.
+ *
+ * Either way, we're done, so we return to the caller.
+ */
+}
+
+static void
+sync_async_got_headers (SoupMessage *msg, gpointer user_data)
+{
+ SyncAsyncData *ad = user_data;
+
+ debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase);
+ if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
+ /* Let soup handle this one; this got_headers handler
+ * will get called again next time around.
+ */
+ return;
+ } else if (msg->status_code != SOUP_STATUS_OK) {
+ debug_printf (1, " unexpected status: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ return;
+ }
+
+ /* Stop I/O and return to the caller */
+ soup_session_pause_message (ad->session, msg);
+ g_main_loop_quit (ad->loop);
+}
+
+static gboolean
+sync_async_is_finished (SoupMessage *msg)
+{
+ SyncAsyncData *ad = g_object_get_data (G_OBJECT (msg), "SyncAsyncData");
+
+ /* sync_async_finished clears ad->loop */
+ return ad->loop == NULL;
+}
+
+/* Tries to read a chunk. Returns %NULL on error/end-of-response. */
+static SoupBuffer *
+sync_async_read_chunk (SoupMessage *msg)
+{
+ SyncAsyncData *ad = g_object_get_data (G_OBJECT (msg), "SyncAsyncData");
+ guint handler;
+
+ if (sync_async_is_finished (msg))
+ return NULL;
+
+ ad->chunk = NULL;
+ handler = g_signal_connect (msg, "got_chunk",
+ G_CALLBACK (sync_async_copy_chunk),
+ ad);
+ soup_session_unpause_message (ad->session, msg);
+ g_main_loop_run (ad->loop);
+ g_signal_handler_disconnect (msg, handler);
+
+ return ad->chunk;
+}
+
+static void
+sync_async_copy_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
+{
+ SyncAsyncData *ad = user_data;
+
+ ad->chunk = soup_buffer_copy (chunk);
+
+ /* Now pause and return from the g_main_loop_run() call in
+ * sync_async_read_chunk().
+ */
+ soup_session_pause_message (ad->session, msg);
+ g_main_loop_quit (ad->loop);
+}
+
+static void
+sync_async_finished (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ SyncAsyncData *ad = user_data;
+
+ /* Unlike in the fully_async_case, we don't need to do much
+ * here, because control will return to
+ * do_synchronously_async_test() when we're done, and we do
+ * the final tests there.
+ */
+ g_main_loop_quit (ad->loop);
+ g_main_loop_unref (ad->loop);
+ ad->loop = NULL;
+}
+
+static void
+sync_async_cleanup (SoupMessage *msg)
+{
+ SyncAsyncData *ad = g_object_get_data (G_OBJECT (msg), "SyncAsyncData");
+
+ if (ad->loop)
+ g_main_loop_unref (ad->loop);
+ g_free (ad);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ SoupSession *session;
+ const char *base_uri;
+
+ test_init (argc, argv, NULL);
+ apache_init ();
+
+ base_uri = "http://127.0.0.1:47524/";
+ get_correct_response (base_uri);
+
+ debug_printf (1, "\nFully async, fast requests\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), NULL);
+ do_fully_async_test (session, base_uri, "/",
+ TRUE, SOUP_STATUS_OK);
+ do_fully_async_test (session, base_uri, "/Basic/realm1/",
+ TRUE, SOUP_STATUS_UNAUTHORIZED);
+ do_fully_async_test (session, base_uri, "/Basic/realm2/",
+ TRUE, SOUP_STATUS_OK);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, "\nFully async, slow requests\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), NULL);
+ do_fully_async_test (session, base_uri, "/",
+ FALSE, SOUP_STATUS_OK);
+ do_fully_async_test (session, base_uri, "/Basic/realm1/",
+ FALSE, SOUP_STATUS_UNAUTHORIZED);
+ do_fully_async_test (session, base_uri, "/Basic/realm2/",
+ FALSE, SOUP_STATUS_OK);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, "\nSynchronously async\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), NULL);
+ do_synchronously_async_test (session, base_uri, "/",
+ SOUP_STATUS_OK);
+ do_synchronously_async_test (session, base_uri, "/Basic/realm1/",
+ SOUP_STATUS_UNAUTHORIZED);
+ do_synchronously_async_test (session, base_uri, "/Basic/realm2/",
+ SOUP_STATUS_OK);
+ soup_test_session_abort_unref (session);
+
+ soup_buffer_free (correct_response);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libsoup/soup.h"
+
+#include "test-utils.h"
+
+SoupBuffer *full_response;
+int total_length;
+char *test_response;
+
+static void
+get_full_response (void)
+{
+ char *contents;
+ gsize length;
+ GError *error = NULL;
+
+ if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
+ fprintf (stderr, "Could not read index.txt: %s\n",
+ error->message);
+ exit (1);
+ }
+
+ full_response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
+ debug_printf (1, "Total response length is %d\n\n", (int)length);
+}
+
+static void
+check_part (SoupMessageHeaders *headers, const char *body, gsize body_len,
+ gboolean check_start_end, int expected_start, int expected_end)
+{
+ goffset start, end, total_length;
+
+ debug_printf (1, " Content-Range: %s\n",
+ soup_message_headers_get_one (headers, "Content-Range"));
+
+ if (!soup_message_headers_get_content_range (headers, &start, &end, &total_length)) {
+ debug_printf (1, " Could not find/parse Content-Range\n");
+ errors++;
+ return;
+ }
+
+ if (total_length != full_response->length && total_length != -1) {
+ debug_printf (1, " Unexpected total length %" G_GINT64_FORMAT " in response\n",
+ total_length);
+ errors++;
+ return;
+ }
+
+ if (check_start_end) {
+ if ((expected_start >= 0 && start != expected_start) ||
+ (expected_start < 0 && start != full_response->length + expected_start)) {
+ debug_printf (1, " Unexpected range start %" G_GINT64_FORMAT " in response\n",
+ start);
+ errors++;
+ return;
+ }
+
+ if ((expected_end >= 0 && end != expected_end) ||
+ (expected_end < 0 && end != full_response->length - 1)) {
+ debug_printf (1, " Unexpected range end %" G_GINT64_FORMAT " in response\n",
+ end);
+ errors++;
+ return;
+ }
+ }
+
+ if (end - start + 1 != body_len) {
+ debug_printf (1, " Range length (%d) does not match body length (%d)\n",
+ (int)(end - start) + 1,
+ (int)body_len);
+ errors++;
+ return;
+ }
+
+ memcpy (test_response + start, body, body_len);
+}
+
+static void
+do_single_range (SoupSession *session, SoupMessage *msg,
+ int start, int end)
+{
+ const char *content_type;
+
+ debug_printf (1, " Range: %s\n",
+ soup_message_headers_get_one (msg->request_headers, "Range"));
+
+ soup_session_send_message (session, msg);
+
+ if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
+ debug_printf (1, " Unexpected status %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ g_object_unref (msg);
+ errors++;
+ return;
+ }
+
+ content_type = soup_message_headers_get_content_type (
+ msg->response_headers, NULL);
+ if (content_type && !strcmp (content_type, "multipart/byteranges")) {
+ debug_printf (1, " Response body should not have been multipart/byteranges\n");
+ g_object_unref (msg);
+ errors++;
+ return;
+ }
+
+ check_part (msg->response_headers, msg->response_body->data,
+ msg->response_body->length, TRUE, start, end);
+ g_object_unref (msg);
+}
+
+static void
+request_single_range (SoupSession *session, const char *uri,
+ int start, int end)
+{
+ SoupMessage *msg;
+
+ msg = soup_message_new ("GET", uri);
+ soup_message_headers_set_range (msg->request_headers, start, end);
+ do_single_range (session, msg, start, end);
+}
+
+static void
+do_multi_range (SoupSession *session, SoupMessage *msg,
+ int expected_return_ranges)
+{
+ SoupMultipart *multipart;
+ const char *content_type;
+ int i, length;
+
+ debug_printf (1, " Range: %s\n",
+ soup_message_headers_get_one (msg->request_headers, "Range"));
+
+ soup_session_send_message (session, msg);
+
+ if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
+ debug_printf (1, " Unexpected status %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ g_object_unref (msg);
+ errors++;
+ return;
+ }
+
+ content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
+ if (!content_type || strcmp (content_type, "multipart/byteranges") != 0) {
+ debug_printf (1, " Response Content-Type (%s) was not multipart/byteranges\n",
+ content_type);
+ g_object_unref (msg);
+ errors++;
+ return;
+ }
+
+ multipart = soup_multipart_new_from_message (msg->response_headers,
+ msg->response_body);
+ if (!multipart) {
+ debug_printf (1, " Could not parse multipart\n");
+ g_object_unref (msg);
+ errors++;
+ return;
+ }
+
+ length = soup_multipart_get_length (multipart);
+ if (length != expected_return_ranges) {
+ debug_printf (1, " Expected %d ranges, got %d\n",
+ expected_return_ranges, length);
+ errors++;
+ }
+
+ for (i = 0; i < length; i++) {
+ SoupMessageHeaders *headers;
+ SoupBuffer *body;
+
+ debug_printf (1, " Part %d\n", i + 1);
+ soup_multipart_get_part (multipart, i, &headers, &body);
+ check_part (headers, body->data, body->length, FALSE, 0, 0);
+ }
+
+ soup_multipart_free (multipart);
+ g_object_unref (msg);
+}
+
+static void
+request_double_range (SoupSession *session, const char *uri,
+ int first_start, int first_end,
+ int second_start, int second_end,
+ int expected_return_ranges)
+{
+ SoupMessage *msg;
+ SoupRange ranges[2];
+
+ msg = soup_message_new ("GET", uri);
+ ranges[0].start = first_start;
+ ranges[0].end = first_end;
+ ranges[1].start = second_start;
+ ranges[1].end = second_end;
+ soup_message_headers_set_ranges (msg->request_headers, ranges, 2);
+
+ if (expected_return_ranges == 1) {
+ do_single_range (session, msg,
+ MIN (first_start, second_start),
+ MAX (first_end, second_end));
+ } else
+ do_multi_range (session, msg, expected_return_ranges);
+}
+
+static void
+request_triple_range (SoupSession *session, const char *uri,
+ int first_start, int first_end,
+ int second_start, int second_end,
+ int third_start, int third_end,
+ int expected_return_ranges)
+{
+ SoupMessage *msg;
+ SoupRange ranges[3];
+
+ msg = soup_message_new ("GET", uri);
+ ranges[0].start = first_start;
+ ranges[0].end = first_end;
+ ranges[1].start = second_start;
+ ranges[1].end = second_end;
+ ranges[2].start = third_start;
+ ranges[2].end = third_end;
+ soup_message_headers_set_ranges (msg->request_headers, ranges, 3);
+
+ if (expected_return_ranges == 1) {
+ do_single_range (session, msg,
+ MIN (first_start, MIN (second_start, third_start)),
+ MAX (first_end, MAX (second_end, third_end)));
+ } else
+ do_multi_range (session, msg, expected_return_ranges);
+}
+
+static void
+do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce)
+{
+ int twelfths = full_response->length / 12;
+
+ memset (test_response, 0, full_response->length);
+
+ /* We divide the response into 12 ranges and request them
+ * as follows:
+ *
+ * 0: A (first single request)
+ * 1: D (2nd part of triple request)
+ * 2: C (1st part of double request)
+ * 3: D (1st part of triple request)
+ * 4: F (trickier overlapping request)
+ * 5: C (2nd part of double request)
+ * 6: D (3rd part of triple request)
+ * 7: E (overlapping request)
+ * 8: E (overlapping request)
+ * 9: F (trickier overlapping request)
+ * 10: F (trickier overlapping request)
+ * 11: B (second and third single requests)
+ */
+
+ /* A: 0, simple request */
+ debug_printf (1, "Requesting %d-%d\n", 0 * twelfths, 1 * twelfths);
+ request_single_range (session, uri,
+ 0 * twelfths, 1 * twelfths);
+
+ /* B: 11, end-relative request. These two are mostly redundant
+ * in terms of data coverage, but they may still catch
+ * Range-header-generating bugs.
+ */
+ debug_printf (1, "Requesting %d-\n", 11 * twelfths);
+ request_single_range (session, uri,
+ 11 * twelfths, -1);
+ debug_printf (1, "Requesting -%d\n", 1 * twelfths);
+ request_single_range (session, uri,
+ -1 * twelfths, -1);
+
+ /* C: 2 and 5 */
+ debug_printf (1, "Requesting %d-%d,%d-%d\n",
+ 2 * twelfths, 3 * twelfths,
+ 5 * twelfths, 6 * twelfths);
+ request_double_range (session, uri,
+ 2 * twelfths, 3 * twelfths,
+ 5 * twelfths, 6 * twelfths,
+ 2);
+
+ /* D: 1, 3, 6 */
+ debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d\n",
+ 3 * twelfths, 4 * twelfths,
+ 1 * twelfths, 2 * twelfths,
+ 6 * twelfths, 7 * twelfths);
+ request_triple_range (session, uri,
+ 3 * twelfths, 4 * twelfths,
+ 1 * twelfths, 2 * twelfths,
+ 6 * twelfths, 7 * twelfths,
+ 3);
+
+ /* E: 7 and 8: should coalesce into a single response */
+ debug_printf (1, "Requesting %d-%d,%d-%d (can coalesce)\n",
+ 7 * twelfths, 8 * twelfths,
+ 8 * twelfths, 9 * twelfths);
+ request_double_range (session, uri,
+ 7 * twelfths, 8 * twelfths,
+ 8 * twelfths, 9 * twelfths,
+ expect_coalesce ? 1 : 2);
+
+ /* F: 4, 9, 10: 9 and 10 should coalesce even though 4 was
+ * requested between them. (Also, they actually overlap in
+ * this case, as opposed to just touching.)
+ */
+ debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d (can partially coalesce)\n",
+ 9 * twelfths, 10 * twelfths + 5,
+ 4 * twelfths, 5 * twelfths,
+ 10 * twelfths - 5, 11 * twelfths);
+ request_triple_range (session, uri,
+ 9 * twelfths, 10 * twelfths + 5,
+ 4 * twelfths, 5 * twelfths,
+ 10 * twelfths - 5, 11 * twelfths,
+ expect_coalesce ? 2 : 3);
+
+ if (memcmp (full_response->data, test_response, full_response->length) != 0) {
+ debug_printf (1, "\nfull_response and test_response don't match\n");
+ errors++;
+ }
+}
+
+static void
+server_handler (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
+{
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_body_append_buffer (msg->response_body,
+ full_response);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupSession *session;
+ SoupServer *server;
+ char *base_uri;
+
+ test_init (argc, argv, NULL);
+ apache_init ();
+
+ get_full_response ();
+ test_response = g_malloc0 (full_response->length);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ debug_printf (1, "1. Testing against apache\n");
+ do_range_test (session, "http://127.0.0.1:47524/", FALSE);
+
+ debug_printf (1, "\n2. Testing against SoupServer\n");
+ server = soup_test_server_new (FALSE);
+ soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
+ base_uri = g_strdup_printf ("http://127.0.0.1:%u/",
+ soup_server_get_port (server));
+ do_range_test (session, base_uri, TRUE);
+ g_free (base_uri);
+ soup_test_server_quit_unref (server);
+
+ soup_test_session_abort_unref (session);
+
+ soup_buffer_free (full_response);
+ g_free (test_response);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+char *server2_uri;
+
+typedef struct {
+ const char *method;
+ const char *path;
+ guint status_code;
+ gboolean repeat;
+} TestRequest;
+
+static struct {
+ TestRequest requests[3];
+ guint final_status;
+} tests[] = {
+ /* A redirecty response to a GET or HEAD should cause a redirect */
+
+ { { { "GET", "/301", 301 },
+ { "GET", "/", 200 },
+ { NULL } }, 200 },
+ { { { "GET", "/302", 302 },
+ { "GET", "/", 200 },
+ { NULL } }, 200 },
+ { { { "GET", "/303", 303 },
+ { "GET", "/", 200 },
+ { NULL } }, 200 },
+ { { { "GET", "/307", 307 },
+ { "GET", "/", 200 },
+ { NULL } }, 200 },
+ { { { "HEAD", "/301", 301 },
+ { "HEAD", "/", 200 },
+ { NULL } }, 200 },
+ { { { "HEAD", "/302", 302 },
+ { "HEAD", "/", 200 },
+ { NULL } }, 200 },
+ /* 303 is a nonsensical response to HEAD, but some sites do
+ * it anyway. :-/
+ */
+ { { { "HEAD", "/303", 303 },
+ { "HEAD", "/", 200 },
+ { NULL } }, 200 },
+ { { { "HEAD", "/307", 307 },
+ { "HEAD", "/", 200 },
+ { NULL } }, 200 },
+
+ /* A non-redirecty response to a GET or HEAD should not */
+
+ { { { "GET", "/300", 300 },
+ { NULL } }, 300 },
+ { { { "GET", "/304", 304 },
+ { NULL } }, 304 },
+ { { { "GET", "/305", 305 },
+ { NULL } }, 305 },
+ { { { "GET", "/306", 306 },
+ { NULL } }, 306 },
+ { { { "GET", "/308", 308 },
+ { NULL } }, 308 },
+ { { { "HEAD", "/300", 300 },
+ { NULL } }, 300 },
+ { { { "HEAD", "/304", 304 },
+ { NULL } }, 304 },
+ { { { "HEAD", "/305", 305 },
+ { NULL } }, 305 },
+ { { { "HEAD", "/306", 306 },
+ { NULL } }, 306 },
+ { { { "HEAD", "/308", 308 },
+ { NULL } }, 308 },
+
+ /* Test double-redirect */
+
+ { { { "GET", "/301/302", 301 },
+ { "GET", "/302", 302 },
+ { "GET", "/", 200 } }, 200 },
+ { { { "HEAD", "/301/302", 301 },
+ { "HEAD", "/302", 302 },
+ { "HEAD", "/", 200 } }, 200 },
+
+ /* POST should only automatically redirect on 301, 302 and 303 */
+
+ { { { "POST", "/301", 301 },
+ { "GET", "/", 200 },
+ { NULL } }, 200 },
+ { { { "POST", "/302", 302 },
+ { "GET", "/", 200 },
+ { NULL } }, 200 },
+ { { { "POST", "/303", 303 },
+ { "GET", "/", 200 },
+ { NULL } }, 200 },
+ { { { "POST", "/307", 307 },
+ { NULL } }, 307 },
+
+ /* Test behavior with recoverably-bad Location header */
+ { { { "GET", "/bad", 302 },
+ { "GET", "/bad%20with%20spaces", 200 },
+ { NULL } }, 200 },
+
+ /* Test behavior with irrecoverably-bad Location header */
+ { { { "GET", "/bad-no-host", 302 },
+ { NULL } }, SOUP_STATUS_MALFORMED },
+
+ /* Test infinite redirection */
+ { { { "GET", "/bad-recursive", 302, TRUE },
+ { NULL } }, SOUP_STATUS_TOO_MANY_REDIRECTS },
+
+ /* Test redirection to a different server */
+ { { { "GET", "/server2", 302 },
+ { "GET", "/on-server2", 200 },
+ { NULL } }, 200 },
+};
+static const int n_tests = G_N_ELEMENTS (tests);
+
+static void
+got_headers (SoupMessage *msg, gpointer user_data)
+{
+ TestRequest **req = user_data;
+ const char *location;
+
+ debug_printf (2, " -> %d %s\n", msg->status_code,
+ msg->reason_phrase);
+ location = soup_message_headers_get_one (msg->response_headers,
+ "Location");
+ if (location)
+ debug_printf (2, " Location: %s\n", location);
+
+ if (!(*req)->method)
+ return;
+
+ if (msg->status_code != (*req)->status_code) {
+ debug_printf (1, " - Expected %d !\n",
+ (*req)->status_code);
+ errors++;
+ }
+}
+
+static void
+restarted (SoupMessage *msg, gpointer user_data)
+{
+ TestRequest **req = user_data;
+ SoupURI *uri = soup_message_get_uri (msg);
+
+ debug_printf (2, " %s %s\n", msg->method, uri->path);
+
+ if ((*req)->method && !(*req)->repeat)
+ (*req)++;
+
+ if (!(*req)->method) {
+ debug_printf (1, " - Expected to be done!\n");
+ errors++;
+ return;
+ }
+
+ if (strcmp (msg->method, (*req)->method) != 0) {
+ debug_printf (1, " - Expected %s !\n", (*req)->method);
+ errors++;
+ }
+ if (strcmp (uri->path, (*req)->path) != 0) {
+ debug_printf (1, " - Expected %s !\n", (*req)->path);
+ errors++;
+ }
+}
+
+static void
+do_test (SoupSession *session, SoupURI *base_uri, int n)
+{
+ SoupURI *uri;
+ SoupMessage *msg;
+ TestRequest *req;
+
+ debug_printf (1, "%2d. %s %s\n", n + 1,
+ tests[n].requests[0].method,
+ tests[n].requests[0].path);
+
+ uri = soup_uri_new_with_base (base_uri, tests[n].requests[0].path);
+ msg = soup_message_new_from_uri (tests[n].requests[0].method, uri);
+ soup_uri_free (uri);
+
+ if (msg->method == SOUP_METHOD_POST) {
+ soup_message_set_request (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "post body",
+ strlen ("post body"));
+ }
+
+ req = &tests[n].requests[0];
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (got_headers), &req);
+ g_signal_connect (msg, "restarted",
+ G_CALLBACK (restarted), &req);
+
+ soup_session_send_message (session, msg);
+
+ if (msg->status_code != tests[n].final_status) {
+ debug_printf (1, " - Expected final status of %d, got %d !\n",
+ tests[n].final_status, msg->status_code);
+ errors++;
+ }
+
+ g_object_unref (msg);
+ debug_printf (2, "\n");
+}
+
+static void
+do_redirect_tests (SoupURI *base_uri)
+{
+ SoupSession *session;
+ int n;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ debug_printf (1, "Async session\n");
+ for (n = 0; n < n_tests; n++)
+ do_test (session, base_uri, n);
+ soup_test_session_abort_unref (session);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ debug_printf (1, "Sync session\n");
+ for (n = 0; n < n_tests; n++)
+ do_test (session, base_uri, n);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ char *remainder;
+ guint status_code;
+
+ if (g_str_has_prefix (path, "/bad")) {
+ if (!strcmp (path, "/bad")) {
+ soup_message_set_status (msg, SOUP_STATUS_FOUND);
+ soup_message_headers_replace (msg->response_headers,
+ "Location",
+ "/bad with spaces");
+ } else if (!strcmp (path, "/bad-recursive")) {
+ soup_message_set_status (msg, SOUP_STATUS_FOUND);
+ soup_message_headers_replace (msg->response_headers,
+ "Location",
+ "/bad-recursive");
+ } else if (!strcmp (path, "/bad-no-host")) {
+ soup_message_set_status (msg, SOUP_STATUS_FOUND);
+ soup_message_headers_replace (msg->response_headers,
+ "Location",
+ "about:blank");
+ } else if (!strcmp (path, "/bad with spaces"))
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ else
+ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+ return;
+ } else if (!strcmp (path, "/server2")) {
+ soup_message_set_status (msg, SOUP_STATUS_FOUND);
+ soup_message_headers_replace (msg->response_headers,
+ "Location",
+ server2_uri);
+ return;
+ } else if (!strcmp (path, "/")) {
+ if (msg->method != SOUP_METHOD_GET &&
+ msg->method != SOUP_METHOD_HEAD) {
+ soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+ return;
+ }
+
+ /* Make sure that redirecting a POST clears the body */
+ if (msg->request_body->length) {
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+ return;
+ }
+
+ /* Make sure that a HTTP/1.0 redirect doesn't cause an
+ * HTTP/1.0 re-request. (#521848)
+ */
+ if (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) {
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+
+ /* FIXME: this is wrong, though it doesn't matter for
+ * the purposes of this test, and to do the right
+ * thing currently we'd have to set Content-Length by
+ * hand.
+ */
+ if (msg->method != SOUP_METHOD_HEAD) {
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "OK\r\n", 4);
+ }
+ return;
+ }
+
+ status_code = strtoul (path + 1, &remainder, 10);
+ if (!SOUP_STATUS_IS_REDIRECTION (status_code) ||
+ (*remainder && *remainder != '/')) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+ return;
+ }
+
+ /* See above comment re bug 521848. */
+ soup_message_set_http_version (msg, SOUP_HTTP_1_0);
+
+ soup_message_set_status (msg, status_code);
+ if (*remainder) {
+ soup_message_headers_replace (msg->response_headers,
+ "Location", remainder);
+ } else {
+ soup_message_headers_replace (msg->response_headers,
+ "Location", "/");
+ }
+}
+
+static void
+server2_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static gboolean run_tests = TRUE;
+
+static GOptionEntry no_test_entry[] = {
+ { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &run_tests,
+ "Don't run tests, just run the test server", NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ SoupServer *server, *server2;
+ guint port;
+ SoupURI *base_uri;
+
+ test_init (argc, argv, no_test_entry);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+ port = soup_server_get_port (server);
+
+ server2 = soup_test_server_new (TRUE);
+ soup_server_add_handler (server2, NULL,
+ server2_callback, NULL, NULL);
+ server2_uri = g_strdup_printf ("http://127.0.0.1:%d/on-server2",
+ soup_server_get_port (server2));
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ if (run_tests) {
+ base_uri = soup_uri_new ("http://127.0.0.1");
+ soup_uri_set_port (base_uri, port);
+ do_redirect_tests (base_uri);
+ soup_uri_free (base_uri);
+ } else {
+ printf ("Listening on port %d\n", port);
+ g_main_loop_run (loop);
+ }
+
+ g_main_loop_unref (loop);
+ g_free (server2_uri);
+ soup_test_server_quit_unref (server);
+ soup_test_server_quit_unref (server2);
+
+ if (run_tests)
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+#include <libsoup/soup.h>
+#include <libsoup/soup-requester.h>
+#include <libsoup/soup-request-http.h>
+
+#include "test-utils.h"
+
+SoupServer *server;
+GMainLoop *loop;
+char buf[1024];
+
+SoupBuffer *response;
+
+static void
+get_index (void)
+{
+ char *contents;
+ gsize length;
+ GError *error = NULL;
+
+ if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
+ fprintf (stderr, "Could not read index.txt: %s\n",
+ error->message);
+ exit (1);
+ }
+
+ response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC, NULL, 0);
+ soup_message_body_append_buffer (msg->response_body, response);
+}
+
+static void
+test_read_ready (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GInputStream *stream = G_INPUT_STREAM (source);
+ GString *body = user_data;
+ GError *error = NULL;
+ gsize nread;
+
+ nread = g_input_stream_read_finish (stream, res, &error);
+ if (nread == -1) {
+ debug_printf (1, " read_async failed: %s", error->message);
+ errors++;
+ g_object_unref (stream);
+ g_main_loop_quit (loop);
+ return;
+ } else if (nread == 0) {
+ g_object_unref (stream);
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ g_string_append_len (body, buf, nread);
+ g_input_stream_read_async (stream, buf, sizeof (buf),
+ G_PRIORITY_DEFAULT, NULL,
+ test_read_ready, body);
+}
+
+static void
+test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GString *body = user_data;
+ GInputStream *stream;
+ GError *error = NULL;
+ SoupMessage *msg;
+
+ stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
+ if (!stream) {
+ debug_printf (1, " send_async failed: %s", error->message);
+ errors++;
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (source));
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " GET failed: %d %s", msg->status_code,
+ msg->reason_phrase);
+ errors++;
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ g_input_stream_read_async (stream, buf, sizeof (buf),
+ G_PRIORITY_DEFAULT, NULL,
+ test_read_ready, body);
+}
+
+static void
+do_test_for_thread_and_context (SoupSession *session, const char *uri)
+{
+ SoupRequester *requester;
+ SoupRequest *request;
+ GString *body;
+
+ requester = soup_requester_new ();
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
+ g_object_unref (requester);
+
+ body = g_string_new (NULL);
+
+ request = soup_requester_request (requester, uri, NULL);
+ soup_request_send_async (request, NULL, test_sent, body);
+ g_object_unref (request);
+
+ loop = g_main_loop_new (soup_session_get_async_context (session), TRUE);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ if (body->len != response->length) {
+ debug_printf (1, " body length mismatch: expected %d, got %d\n",
+ (int)response->length, (int)body->len);
+ errors++;
+ } else if (memcmp (body->str, response->data, response->length) != 0) {
+ debug_printf (1, " body data mismatch\n");
+ errors++;
+ }
+
+ g_string_free (body, TRUE);
+}
+
+static void
+do_simple_test (const char *uri)
+{
+ SoupSession *session;
+
+ debug_printf (1, "Simple streaming test\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_test_for_thread_and_context (session, uri);
+ soup_test_session_abort_unref (session);
+}
+
+static gpointer
+do_test_with_context (const char *uri)
+{
+ GMainContext *async_context;
+ SoupSession *session;
+
+ async_context = g_main_context_new ();
+ g_main_context_push_thread_default (async_context);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_ASYNC_CONTEXT, async_context,
+ NULL);
+ g_main_context_unref (async_context);
+
+ do_test_for_thread_and_context (session, uri);
+ soup_test_session_abort_unref (session);
+
+ return NULL;
+}
+
+static void
+do_context_test (const char *uri)
+{
+ debug_printf (1, "Streaming with a non-default-context\n");
+ do_test_with_context (uri);
+}
+
+static void
+do_thread_test (const char *uri)
+{
+ GThread *thread;
+
+ debug_printf (1, "Streaming in another thread\n");
+
+ thread = g_thread_create ((GThreadFunc)do_test_with_context,
+ (gpointer)uri, TRUE, NULL);
+ g_thread_join (thread);
+}
+
+int
+main (int argc, char **argv)
+{
+ char *uri;
+
+ test_init (argc, argv, NULL);
+ get_index ();
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ uri = g_strdup_printf ("http://127.0.0.1:%u/", soup_server_get_port (server));
+
+ do_simple_test (uri);
+ do_thread_test (uri);
+ do_context_test (uri);
+
+ g_free (uri);
+ soup_buffer_free (response);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+<?xml version="1.0"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.libsouprocks.net/" xmlns:indexing="urn:atom-extension:indexing" indexing:index="no"><access:restriction xmlns:access="http://www.bloglines.com/about/specs/fac-1.0" relationship="deny"/>
+ <title>A small ATOM feed</title>
+ <updated>2009-07-02T10:27:44Z</updated>
+ <generator>kov</generator>
+ <author>
+ <name>Anonymous Coward</name>
+ </author>
+ <id>http://libsoup.rocks/atom.xml</id>
+ <link href="http://libsoup.rocks/atom.xml" rel="self" type="application/atom+xml"/>
+ <link href="http://libsoup.rocks/" rel="alternate"/>
+
+ <entry xml:lang="en">
+ <id>http://libsoup.rocks/so/much/</id>
+ <link href="http://libsoup.rocks/so/much/" rel="alternate" type="text/html"/>
+ <title>One post too many</title>
+ <summary>woo [...]</summary>
+ <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>woohoo</p></div>
+ </content>
+ <updated>2009-07-02T10:38:28Z</updated>
+ <category term="Category1"/>
+ <category term="Personal"/>
+ <author>
+ <name>kov</name>
+ </author>
+ <source>
+ <id>http://libsoup.rocks/blog</id>
+ <link href="http://libsoup.rocks/blog/feed" rel="self" type="application/atom+xml"/>
+ <link href="http://libsoup.rocks/blog" rel="alternate" type="text/html"/>
+ <subtitle>Just stuff to test libsoup</subtitle>
+ <title>Random stuff to test libsoup</title>
+ <updated>2009-07-02T00:38:29Z</updated>
+ </source>
+ </entry>
+</feed>
--- /dev/null
+From email@here Wed Jun 17 21:20:48 2009
+Return-path: <email@here>
+Envelope-to: email@here
+Delivery-date: Wed, 17 Jun 2009 21:20:48 -0300
+Received: from email by here.domain with local (Exim 4.69)
+ (envelope-from <email@here>)
+ id 1MH5N2-0008Lq-7c
+ for email@here; Wed, 17 Jun 2009 21:20:48 -0300
+To: email@here
+Subject: This is just so that I have a mailbox
+Message-Id: <E1MH5N2-0008Lq-7c@here.domain>
+From: A Nice User <email@here>
+Date: Wed, 17 Jun 2009 21:20:48 -0300
+
+This is a dumb email.
+
+From email@here Wed Jun 17 21:20:48 2009
+Return-path: <email@here>
+Envelope-to: email@here
+Delivery-date: Wed, 17 Jun 2009 21:20:48 -0300
+Received: from email by here.domain with local (Exim 4.69)
+ (envelope-from <email@here>)
+ id 1MH5N2-0008Lq-7c
+ for email@here; Wed, 17 Jun 2009 21:20:48 -0300
+To: email@here
+Subject: This is just so that I have a mailbox
+Message-Id: <E1MH5N2-0008Lq-7c@here.domain>
+From: A Nice User <email@here>
+Date: Wed, 17 Jun 2009 21:20:48 -0300
+
+This is a dumb email.
+
+From email@here Wed Jun 17 21:20:48 2009
+Return-path: <email@here>
+Envelope-to: email@here
+Delivery-date: Wed, 17 Jun 2009 21:20:48 -0300
+Received: from email by here.domain with local (Exim 4.69)
+ (envelope-from <email@here>)
+ id 1MH5N2-0008Lq-7c
+ for email@here; Wed, 17 Jun 2009 21:20:48 -0300
+To: email@here
+Subject: This is just so that I have a mailbox
+Message-Id: <E1MH5N2-0008Lq-7c@here.domain>
+From: A Nice User <email@here>
+Date: Wed, 17 Jun 2009 21:20:48 -0300
+
+This is a dumb email.
+
+From email@here Wed Jun 17 21:20:48 2009
+Return-path: <email@here>
+Envelope-to: email@here
+Delivery-date: Wed, 17 Jun 2009 21:20:48 -0300
+Received: from email by here.domain with local (Exim 4.69)
+ (envelope-from <email@here>)
+ id 1MH5N2-0008Lq-7c
+ for email@here; Wed, 17 Jun 2009 21:20:48 -0300
+To: email@here
+Subject: This is just so that I have a mailbox
+Message-Id: <E1MH5N2-0008Lq-7c@here.domain>
+From: A Nice User <email@here>
+Date: Wed, 17 Jun 2009 21:20:48 -0300
+
+This is a dumb email.
+
--- /dev/null
+%!PS-Adobe-" \16
--- /dev/null
+<?xml version="1.0"?>
+<rss version="2.0">
+
+<channel>
+ <title>A small RSS</title>
+ <link>http://libsoup.rocks/</link>
+ <language>en</language>
+ <description>A small RSS to test libsoup</description>
+
+<item>
+ <title>One post too many</title>
+ <guid isPermaLink="true">http://libsoup.rocks/so/much/</guid>
+ <link>http://libsoup.rocks/so/much/</link>
+ <description><p>woohoo</p></description>
+ <pubDate>Wed, 02 Jul 2009 10:26:28 +0000</pubDate>
+</item>
+<item>
+ <title>GCDS will rock</title>
+ <guid isPermaLink="true">http://libsoup.rocks/so/much/again/</guid>
+ <link>http://libsoup.rocks/so/much/again/</link>
+ <description><p>I mean, really.</p></description>
+ <pubDate>Wed, 02 Jul 2009 10:26:28 +0000</pubDate>
+</item>
+
+</channel>
+</rss>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title></title>
+</head>
+<body>
+<h1>GNOME!</h1>
+</body>
+</html>
--- /dev/null
+abc\1c
\ No newline at end of file
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup-address.h>
+#include <libsoup/soup-auth-domain-basic.h>
+#include <libsoup/soup-auth-domain-digest.h>
+#include <libsoup/soup-message.h>
+#include <libsoup/soup-server.h>
+
+#include "test-utils.h"
+
+static struct {
+ gboolean client_sent_basic, client_sent_digest;
+ gboolean server_requested_basic, server_requested_digest;
+ gboolean succeeded;
+} test_data;
+
+static void
+curl_exited (GPid pid, int status, gpointer data)
+{
+ gboolean *done = data;
+
+ *done = TRUE;
+ test_data.succeeded = (status == 0);
+}
+
+static void
+do_test (int n, SoupURI *base_uri, const char *path,
+ gboolean good_user, gboolean good_password,
+ gboolean offer_basic, gboolean offer_digest,
+ gboolean client_sends_basic, gboolean client_sends_digest,
+ gboolean server_requests_basic, gboolean server_requests_digest,
+ gboolean success)
+{
+ SoupURI *uri;
+ char *uri_str;
+ GPtrArray *args;
+ GPid pid;
+ gboolean done;
+
+ debug_printf (1, "%2d. %s, %soffer Basic, %soffer Digest, %s user, %s password\n",
+ n, path, offer_basic ? "" : "don't ",
+ offer_digest ? "" : "don't ",
+ good_user ? "good" : "bad",
+ good_password ? "good" : "bad");
+
+ uri = soup_uri_new_with_base (base_uri, path);
+ uri_str = soup_uri_to_string (uri, FALSE);
+ soup_uri_free (uri);
+
+ args = g_ptr_array_new ();
+ g_ptr_array_add (args, "curl");
+ g_ptr_array_add (args, "-f");
+ g_ptr_array_add (args, "-s");
+ if (offer_basic || offer_digest) {
+ g_ptr_array_add (args, "-u");
+ if (good_user) {
+ if (good_password)
+ g_ptr_array_add (args, "user:password");
+ else
+ g_ptr_array_add (args, "user:badpassword");
+ } else {
+ if (good_password)
+ g_ptr_array_add (args, "baduser:password");
+ else
+ g_ptr_array_add (args, "baduser:badpassword");
+ }
+
+ if (offer_basic && offer_digest)
+ g_ptr_array_add (args, "--anyauth");
+ else if (offer_basic)
+ g_ptr_array_add (args, "--basic");
+ else
+ g_ptr_array_add (args, "--digest");
+ }
+ g_ptr_array_add (args, uri_str);
+ g_ptr_array_add (args, NULL);
+
+ memset (&test_data, 0, sizeof (test_data));
+ if (g_spawn_async (NULL, (char **)args->pdata, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, &pid, NULL)) {
+ done = FALSE;
+ g_child_watch_add (pid, curl_exited, &done);
+
+ while (!done)
+ g_main_context_iteration (NULL, TRUE);
+ } else
+ test_data.succeeded = FALSE;
+ g_ptr_array_free (args, TRUE);
+ g_free (uri_str);
+
+ if (server_requests_basic != test_data.server_requested_basic) {
+ errors++;
+ if (test_data.server_requested_basic)
+ debug_printf (1, " Server sent WWW-Authenticate: Basic, but shouldn't have!\n");
+ else
+ debug_printf (1, " Server didn't send WWW-Authenticate: Basic, but should have!\n");
+ }
+ if (server_requests_digest != test_data.server_requested_digest) {
+ errors++;
+ if (test_data.server_requested_digest)
+ debug_printf (1, " Server sent WWW-Authenticate: Digest, but shouldn't have!\n");
+ else
+ debug_printf (1, " Server didn't send WWW-Authenticate: Digest, but should have!\n");
+ }
+ if (client_sends_basic != test_data.client_sent_basic) {
+ errors++;
+ if (test_data.client_sent_basic)
+ debug_printf (1, " Client sent Authorization: Basic, but shouldn't have!\n");
+ else
+ debug_printf (1, " Client didn't send Authorization: Basic, but should have!\n");
+ }
+ if (client_sends_digest != test_data.client_sent_digest) {
+ errors++;
+ if (test_data.client_sent_digest)
+ debug_printf (1, " Client sent Authorization: Digest, but shouldn't have!\n");
+ else
+ debug_printf (1, " Client didn't send Authorization: Digest, but should have!\n");
+ }
+ if (success && !test_data.succeeded) {
+ errors++;
+ debug_printf (1, " Should have succeeded, but didn't!\n");
+ } else if (!success && test_data.succeeded) {
+ errors++;
+ debug_printf (1, " Should not have succeeded, but did!\n");
+ }
+}
+
+static void
+do_auth_tests (SoupURI *base_uri)
+{
+ int i, n = 1;
+ gboolean use_basic, use_digest, good_user, good_password;
+ gboolean preemptive_basic, good_auth;
+
+ for (i = 0; i < 16; i++) {
+ use_basic = (i & 1) == 1;
+ use_digest = (i & 2) == 2;
+ good_user = (i & 4) == 4;
+ good_password = (i & 8) == 8;
+
+ good_auth = good_user && good_password;
+
+ /* Curl will preemptively send Basic if it's told to
+ * use Basic but not Digest.
+ */
+ preemptive_basic = use_basic && !use_digest;
+
+ /* 1. No auth required. The server will ignore the
+ * Authorization headers completely, and the request
+ * will always succeed.
+ */
+ do_test (n++, base_uri, "/foo",
+ good_user, good_password,
+ /* request */
+ use_basic, use_digest,
+ /* expected from client */
+ preemptive_basic, FALSE,
+ /* expected from server */
+ FALSE, FALSE,
+ /* success? */
+ TRUE);
+
+ /* 2. Basic auth required. The server will send
+ * "WWW-Authenticate: Basic" if the client fails to
+ * send an Authorization: Basic on the first request,
+ * or if it sends a bad password.
+ */
+ do_test (n++, base_uri, "/Basic/foo",
+ good_user, good_password,
+ /* request */
+ use_basic, use_digest,
+ /* expected from client */
+ use_basic, FALSE,
+ /* expected from server */
+ !preemptive_basic || !good_auth, FALSE,
+ /* success? */
+ use_basic && good_auth);
+
+ /* 3. Digest auth required. Simpler than the basic
+ * case because the client can't send Digest auth
+ * premptively.
+ */
+ do_test (n++, base_uri, "/Digest/foo",
+ good_user, good_password,
+ /* request */
+ use_basic, use_digest,
+ /* expected from client */
+ preemptive_basic, use_digest,
+ /* expected from server */
+ FALSE, TRUE,
+ /* success? */
+ use_digest && good_auth);
+
+ /* 4. Any auth required. */
+ do_test (n++, base_uri, "/Any/foo",
+ good_user, good_password,
+ /* request */
+ use_basic, use_digest,
+ /* expected from client */
+ preemptive_basic, use_digest,
+ /* expected from server */
+ !preemptive_basic || !good_auth, !preemptive_basic || !good_auth,
+ /* success? */
+ (use_basic || use_digest) && good_auth);
+
+ /* 5. No auth required again. (Makes sure that
+ * SOUP_AUTH_DOMAIN_REMOVE_PATH works.)
+ */
+ do_test (n++, base_uri, "/Any/Not/foo",
+ good_user, good_password,
+ /* request */
+ use_basic, use_digest,
+ /* expected from client */
+ preemptive_basic, FALSE,
+ /* expected from server */
+ FALSE, FALSE,
+ /* success? */
+ TRUE);
+ }
+}
+
+static gboolean
+basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+ const char *username, const char *password, gpointer data)
+{
+ return !strcmp (username, "user") && !strcmp (password, "password");
+}
+
+static char *
+digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+ const char *username, gpointer data)
+{
+ if (strcmp (username, "user") != 0)
+ return NULL;
+
+ /* Note: this is exactly how you *shouldn't* do it in the real
+ * world; you should have the pre-encoded password stored in a
+ * database of some sort rather than using the cleartext
+ * password in the callback.
+ */
+ return soup_auth_domain_digest_encode_password ("user",
+ "server-auth-test",
+ "password");
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "OK\r\n", 4);
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static void
+got_headers_callback (SoupMessage *msg, gpointer data)
+{
+ const char *header;
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Authorization");
+ if (header) {
+ if (strstr (header, "Basic "))
+ test_data.client_sent_basic = TRUE;
+ if (strstr (header, "Digest "))
+ test_data.client_sent_digest = TRUE;
+ }
+}
+
+static void
+wrote_headers_callback (SoupMessage *msg, gpointer data)
+{
+ const char *header;
+
+ header = soup_message_headers_get_list (msg->response_headers,
+ "WWW-Authenticate");
+ if (header) {
+ if (strstr (header, "Basic "))
+ test_data.server_requested_basic = TRUE;
+ if (strstr (header, "Digest "))
+ test_data.server_requested_digest = TRUE;
+ }
+}
+
+static void
+request_started_callback (SoupServer *server, SoupMessage *msg,
+ SoupClientContext *client, gpointer data)
+{
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (got_headers_callback), NULL);
+ g_signal_connect (msg, "wrote_headers",
+ G_CALLBACK (wrote_headers_callback), NULL);
+}
+
+static gboolean run_tests = TRUE;
+
+static GOptionEntry no_test_entry[] = {
+ { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &run_tests,
+ "Don't run tests, just run the test server", NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ SoupServer *server;
+ SoupURI *uri;
+ SoupAuthDomain *auth_domain;
+
+ test_init (argc, argv, no_test_entry);
+
+ server = soup_test_server_new (FALSE);
+ g_signal_connect (server, "request_started",
+ G_CALLBACK (request_started_callback), NULL);
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+
+ auth_domain = soup_auth_domain_basic_new (
+ SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/Basic",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
+ SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_callback,
+ NULL);
+ soup_server_add_auth_domain (server, auth_domain);
+ g_object_unref (auth_domain);
+
+ auth_domain = soup_auth_domain_digest_new (
+ SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/Digest",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
+ SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
+ SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_callback,
+ NULL);
+ soup_server_add_auth_domain (server, auth_domain);
+ g_object_unref (auth_domain);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ if (run_tests) {
+ uri = soup_uri_new ("http://127.0.0.1");
+ soup_uri_set_port (uri, soup_server_get_port (server));
+ do_auth_tests (uri);
+ soup_uri_free (uri);
+ } else {
+ printf ("Listening on port %d\n", soup_server_get_port (server));
+ g_main_loop_run (loop);
+ }
+
+ g_main_loop_unref (loop);
+ soup_test_server_quit_unref (server);
+
+ if (run_tests)
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#include <libsoup/soup.h>
+
+#ifdef HAVE_MMAP
+struct mapping {
+ void *start;
+ size_t length;
+};
+
+static void
+free_mapping (gpointer data)
+{
+ struct mapping *mapping = data;
+ munmap (mapping->start, mapping->length);
+ g_slice_free (struct mapping, mapping);
+}
+#endif
+
+static int
+compare_strings (gconstpointer a, gconstpointer b)
+{
+ const char **sa = (const char **)a;
+ const char **sb = (const char **)b;
+
+ return strcmp (*sa, *sb);
+}
+
+static GString *
+get_directory_listing (const char *path)
+{
+ GPtrArray *entries;
+ GString *listing;
+ char *escaped;
+ DIR *dir;
+ struct dirent *dent;
+ int i;
+
+ entries = g_ptr_array_new ();
+ dir = opendir (path);
+ if (dir) {
+ while ((dent = readdir (dir))) {
+ if (!strcmp (dent->d_name, ".") ||
+ (!strcmp (dent->d_name, "..") &&
+ !strcmp (path, "./")))
+ continue;
+ escaped = g_markup_escape_text (dent->d_name, -1);
+ g_ptr_array_add (entries, escaped);
+ }
+ closedir (dir);
+ }
+
+ g_ptr_array_sort (entries, (GCompareFunc)compare_strings);
+
+ listing = g_string_new ("<html>\r\n");
+ escaped = g_markup_escape_text (strchr (path, '/'), -1);
+ g_string_append_printf (listing, "<head><title>Index of %s</title></head>\r\n", escaped);
+ g_string_append_printf (listing, "<body><h1>Index of %s</h1>\r\n<p>\r\n", escaped);
+ g_free (escaped);
+ for (i = 0; i < entries->len; i++) {
+ g_string_append_printf (listing, "<a href=\"%s\">%s</a><br>\r\n",
+ (char *)entries->pdata[i],
+ (char *)entries->pdata[i]);
+ g_free (entries->pdata[i]);
+ }
+ g_string_append (listing, "</body>\r\n</html>\r\n");
+
+ g_ptr_array_free (entries, TRUE);
+ return listing;
+}
+
+static void
+do_get (SoupServer *server, SoupMessage *msg, const char *path)
+{
+ char *slash;
+ struct stat st;
+ int fd;
+
+ if (stat (path, &st) == -1) {
+ if (errno == EPERM)
+ soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
+ else if (errno == ENOENT)
+ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+ else
+ soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (S_ISDIR (st.st_mode)) {
+ GString *listing;
+ char *index_path;
+
+ slash = strrchr (path, '/');
+ if (!slash || slash[1]) {
+ char *uri, *redir_uri;
+
+ uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+ redir_uri = g_strdup_printf ("%s/", uri);
+ soup_message_headers_append (msg->response_headers,
+ "Location", redir_uri);
+ soup_message_set_status (msg, SOUP_STATUS_MOVED_PERMANENTLY);
+ g_free (redir_uri);
+ g_free (uri);
+ return;
+ }
+
+ index_path = g_strdup_printf ("%s/index.html", path);
+ if (stat (index_path, &st) != -1) {
+ do_get (server, msg, index_path);
+ g_free (index_path);
+ return;
+ }
+
+ listing = get_directory_listing (path);
+ soup_message_set_response (msg, "text/html",
+ SOUP_MEMORY_TAKE,
+ listing->str, listing->len);
+ g_string_free (listing, FALSE);
+ return;
+ }
+
+ fd = open (path, O_RDONLY);
+ if (fd == -1) {
+ soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (msg->method == SOUP_METHOD_GET) {
+#ifdef HAVE_MMAP
+ struct mapping *mapping = g_slice_new (struct mapping);
+ SoupBuffer *buffer;
+
+ mapping->start = mmap (NULL, st.st_size, PROT_READ,
+ MAP_PRIVATE, fd, 0);
+ mapping->length = st.st_size;
+ buffer = soup_buffer_new_with_owner (mapping->start,
+ mapping->length,
+ mapping, free_mapping);
+ soup_message_body_append_buffer (msg->response_body, buffer);
+ soup_buffer_free (buffer);
+#else
+ char *buf;
+
+ buf = g_malloc (st.st_size);
+ read (fd, buf, st.st_size);
+ close (fd);
+ soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
+ buf, st.st_size);
+#endif
+ } else /* msg->method == SOUP_METHOD_HEAD */ {
+ char *length;
+
+ /* We could just use the same code for both GET and
+ * HEAD. But we'll optimize and avoid the extra
+ * malloc.
+ */
+ length = g_strdup_printf ("%lu", (gulong)st.st_size);
+ soup_message_headers_append (msg->response_headers,
+ "Content-Length", length);
+ g_free (length);
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static void
+do_put (SoupServer *server, SoupMessage *msg, const char *path)
+{
+ struct stat st;
+ FILE *f;
+ gboolean created = TRUE;
+
+ if (stat (path, &st) != -1) {
+ const char *match = soup_message_headers_get_one (msg->request_headers, "If-None-Match");
+ if (match && !strcmp (match, "*")) {
+ soup_message_set_status (msg, SOUP_STATUS_CONFLICT);
+ return;
+ }
+
+ if (!S_ISREG (st.st_mode)) {
+ soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
+ return;
+ }
+
+ created = FALSE;
+ }
+
+ f = fopen (path, "w");
+ if (!f) {
+ soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ fwrite (msg->request_body->data, 1, msg->request_body->length, f);
+ fclose (f);
+
+ soup_message_set_status (msg, created ? SOUP_STATUS_CREATED : SOUP_STATUS_OK);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ char *file_path;
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+
+ printf ("%s %s HTTP/1.%d\n", msg->method, path,
+ soup_message_get_http_version (msg));
+ soup_message_headers_iter_init (&iter, msg->request_headers);
+ while (soup_message_headers_iter_next (&iter, &name, &value))
+ printf ("%s: %s\n", name, value);
+ if (msg->request_body->length)
+ printf ("%s\n", msg->request_body->data);
+
+ file_path = g_strdup_printf (".%s", path);
+
+ if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
+ do_get (server, msg, file_path);
+ else if (msg->method == SOUP_METHOD_PUT)
+ do_put (server, msg, file_path);
+ else
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+
+ g_free (file_path);
+ printf (" -> %d %s\n\n", msg->status_code, msg->reason_phrase);
+}
+
+static void
+quit (int sig)
+{
+ /* Exit cleanly on ^C in case we're valgrinding. */
+ exit (0);
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ SoupServer *server, *ssl_server;
+ int opt;
+ int port = SOUP_ADDRESS_ANY_PORT;
+ int ssl_port = SOUP_ADDRESS_ANY_PORT;
+ const char *ssl_cert_file = NULL, *ssl_key_file = NULL;
+
+ g_thread_init (NULL);
+ g_type_init ();
+ signal (SIGINT, quit);
+
+ while ((opt = getopt (argc, argv, "p:k:c:s:")) != -1) {
+ switch (opt) {
+ case 'p':
+ port = atoi (optarg);
+ break;
+ case 'k':
+ ssl_key_file = optarg;
+ break;
+ case 'c':
+ ssl_cert_file = optarg;
+ break;
+ case 's':
+ ssl_port = atoi (optarg);
+ break;
+ default:
+ fprintf (stderr, "Usage: %s [-p port] [-c ssl-cert-file -k ssl-key-file [-s ssl-port]]\n",
+ argv[0]);
+ exit (1);
+ }
+ }
+
+ server = soup_server_new (SOUP_SERVER_PORT, port,
+ SOUP_SERVER_SERVER_HEADER, "simple-httpd ",
+ NULL);
+ if (!server) {
+ fprintf (stderr, "Unable to bind to server port %d\n", port);
+ exit (1);
+ }
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+ printf ("\nStarting Server on port %d\n",
+ soup_server_get_port (server));
+ soup_server_run_async (server);
+
+ if (ssl_cert_file && ssl_key_file) {
+ ssl_server = soup_server_new (
+ SOUP_SERVER_PORT, ssl_port,
+ SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
+ SOUP_SERVER_SSL_KEY_FILE, ssl_key_file,
+ NULL);
+
+ if (!ssl_server) {
+ fprintf (stderr, "Unable to bind to SSL server port %d\n", ssl_port);
+ exit (1);
+ }
+ soup_server_add_handler (ssl_server, NULL,
+ server_callback, NULL, NULL);
+ printf ("Starting SSL Server on port %d\n",
+ soup_server_get_port (ssl_server));
+ soup_server_run_async (ssl_server);
+ }
+
+ printf ("\nWaiting for requests...\n");
+
+ loop = g_main_loop_new (NULL, TRUE);
+ g_main_loop_run (loop);
+
+ return 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+/* WARNING: this is really really really not especially compliant with
+ * RFC 2616. But it does work for basic stuff.
+ */
+
+static SoupSession *session;
+static SoupServer *server;
+
+static void
+copy_header (const char *name, const char *value, gpointer dest_headers)
+{
+ soup_message_headers_append (dest_headers, name, value);
+}
+
+static void
+send_headers (SoupMessage *from, SoupMessage *to)
+{
+ printf ("[%p] HTTP/1.%d %d %s\n", to,
+ soup_message_get_http_version (from),
+ from->status_code, from->reason_phrase);
+
+ soup_message_set_status_full (to, from->status_code,
+ from->reason_phrase);
+ soup_message_headers_foreach (from->response_headers, copy_header,
+ to->response_headers);
+ soup_message_headers_remove (to->response_headers, "Content-Length");
+ soup_server_unpause_message (server, to);
+}
+
+static void
+send_chunk (SoupMessage *from, SoupBuffer *chunk, SoupMessage *to)
+{
+ printf ("[%p] writing chunk of %lu bytes\n", to,
+ (unsigned long)chunk->length);
+
+ soup_message_body_append_buffer (to->response_body, chunk);
+ soup_server_unpause_message (server, to);
+}
+
+static void
+client_msg_failed (SoupMessage *msg, gpointer msg2)
+{
+ soup_session_cancel_message (session, msg2, SOUP_STATUS_IO_ERROR);
+}
+
+static void
+finish_msg (SoupSession *session, SoupMessage *msg2, gpointer data)
+{
+ SoupMessage *msg = data;
+
+ printf ("[%p] done\n\n", msg);
+ g_signal_handlers_disconnect_by_func (msg, client_msg_failed, msg2);
+
+ soup_message_body_complete (msg->response_body);
+ soup_server_unpause_message (server, msg);
+ g_object_unref (msg);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ SoupMessage *msg2;
+ char *uristr;
+
+ uristr = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+ printf ("[%p] %s %s HTTP/1.%d\n", msg, msg->method, uristr,
+ soup_message_get_http_version (msg));
+
+ if (msg->method == SOUP_METHOD_CONNECT) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ msg2 = soup_message_new (msg->method, uristr);
+ soup_message_headers_foreach (msg->request_headers, copy_header,
+ msg2->request_headers);
+ soup_message_headers_remove (msg2->request_headers, "Host");
+ soup_message_headers_remove (msg2->request_headers, "Connection");
+
+ if (msg->request_body->length) {
+ SoupBuffer *request = soup_message_body_flatten (msg->request_body);
+ soup_message_body_append_buffer (msg2->request_body, request);
+ soup_buffer_free (request);
+ }
+ soup_message_headers_set_encoding (msg->response_headers,
+ SOUP_ENCODING_CHUNKED);
+
+ g_signal_connect (msg2, "got_headers",
+ G_CALLBACK (send_headers), msg);
+ g_signal_connect (msg2, "got_chunk",
+ G_CALLBACK (send_chunk), msg);
+
+ g_signal_connect (msg, "finished", G_CALLBACK (client_msg_failed), msg2);
+
+ soup_session_queue_message (session, msg2, finish_msg, msg);
+
+ g_object_ref (msg);
+ soup_server_pause_message (server, msg);
+}
+
+static gboolean
+auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
+ const char *username, const char *password, gpointer data)
+{
+ return !strcmp (username, "user") && !strcmp (password, "password");
+}
+
+static void
+quit (int sig)
+{
+ /* Exit cleanly on ^C in case we're valgrinding. */
+ exit (0);
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ int opt;
+ int port = SOUP_ADDRESS_ANY_PORT;
+ SoupAuthDomain *auth_domain = NULL;
+
+ g_thread_init (NULL);
+ g_type_init ();
+ signal (SIGINT, quit);
+
+ while ((opt = getopt (argc, argv, "ap:")) != -1) {
+ switch (opt) {
+ case 'a':
+ auth_domain = soup_auth_domain_basic_new (
+ SOUP_AUTH_DOMAIN_REALM, "simple-proxy",
+ SOUP_AUTH_DOMAIN_PROXY, TRUE,
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
+ NULL);
+ break;
+ case 'p':
+ port = atoi (optarg);
+ break;
+ default:
+ fprintf (stderr, "Usage: %s [-p port] [-n]\n",
+ argv[0]);
+ exit (1);
+ }
+ }
+
+ server = soup_server_new (SOUP_SERVER_PORT, port,
+ NULL);
+ if (!server) {
+ fprintf (stderr, "Unable to bind to server port %d\n", port);
+ exit (1);
+ }
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+ if (auth_domain) {
+ soup_server_add_auth_domain (server, auth_domain);
+ g_object_unref (auth_domain);
+ }
+
+ printf ("\nStarting proxy on port %d\n",
+ soup_server_get_port (server));
+ soup_server_run_async (server);
+
+ session = soup_session_async_new ();
+
+ printf ("\nWaiting for requests...\n");
+
+ loop = g_main_loop_new (NULL, TRUE);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ return 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+SoupSession *session;
+SoupURI *base_uri;
+SoupMessageBody *chunk_data;
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ GError *error = NULL;
+ char *query_key;
+ char *contents;
+ gsize length, offset;
+ gboolean empty_response = FALSE;
+
+ if (msg->method != SOUP_METHOD_GET) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+
+ if (query) {
+ query_key = g_hash_table_lookup (query, "chunked");
+ if (query_key && g_str_equal (query_key, "yes")) {
+ soup_message_headers_set_encoding (msg->response_headers,
+ SOUP_ENCODING_CHUNKED);
+ }
+
+ query_key = g_hash_table_lookup (query, "empty_response");
+ if (query_key && g_str_equal (query_key, "yes"))
+ empty_response = TRUE;
+ }
+
+ if (!strcmp (path, "/mbox")) {
+ if (empty_response) {
+ contents = g_strdup ("");
+ length = 0;
+ } else {
+ g_file_get_contents (SRCDIR "/resources/mbox",
+ &contents, &length,
+ &error);
+ }
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "text/plain");
+ }
+
+ if (g_str_has_prefix (path, "/text_or_binary/")) {
+ char *base_name = g_path_get_basename (path);
+ char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
+
+ g_file_get_contents (file_name,
+ &contents, &length,
+ &error);
+
+ g_free (base_name);
+ g_free (file_name);
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "text/plain");
+ }
+
+ if (g_str_has_prefix (path, "/unknown/")) {
+ char *base_name = g_path_get_basename (path);
+ char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
+
+ g_file_get_contents (file_name,
+ &contents, &length,
+ &error);
+
+ g_free (base_name);
+ g_free (file_name);
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "UNKNOWN/unknown");
+ }
+
+ if (g_str_has_prefix (path, "/type/")) {
+ char **components = g_strsplit (path, "/", 4);
+ char *ptr;
+
+ char *base_name = g_path_get_basename (path);
+ char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
+
+ g_file_get_contents (file_name,
+ &contents, &length,
+ &error);
+
+ g_free (base_name);
+ g_free (file_name);
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ /* Hack to allow passing type in the URI */
+ ptr = g_strrstr (components[2], "_");
+ *ptr = '/';
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", components[2]);
+ g_strfreev (components);
+ }
+
+ if (g_str_has_prefix (path, "/multiple_headers/")) {
+ char *base_name = g_path_get_basename (path);
+ char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
+
+ g_file_get_contents (file_name,
+ &contents, &length,
+ &error);
+
+ g_free (base_name);
+ g_free (file_name);
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "text/xml");
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "text/plain");
+ }
+
+ for (offset = 0; offset < length; offset += 500) {
+ soup_message_body_append (msg->response_body,
+ SOUP_MEMORY_COPY,
+ contents + offset,
+ MIN(500, length - offset));
+ }
+ soup_message_body_complete (msg->response_body);
+
+ g_free (contents);
+}
+
+static gboolean
+unpause_msg (gpointer data)
+{
+ SoupMessage *msg = (SoupMessage*)data;
+ debug_printf (2, " unpause\n");
+ soup_session_unpause_message (session, msg);
+ return FALSE;
+}
+
+
+static void
+content_sniffed (SoupMessage *msg, char *content_type, GHashTable *params, gpointer data)
+{
+ gboolean should_pause = GPOINTER_TO_INT (data);
+
+ debug_printf (2, " content-sniffed -> %s\n", content_type);
+
+ if (g_object_get_data (G_OBJECT (msg), "got-chunk")) {
+ debug_printf (1, " got-chunk got emitted before content-sniffed\n");
+ errors++;
+ }
+
+ g_object_set_data (G_OBJECT (msg), "content-sniffed", GINT_TO_POINTER (TRUE));
+
+ if (should_pause) {
+ debug_printf (2, " pause\n");
+ soup_session_pause_message (session, msg);
+ g_idle_add (unpause_msg, msg);
+ }
+}
+
+static void
+got_headers (SoupMessage *msg, gpointer data)
+{
+ gboolean should_pause = GPOINTER_TO_INT (data);
+
+ debug_printf (2, " got-headers\n");
+
+ if (g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
+ debug_printf (1, " content-sniffed got emitted before got-headers\n");
+ errors++;
+ }
+
+ g_object_set_data (G_OBJECT (msg), "got-headers", GINT_TO_POINTER (TRUE));
+
+ if (should_pause) {
+ debug_printf (2, " pause\n");
+ soup_session_pause_message (session, msg);
+ g_idle_add (unpause_msg, msg);
+ }
+}
+
+static void
+got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer data)
+{
+ gboolean should_accumulate = GPOINTER_TO_INT (data);
+
+ debug_printf (2, " got-chunk\n");
+
+ g_object_set_data (G_OBJECT (msg), "got-chunk", GINT_TO_POINTER (TRUE));
+
+ if (!should_accumulate) {
+ if (!chunk_data)
+ chunk_data = soup_message_body_new ();
+ soup_message_body_append_buffer (chunk_data, chunk);
+ }
+}
+
+static void
+finished (SoupSession *session, SoupMessage *msg, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop*)data;
+ g_main_loop_quit (loop);
+}
+
+static void
+do_signals_test (gboolean should_content_sniff,
+ gboolean should_pause,
+ gboolean should_accumulate,
+ gboolean chunked_encoding,
+ gboolean empty_response)
+{
+ SoupURI *uri = soup_uri_new_with_base (base_uri, "/mbox");
+ SoupMessage *msg = soup_message_new_from_uri ("GET", uri);
+ GMainLoop *loop = g_main_loop_new (NULL, TRUE);
+ char *contents;
+ gsize length;
+ GError *error = NULL;
+ SoupBuffer *body = NULL;
+
+ debug_printf (1, "do_signals_test(%ssniff, %spause, %saccumulate, %schunked, %sempty)\n",
+ should_content_sniff ? "" : "!",
+ should_pause ? "" : "!",
+ should_accumulate ? "" : "!",
+ chunked_encoding ? "" : "!",
+ empty_response ? "" : "!");
+
+ if (chunked_encoding)
+ soup_uri_set_query (uri, "chunked=yes");
+
+ if (empty_response) {
+ if (uri->query) {
+ char *tmp = uri->query;
+ uri->query = g_strdup_printf ("%s&empty_response=yes", tmp);
+ g_free (tmp);
+ } else
+ soup_uri_set_query (uri, "empty_response=yes");
+ }
+
+ soup_message_set_uri (msg, uri);
+
+ soup_message_body_set_accumulate (msg->response_body, should_accumulate);
+
+ g_object_connect (msg,
+ "signal::got-headers", got_headers, GINT_TO_POINTER (should_pause),
+ "signal::got-chunk", got_chunk, GINT_TO_POINTER (should_accumulate),
+ "signal::content_sniffed", content_sniffed, GINT_TO_POINTER (should_pause),
+ NULL);
+
+ g_object_ref (msg);
+ soup_session_queue_message (session, msg, finished, loop);
+
+ g_main_loop_run (loop);
+
+ if (!should_content_sniff &&
+ g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
+ debug_printf (1, " content-sniffed got emitted without a sniffer\n");
+ errors++;
+ } else if (should_content_sniff &&
+ !g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
+ debug_printf (1, " content-sniffed did not get emitted\n");
+ errors++;
+ }
+
+ if (empty_response) {
+ contents = g_strdup ("");
+ length = 0;
+ } else {
+ g_file_get_contents (SRCDIR "/resources/mbox",
+ &contents, &length,
+ &error);
+ }
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (!should_accumulate && chunk_data)
+ body = soup_message_body_flatten (chunk_data);
+ else if (msg->response_body)
+ body = soup_message_body_flatten (msg->response_body);
+
+ if (body && body->length != length) {
+ debug_printf (1, " lengths do not match\n");
+ errors++;
+ }
+
+ if (body && memcmp (body->data, contents, length)) {
+ debug_printf (1, " downloaded data does not match\n");
+ errors++;
+ }
+
+ g_free (contents);
+ if (body)
+ soup_buffer_free (body);
+ if (chunk_data) {
+ soup_message_body_free (chunk_data);
+ chunk_data = NULL;
+ }
+
+ soup_uri_free (uri);
+ g_object_unref (msg);
+ g_main_loop_unref (loop);
+}
+
+static void
+sniffing_content_sniffed (SoupMessage *msg, const char *content_type,
+ GHashTable *params, gpointer data)
+{
+ char **sniffed_type = (char **)data;
+ GString *full_header;
+ GList *keys;
+ GList *iter;
+
+ if (params == NULL) {
+ *sniffed_type = g_strdup (content_type);
+ return;
+ }
+
+ full_header = g_string_new (content_type);
+ g_string_append (full_header, "; ");
+
+ keys = g_hash_table_get_keys (params);
+ for (iter = keys; iter != NULL; iter = iter->next) {
+ const gchar *value = (const gchar*) g_hash_table_lookup (params, iter->data);
+
+ soup_header_g_string_append_param (full_header,
+ (const gchar*) iter->data,
+ value);
+ }
+
+ *sniffed_type = full_header->str;
+
+ g_string_free (full_header, FALSE);
+ g_list_free (keys);
+}
+
+static void
+test_sniffing (const char *path, const char *expected_type)
+{
+ SoupURI *uri = soup_uri_new_with_base (base_uri, path);
+ SoupMessage *msg = soup_message_new_from_uri ("GET", uri);
+ GMainLoop *loop = g_main_loop_new (NULL, TRUE);
+ char *sniffed_type = NULL;
+
+ debug_printf (1, "test_sniffing(\"%s\", \"%s\")\n", path, expected_type);
+
+ g_signal_connect (msg, "content-sniffed",
+ G_CALLBACK (sniffing_content_sniffed), &sniffed_type);
+
+ g_object_ref (msg);
+
+ soup_session_queue_message (session, msg, finished, loop);
+
+ g_main_loop_run (loop);
+
+ if (!sniffed_type) {
+ debug_printf (1, " message was not sniffed!\n");
+ errors++;
+ } else if (strcmp (sniffed_type, expected_type) != 0) {
+ debug_printf (1, " sniffing failed! expected %s, got %s\n",
+ expected_type, sniffed_type);
+ errors++;
+ }
+ g_free (sniffed_type);
+
+ soup_uri_free (uri);
+ g_object_unref (msg);
+ g_main_loop_unref (loop);
+}
+
+static void
+test_disabled (const char *path)
+{
+ SoupURI *uri = soup_uri_new_with_base (base_uri, path);
+ SoupMessage *msg = soup_message_new_from_uri ("GET", uri);
+ GMainLoop *loop = g_main_loop_new (NULL, TRUE);
+ char *sniffed_type = NULL;
+
+ soup_message_disable_feature (msg, SOUP_TYPE_CONTENT_SNIFFER);
+
+ debug_printf (1, "test_disabled(\"%s\")\n", path);
+
+ g_signal_connect (msg, "content-sniffed",
+ G_CALLBACK (sniffing_content_sniffed), &sniffed_type);
+
+ g_object_ref (msg);
+
+ soup_session_queue_message (session, msg, finished, loop);
+
+ g_main_loop_run (loop);
+
+ if (sniffed_type) {
+ debug_printf (1, " message was sniffed!\n");
+ errors++;
+ g_free (sniffed_type);
+ }
+
+ soup_uri_free (uri);
+ g_object_unref (msg);
+ g_main_loop_unref (loop);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+
+ test_init (argc, argv, NULL);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ base_uri = soup_uri_new ("http://127.0.0.1/");
+ soup_uri_set_port (base_uri, soup_server_get_port (server));
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ /* No sniffer, no content_sniffed should be emitted */
+ do_signals_test (FALSE, FALSE, FALSE, FALSE, FALSE);
+ do_signals_test (FALSE, FALSE, FALSE, TRUE, FALSE);
+ do_signals_test (FALSE, FALSE, TRUE, FALSE, FALSE);
+ do_signals_test (FALSE, FALSE, TRUE, TRUE, FALSE);
+
+ do_signals_test (FALSE, TRUE, TRUE, FALSE, FALSE);
+ do_signals_test (FALSE, TRUE, TRUE, TRUE, FALSE);
+ do_signals_test (FALSE, TRUE, FALSE, FALSE, FALSE);
+ do_signals_test (FALSE, TRUE, FALSE, TRUE, FALSE);
+
+ /* Tests that the signals are correctly emitted for empty
+ * responses; see
+ * http://bugzilla.gnome.org/show_bug.cgi?id=587907 */
+
+ do_signals_test (FALSE, TRUE, TRUE, FALSE, TRUE);
+ do_signals_test (FALSE, TRUE, TRUE, TRUE, TRUE);
+
+ soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
+
+ /* Now, with a sniffer, content_sniffed must be emitted after
+ * got-headers, and before got-chunk.
+ */
+ do_signals_test (TRUE, FALSE, FALSE, FALSE, FALSE);
+ do_signals_test (TRUE, FALSE, FALSE, TRUE, FALSE);
+ do_signals_test (TRUE, FALSE, TRUE, FALSE, FALSE);
+ do_signals_test (TRUE, FALSE, TRUE, TRUE, FALSE);
+
+ do_signals_test (TRUE, TRUE, TRUE, FALSE, FALSE);
+ do_signals_test (TRUE, TRUE, TRUE, TRUE, FALSE);
+ do_signals_test (TRUE, TRUE, FALSE, FALSE, FALSE);
+ do_signals_test (TRUE, TRUE, FALSE, TRUE, FALSE);
+
+ /* Empty response tests */
+ do_signals_test (TRUE, TRUE, TRUE, FALSE, TRUE);
+ do_signals_test (TRUE, TRUE, TRUE, TRUE, TRUE);
+
+ /* Test the text_or_binary sniffing path */
+
+ /* GIF is a 'safe' type */
+ test_sniffing ("/text_or_binary/home.gif", "image/gif");
+
+ /* With our current code, no sniffing is done using GIO, so
+ * the mbox will be identified as text/plain; should we change
+ * this?
+ */
+ test_sniffing ("/text_or_binary/mbox", "text/plain");
+
+ /* HTML is considered unsafe for this algorithm, since it is
+ * scriptable, so going from text/plain to text/html is
+ * considered 'privilege escalation'
+ */
+ test_sniffing ("/text_or_binary/test.html", "text/plain");
+
+ /* text/plain with binary content and unknown pattern should be
+ * application/octet-stream */
+ test_sniffing ("/text_or_binary/text_binary.txt", "application/octet-stream");
+
+ /* text/plain with binary content and scriptable pattern should be
+ * application/octet-stream to avoid 'privilege escalation' */
+ test_sniffing ("/text_or_binary/html_binary.html", "application/octet-stream");
+
+ /* text/plain with binary content and non scriptable known pattern should
+ * be the given type */
+ test_sniffing ("/text_or_binary/ps_binary.ps", "application/postscript");
+
+ /* Test the unknown sniffing path */
+
+ test_sniffing ("/unknown/test.html", "text/html");
+ test_sniffing ("/unknown/home.gif", "image/gif");
+ test_sniffing ("/unknown/mbox", "text/plain");
+ test_sniffing ("/unknown/text_binary.txt", "application/octet-stream");
+
+ /* Test the XML sniffing path */
+
+ test_sniffing ("/type/text_xml/home.gif", "text/xml");
+ test_sniffing ("/type/anice_type+xml/home.gif", "anice/type+xml");
+ test_sniffing ("/type/application_xml/home.gif", "application/xml");
+
+ /* Test the image sniffing path */
+
+ test_sniffing ("/type/image_png/home.gif", "image/gif");
+
+ /* Test the feed or html path */
+
+ test_sniffing ("/type/text_html/test.html", "text/html");
+ test_sniffing ("/type/text_html/rss20.xml", "application/rss+xml");
+ test_sniffing ("/type/text_html/atom.xml", "application/atom+xml");
+
+ /* The spec tells us to only use the last Content-Type header */
+
+ test_sniffing ("/multiple_headers/home.gif", "image/gif");
+
+ /* Test that we keep the parameters when sniffing */
+ test_sniffing ("/type/text_html; charset=UTF-8/test.html", "text/html; charset=UTF-8");
+
+ /* Test that disabling the sniffer works correctly */
+
+ test_disabled ("/text_or_binary/home.gif");
+
+ soup_uri_free (base_uri);
+
+ soup_test_session_abort_unref (session);
+ soup_test_server_quit_unref (server);
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+#define RESPONSE_CHUNK_SIZE 1024
+
+char *full_response, *full_response_md5;
+gsize full_response_length;
+
+static void
+get_full_response (void)
+{
+ GError *error = NULL;
+
+ if (!g_file_get_contents (SRCDIR "/index.txt",
+ &full_response,
+ &full_response_length,
+ &error)) {
+ fprintf (stderr, "Could not read index file %s: %s\n",
+ SRCDIR "/index.txt", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ full_response_md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+ (guchar *)full_response,
+ full_response_length);
+}
+
+static void
+write_next_chunk (SoupMessage *msg, gpointer user_data)
+{
+ gsize *offset = user_data;
+ gsize chunk_length;
+
+ chunk_length = MIN (RESPONSE_CHUNK_SIZE, full_response_length - *offset);
+ if (chunk_length > 0) {
+ debug_printf (2, " writing chunk\n");
+ soup_message_body_append (msg->response_body,
+ SOUP_MEMORY_STATIC,
+ full_response + *offset,
+ chunk_length);
+ *offset += chunk_length;
+ } else {
+ debug_printf (2, " done\n");
+ /* This is only actually needed in the chunked and eof
+ * cases, but it's harmless in the content-length
+ * case.
+ */
+ soup_message_body_complete (msg->response_body);
+ }
+}
+
+static void
+free_offset (SoupMessage *msg, gpointer offset)
+{
+ g_free (offset);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ gsize *offset;
+
+ if (!strcmp (path, "/chunked")) {
+ soup_message_headers_set_encoding (msg->response_headers,
+ SOUP_ENCODING_CHUNKED);
+ } else if (!strcmp (path, "/content-length")) {
+ soup_message_headers_set_encoding (msg->response_headers,
+ SOUP_ENCODING_CONTENT_LENGTH);
+ soup_message_headers_set_content_length (msg->response_headers,
+ full_response_length);
+ } else if (!strcmp (path, "/eof")) {
+ soup_message_headers_set_encoding (msg->response_headers,
+ SOUP_ENCODING_EOF);
+ } else {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+ return;
+ }
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+
+ offset = g_new0 (gsize, 1);
+ g_signal_connect (msg, "wrote_headers",
+ G_CALLBACK (write_next_chunk), offset);
+ g_signal_connect (msg, "wrote_chunk",
+ G_CALLBACK (write_next_chunk), offset);
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (free_offset), offset);
+}
+
+static void
+do_request (SoupSession *session, SoupURI *base_uri, char *path)
+{
+ SoupURI *uri;
+ SoupMessage *msg;
+ char *md5;
+
+ uri = soup_uri_new_with_base (base_uri, path);
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ soup_session_send_message (session, msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, " message failed: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+
+ if (msg->response_body->length != full_response_length) {
+ debug_printf (1, " received length mismatch: expected %d, got %d\n",
+ (int)full_response_length, (int)msg->request_body->length);
+ errors++;
+ }
+
+ md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+ (guchar *)msg->response_body->data,
+ msg->response_body->length);
+ if (strcmp (md5, full_response_md5) != 0) {
+ debug_printf (1, " data mismatch: expected %s, got %s\n",
+ full_response_md5, md5);
+ errors++;
+ }
+ g_free (md5);
+
+ g_object_unref (msg);
+}
+
+static void
+do_tests (SoupURI *base_uri)
+{
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ debug_printf (1, "Chunked encoding\n");
+ do_request (session, base_uri, "chunked");
+ debug_printf (1, "\n");
+ debug_printf (1, "Content-Length encoding\n");
+ do_request (session, base_uri, "content-length");
+ debug_printf (1, "\n");
+ debug_printf (1, "EOF encoding\n");
+ do_request (session, base_uri, "eof");
+ soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ GMainLoop *loop;
+ SoupServer *server;
+ guint port;
+ SoupURI *base_uri;
+
+ test_init (argc, argv, NULL);
+ get_full_response ();
+
+ server = soup_test_server_new (FALSE);
+ soup_server_add_handler (server, NULL,
+ server_callback, NULL, NULL);
+ port = soup_server_get_port (server);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ base_uri = soup_uri_new ("http://127.0.0.1");
+ soup_uri_set_port (base_uri, port);
+ do_tests (base_uri);
+ soup_uri_free (base_uri);
+
+ g_main_loop_unref (loop);
+
+ g_free (full_response);
+ g_free (full_response_md5);
+ soup_test_server_quit_unref (server);
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDjzCCAvigAwIBAgIBADANBgkqhkiG9w0BAQQFADCBkjELMAkGA1UEBhMCVVMx
+FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcTBkJvc3RvbjEPMA0GA1UE
+ChMGWGltaWFuMRUwEwYDVQQLEwxTb3VwIEtpdGNoZW4xEjAQBgNVBAMTCWxvY2Fs
+aG9zdDEeMBwGCSqGSIb3DQEJARYPc291cEB4aW1pYW4uY29tMB4XDTAzMDkyMzE4
+Mzc0MVoXDTEzMDkyMzE4Mzc0MVowgZIxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1N
+YXNzYWNodXNldHRzMQ8wDQYDVQQHEwZCb3N0b24xDzANBgNVBAoTBlhpbWlhbjEV
+MBMGA1UECxMMU291cCBLaXRjaGVuMRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkq
+hkiG9w0BCQEWD3NvdXBAeGltaWFuLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAwzT/WxfdXqb2hbyjQav3FtN7tLxj3UbZKCKDYlizBsNLxb9exfebhV4h
+CoAcaSNvLUnk3tAXnk+BDsIC1V4SbwqHYR17PnO3YZ8fkNwh5RGZwNx+zafdfFyu
++3Sh+mE03bljpDlTsgPL8CiFCd68MPRnuHoKt5iTpSyLC6Df0qcCAwEAAaOB8jCB
+7zAdBgNVHQ4EFgQU9A9omrgBK5Kkl6FRxrgJU2voj4Uwgb8GA1UdIwSBtzCBtIAU
+9A9omrgBK5Kkl6FRxrgJU2voj4WhgZikgZUwgZIxCzAJBgNVBAYTAlVTMRYwFAYD
+VQQIEw1NYXNzYWNodXNldHRzMQ8wDQYDVQQHEwZCb3N0b24xDzANBgNVBAoTBlhp
+bWlhbjEVMBMGA1UECxMMU291cCBLaXRjaGVuMRIwEAYDVQQDEwlsb2NhbGhvc3Qx
+HjAcBgkqhkiG9w0BCQEWD3NvdXBAeGltaWFuLmNvbYIBADAMBgNVHRMEBTADAQH/
+MA0GCSqGSIb3DQEBBAUAA4GBAGCV56N7bEDNdE76T8i68gS00NIVVosVQjS39Ojd
+ED+rvq0YYvuc2UXlzAonuCJfwFc73g4wSIjS0xijF5rnugZ+aay0LNv2y+Rf34CQ
+RNswrwurFjlxgTOO+Wx2IM64mAnBfj43M8uKEZFqAiGKrZZ0xIqyUMlku0FgXDH2
+Jvpg
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDDNP9bF91epvaFvKNBq/cW03u0vGPdRtkoIoNiWLMGw0vFv17F
+95uFXiEKgBxpI28tSeTe0BeeT4EOwgLVXhJvCodhHXs+c7dhnx+Q3CHlEZnA3H7N
+p918XK77dKH6YTTduWOkOVOyA8vwKIUJ3rww9Ge4egq3mJOlLIsLoN/SpwIDAQAB
+AoGAOGAi6zzuKrrPcXo0L/ApEQeMr3rE4I/ogUXOaeWx9l8KkBafmU7UNGUl57Fu
+AxM/tXWkypCQcaEGZau0Q8jCS5wKgynNi72F4OzBqgjgW4vvtrjfC1LagnCd2ZMX
+V5XVECjO/sEDg0hJeOsXlKbECAgvHMU3dSCGO7DmuG9tIxkCQQDsth1VvVjOdfp6
+klOfYzbAM1p9HIcNPJMeuBFqq//UHX4aPqh/6G6W06TOTN+bjZBmitG9yjV958t2
+rPxl64f7AkEA0x0WOLm5S0LNsv7zwjXuTcj+NCHL36b3dK90oxX8Gq69PANL/EJY
+ItpHNLgzzo4DRmQy8q0WZlC9HYk1YljERQJAEN7+AkFnlfeErb3GJgMNQO+oEGi7
+G29o0PSvkRnHNxgPB9HVcqBfWXKmOWnzOgQB+b0FK/DAlUOzFbdImf8KhwJAFLty
+hzeV/tIcqUtoXNY3BOSMMkpvXxNikc75QVrTWzt10gLw32EUjreo7oB4dfx0TeFh
+L3vYC0w6hkAHQhU9kQJAPSEQ+Bqzlk6BrQNrNFEVzi1Rwpz7LOzhOjuYW6bsiAdX
+axA4r6Xh25x08ZU7cqX7gwVLHL6pgrEKuUs0Nc5Klg==
+-----END RSA PRIVATE KEY-----
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "test-utils.h"
+#include "libsoup/soup.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_APACHE
+static gboolean apache_running;
+#endif
+
+static SoupLogger *logger;
+
+int debug_level, errors;
+gboolean expect_warning, tls_available;
+static int http_debug_level;
+
+static gboolean
+increment_debug_level (const char *option_name, const char *value,
+ gpointer data, GError **error)
+{
+ debug_level++;
+ return TRUE;
+}
+
+static gboolean
+increment_http_debug_level (const char *option_name, const char *value,
+ gpointer data, GError **error)
+{
+ http_debug_level++;
+ return TRUE;
+}
+
+static GOptionEntry debug_entry[] = {
+ { "debug", 'd', G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, increment_debug_level,
+ "Enable (or increase) test-specific debugging", NULL },
+ { "http-debug", 'h', G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, increment_http_debug_level,
+ "Enable (or increase) HTTP-level debugging", NULL },
+ { NULL }
+};
+
+static void
+quit (int sig)
+{
+#ifdef HAVE_APACHE
+ if (apache_running)
+ apache_cleanup ();
+#endif
+
+ exit (1);
+}
+
+static void
+test_log_handler (const char *log_domain, GLogLevelFlags log_level,
+ const char *message, gpointer user_data)
+{
+ if (log_level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL)) {
+ if (expect_warning) {
+ expect_warning = FALSE;
+ debug_printf (2, "Got expected warning: %s\n", message);
+ return;
+ } else
+ errors++;
+ }
+ g_log_default_handler (log_domain, log_level, message, user_data);
+}
+
+void
+test_init (int argc, char **argv, GOptionEntry *entries)
+{
+ GOptionContext *opts;
+ char *name;
+ GError *error = NULL;
+ GTlsBackend *tls_backend;
+
+ g_thread_init (NULL);
+ g_type_init ();
+
+ name = strrchr (argv[0], '/');
+ if (!name++)
+ name = argv[0];
+ if (!strncmp (name, "lt-", 3))
+ name += 3;
+ g_set_prgname (name);
+
+ opts = g_option_context_new (NULL);
+ g_option_context_add_main_entries (opts, debug_entry, NULL);
+ if (entries)
+ g_option_context_add_main_entries (opts, entries, NULL);
+
+ if (!g_option_context_parse (opts, &argc, &argv, &error)) {
+ fprintf (stderr, "Could not parse arguments: %s\n",
+ error->message);
+ fprintf (stderr, "%s",
+ g_option_context_get_help (opts, TRUE, NULL));
+ exit (1);
+ }
+ g_option_context_free (opts);
+
+ /* Exit cleanly on ^C in case we're valgrinding. */
+ signal (SIGINT, quit);
+
+ g_log_set_default_handler (test_log_handler, NULL);
+
+ tls_backend = g_tls_backend_get_default ();
+ tls_available = g_tls_backend_supports_tls (tls_backend);
+}
+
+void
+test_cleanup (void)
+{
+#ifdef HAVE_APACHE
+ if (apache_running)
+ apache_cleanup ();
+#endif
+
+ if (logger)
+ g_object_unref (logger);
+
+ g_main_context_unref (g_main_context_default ());
+
+ debug_printf (1, "\n");
+ if (errors) {
+ printf ("%s: %d error(s).%s\n",
+ g_get_prgname (), errors,
+ debug_level == 0 ? " Run with '-d' for details" : "");
+ } else
+ printf ("%s: OK\n", g_get_prgname ());
+}
+
+void
+debug_printf (int level, const char *format, ...)
+{
+ va_list args;
+
+ if (debug_level < level)
+ return;
+
+ va_start (args, format);
+ vprintf (format, args);
+ va_end (args);
+}
+
+#ifdef HAVE_APACHE
+
+static gboolean
+apache_cmd (const char *cmd)
+{
+ const char *argv[8];
+ char *cwd, *conf;
+ int status;
+ gboolean ok;
+
+ cwd = g_get_current_dir ();
+ conf = g_build_filename (cwd, "httpd.conf", NULL);
+
+ argv[0] = APACHE_HTTPD;
+ argv[1] = "-d";
+ argv[2] = cwd;
+ argv[3] = "-f";
+ argv[4] = conf;
+ argv[5] = "-k";
+ argv[6] = cmd;
+ argv[7] = NULL;
+
+ ok = g_spawn_sync (cwd, (char **)argv, NULL, 0, NULL, NULL,
+ NULL, NULL, &status, NULL);
+ if (ok)
+ ok = (status == 0);
+
+ g_free (cwd);
+ g_free (conf);
+
+ return ok;
+}
+
+void
+apache_init (void)
+{
+ if (!apache_cmd ("start")) {
+ fprintf (stderr, "Could not start apache\n");
+ exit (1);
+ }
+ apache_running = TRUE;
+}
+
+void
+apache_cleanup (void)
+{
+ pid_t pid;
+ char *contents;
+
+ if (g_file_get_contents ("httpd.pid", &contents, NULL, NULL)) {
+ pid = strtoul (contents, NULL, 10);
+ g_free (contents);
+ } else
+ pid = 0;
+
+ if (!apache_cmd ("graceful-stop"))
+ return;
+ apache_running = FALSE;
+
+ if (pid) {
+ while (kill (pid, 0) == 0)
+ g_usleep (100);
+ }
+}
+
+#endif /* HAVE_APACHE */
+
+SoupSession *
+soup_test_session_new (GType type, ...)
+{
+ va_list args;
+ const char *propname;
+ SoupSession *session;
+
+ va_start (args, type);
+ propname = va_arg (args, const char *);
+ session = (SoupSession *)g_object_new_valist (type, propname, args);
+ va_end (args);
+
+ g_object_set (G_OBJECT (session),
+ SOUP_SESSION_SSL_CA_FILE, SRCDIR "/test-cert.pem",
+ SOUP_SESSION_SSL_STRICT, FALSE,
+ NULL);
+
+ if (http_debug_level && !logger) {
+ SoupLoggerLogLevel level = MIN ((SoupLoggerLogLevel)http_debug_level, SOUP_LOGGER_LOG_BODY);
+
+ logger = soup_logger_new (level, -1);
+ }
+
+ if (logger)
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
+
+ return session;
+}
+
+void
+soup_test_session_abort_unref (SoupSession *session)
+{
+ g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+
+ soup_session_abort (session);
+ g_object_unref (session);
+
+ if (session) {
+ errors++;
+ debug_printf (1, "leaked SoupSession!\n");
+ g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+ }
+}
+
+static gpointer run_server_thread (gpointer user_data);
+
+static SoupServer *
+test_server_new (gboolean in_own_thread, gboolean ssl)
+{
+ SoupServer *server;
+ GMainContext *async_context;
+ const char *ssl_cert_file, *ssl_key_file;
+ SoupAddress *addr;
+
+ async_context = in_own_thread ? g_main_context_new () : NULL;
+
+ if (ssl) {
+ ssl_cert_file = SRCDIR "/test-cert.pem";
+ ssl_key_file = SRCDIR "/test-key.pem";
+ } else
+ ssl_cert_file = ssl_key_file = NULL;
+
+ addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
+ soup_address_resolve_sync (addr, NULL);
+
+ server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
+ SOUP_SERVER_ASYNC_CONTEXT, async_context,
+ SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
+ SOUP_SERVER_SSL_KEY_FILE, ssl_key_file,
+ NULL);
+ g_object_unref (addr);
+ if (async_context)
+ g_main_context_unref (async_context);
+
+ if (!server) {
+ fprintf (stderr, "Unable to create server\n");
+ exit (1);
+ }
+
+ if (in_own_thread) {
+ GThread *thread;
+
+ thread = g_thread_create (run_server_thread, server,
+ TRUE, NULL);
+ g_object_set_data (G_OBJECT (server), "thread", thread);
+ } else
+ soup_server_run_async (server);
+
+ return server;
+}
+
+SoupServer *
+soup_test_server_new (gboolean in_own_thread)
+{
+ return test_server_new (in_own_thread, FALSE);
+}
+
+SoupServer *
+soup_test_server_new_ssl (gboolean in_own_thread)
+{
+ return test_server_new (in_own_thread, TRUE);
+}
+
+static gpointer
+run_server_thread (gpointer user_data)
+{
+ SoupServer *server = user_data;
+
+ soup_server_run (server);
+ return NULL;
+}
+
+static gboolean
+idle_quit_server (gpointer server)
+{
+ soup_server_quit (server);
+ return FALSE;
+}
+
+void
+soup_test_server_quit_unref (SoupServer *server)
+{
+ GThread *thread;
+
+ g_object_add_weak_pointer (G_OBJECT (server),
+ (gpointer *)&server);
+
+ thread = g_object_get_data (G_OBJECT (server), "thread");
+ if (thread) {
+ soup_add_completion (soup_server_get_async_context (server),
+ idle_quit_server, server);
+ g_thread_join (thread);
+ } else
+ soup_server_quit (server);
+ g_object_unref (server);
+
+ if (server) {
+ errors++;
+ debug_printf (1, "leaked SoupServer!\n");
+ g_object_remove_weak_pointer (G_OBJECT (server),
+ (gpointer *)&server);
+ }
+}
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "libsoup/soup-types.h"
+
+void test_init (int argc, char **argv, GOptionEntry *entries);
+void test_cleanup (void);
+
+extern int debug_level, errors;
+extern gboolean expect_warning, tls_available;
+void debug_printf (int level, const char *format, ...) G_GNUC_PRINTF (2, 3);
+
+#ifdef HAVE_APACHE
+void apache_init (void);
+void apache_cleanup (void);
+#endif
+
+SoupSession *soup_test_session_new (GType type, ...);
+void soup_test_session_abort_unref (SoupSession *session);
+
+SoupServer *soup_test_server_new (gboolean in_own_thread);
+SoupServer *soup_test_server_new_ssl (gboolean in_own_thread);
+void soup_test_server_quit_unref (SoupServer *server);
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libsoup/soup.h"
+
+#include "test-utils.h"
+
+static void
+do_message_to_session (SoupSession *session, const char *uri,
+ const char *comment, guint expected_status)
+{
+ SoupMessage *msg;
+
+ debug_printf (1, " %s\n", comment);
+ msg = soup_message_new ("GET", uri);
+ soup_session_send_message (session, msg);
+
+ if (msg->status_code != expected_status) {
+ debug_printf (1, " FAILED: %d %s (expected %d %s)\n",
+ msg->status_code, msg->reason_phrase,
+ expected_status,
+ soup_status_get_phrase (expected_status));
+ errors++;
+ }
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) &&
+ !soup_message_is_keepalive (msg)) {
+ debug_printf (1, " ERROR: message is not keepalive!");
+ errors++;
+ }
+
+ g_object_unref (msg);
+}
+
+static void
+request_started_cb (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket, gpointer user_data)
+{
+ SoupSocket **ret = user_data;
+
+ *ret = socket;
+}
+
+static void
+do_tests_for_session (SoupSession *timeout_session,
+ SoupSession *idle_session,
+ SoupSession *plain_session,
+ char *fast_uri, char *slow_uri)
+{
+ SoupSocket *ret, *idle_first, *idle_second;
+ SoupSocket *plain_first, *plain_second;
+
+ if (idle_session) {
+ g_signal_connect (idle_session, "request-started",
+ G_CALLBACK (request_started_cb), &ret);
+ do_message_to_session (idle_session, fast_uri, "fast to idle", SOUP_STATUS_OK);
+ idle_first = ret;
+ }
+
+ if (plain_session) {
+ g_signal_connect (plain_session, "request-started",
+ G_CALLBACK (request_started_cb), &ret);
+ do_message_to_session (plain_session, fast_uri, "fast to plain", SOUP_STATUS_OK);
+ plain_first = ret;
+ }
+
+ do_message_to_session (timeout_session, fast_uri, "fast to timeout", SOUP_STATUS_OK);
+ do_message_to_session (timeout_session, slow_uri, "slow to timeout", SOUP_STATUS_IO_ERROR);
+
+ if (idle_session) {
+ do_message_to_session (idle_session, fast_uri, "fast to idle", SOUP_STATUS_OK);
+ idle_second = ret;
+ g_signal_handlers_disconnect_by_func (idle_session,
+ (gpointer)request_started_cb,
+ &ret);
+
+ if (idle_first == idle_second) {
+ debug_printf (1, " ERROR: idle_session did not close first connection\n");
+ errors++;
+ }
+ }
+
+ if (plain_session) {
+ do_message_to_session (plain_session, fast_uri, "fast to plain", SOUP_STATUS_OK);
+ plain_second = ret;
+ g_signal_handlers_disconnect_by_func (plain_session,
+ (gpointer)request_started_cb,
+ &ret);
+
+ if (plain_first != plain_second) {
+ debug_printf (1, " ERROR: plain_session closed connection\n");
+ errors++;
+ }
+ }
+}
+
+static void
+do_timeout_tests (char *fast_uri, char *slow_uri)
+{
+ SoupSession *timeout_session, *idle_session, *plain_session;
+
+ debug_printf (1, " async\n");
+ timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_TIMEOUT, 1,
+ NULL);
+ idle_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_IDLE_TIMEOUT, 1,
+ NULL);
+ /* The "plain" session also has an idle timeout, but it's longer
+ * than the test takes, so for our purposes it should behave like
+ * it has no timeout.
+ */
+ plain_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_IDLE_TIMEOUT, 2,
+ NULL);
+ do_tests_for_session (timeout_session, idle_session, plain_session,
+ fast_uri, slow_uri);
+ soup_test_session_abort_unref (timeout_session);
+ soup_test_session_abort_unref (idle_session);
+ soup_test_session_abort_unref (plain_session);
+
+ debug_printf (1, " sync\n");
+ timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
+ SOUP_SESSION_TIMEOUT, 1,
+ NULL);
+ /* SOUP_SESSION_TIMEOUT doesn't work with sync sessions */
+ plain_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
+ NULL);
+ do_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri);
+ soup_test_session_abort_unref (timeout_session);
+}
+
+static gboolean
+timeout_finish_message (gpointer msg)
+{
+ SoupServer *server = g_object_get_data (G_OBJECT (msg), "server");
+
+ soup_server_unpause_message (server, msg);
+ return FALSE;
+}
+
+static void
+server_handler (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
+{
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "ok\r\n", 4);
+
+ if (!strcmp (path, "/slow")) {
+ soup_server_pause_message (server, msg);
+ g_object_set_data (G_OBJECT (msg), "server", server);
+ soup_add_timeout (soup_server_get_async_context (server),
+ 1100, timeout_finish_message, msg);
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ char *fast_uri, *slow_uri;
+
+ test_init (argc, argv, NULL);
+
+ debug_printf (1, "http\n");
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
+ fast_uri = g_strdup_printf ("http://127.0.0.1:%u/",
+ soup_server_get_port (server));
+ slow_uri = g_strdup_printf ("http://127.0.0.1:%u/slow",
+ soup_server_get_port (server));
+ do_timeout_tests (fast_uri, slow_uri);
+ g_free (fast_uri);
+ g_free (slow_uri);
+ soup_test_server_quit_unref (server);
+
+ if (tls_available) {
+ debug_printf (1, "\nhttps\n");
+ server = soup_test_server_new_ssl (TRUE);
+ soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
+ fast_uri = g_strdup_printf ("https://127.0.0.1:%u/",
+ soup_server_get_port (server));
+ slow_uri = g_strdup_printf ("https://127.0.0.1:%u/slow",
+ soup_server_get_port (server));
+ do_timeout_tests (fast_uri, slow_uri);
+ g_free (fast_uri);
+ g_free (slow_uri);
+ soup_test_server_quit_unref (server);
+ }
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+#include <config.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libsoup/soup-uri.h"
+
+#include "test-utils.h"
+
+static struct {
+ const char *uri_string, *result;
+} abs_tests[] = {
+ { "foo:", "foo:" },
+ { "file:/dev/null", "file:/dev/null" },
+ { "file:///dev/null", "file:///dev/null" },
+ { "ftp://user@host/path", "ftp://user@host/path" },
+ { "ftp://user@host:9999/path", "ftp://user@host:9999/path" },
+ { "ftp://user:password@host/path", "ftp://user@host/path" },
+ { "ftp://user:password@host:9999/path", "ftp://user@host:9999/path" },
+ { "ftp://user:password@host", "ftp://user@host" },
+ { "http://us%65r@host", "http://user@host/" },
+ { "http://us%40r@host", "http://us%40r@host/" },
+ { "http://us%3ar@host", "http://us%3Ar@host/" },
+ { "http://us%2fr@host", "http://us%2Fr@host/" },
+ { "http://us%3fr@host", "http://us%3Fr@host/" },
+ { "http://host?query", "http://host/?query" },
+ { "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue¶m=value",
+ "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue¶m=value" },
+ { "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F",
+ "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F"},
+ { "http://space/%20",
+ "http://space/%20" },
+ { "http://delims/%3C%3E%23%25%22",
+ "http://delims/%3C%3E%23%25%22" },
+ { "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60",
+ "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60" },
+
+ /* From RFC 2732 */
+ { "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html",
+ "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/index.html" },
+ { "http://[1080:0:0:0:8:800:200C:417A]/index.html",
+ "http://[1080:0:0:0:8:800:200C:417A]/index.html" },
+ { "http://[3ffe:2a00:100:7031::1]",
+ "http://[3ffe:2a00:100:7031::1]/" },
+ { "http://[1080::8:800:200C:417A]/foo",
+ "http://[1080::8:800:200C:417A]/foo" },
+ { "http://[::192.9.5.5]/ipng",
+ "http://[::192.9.5.5]/ipng" },
+ { "http://[::FFFF:129.144.52.38]:80/index.html",
+ "http://[::FFFF:129.144.52.38]/index.html" },
+ { "http://[2010:836B:4179::836B:4179]",
+ "http://[2010:836B:4179::836B:4179]/" },
+
+ /* Try to recover certain kinds of invalid URIs */
+ { "http://host/path with spaces",
+ "http://host/path%20with%20spaces" },
+ { " http://host/path", "http://host/path" },
+ { "http://host/path ", "http://host/path" },
+ { "http://host ", "http://host/" },
+ { "http://host:999 ", "http://host:999/" },
+ { "http://host/pa\nth", "http://host/path" },
+ { "http:\r\n//host/path", "http://host/path" },
+ { "http://\thost/path", "http://host/path" },
+
+ /* Bug 594405; 0-length is different from not-present */
+ { "http://host/path?", "http://host/path?" },
+ { "http://host/path#", "http://host/path#" },
+
+ /* Bug 590524; ignore badly-%-encoding */
+ { "http://host/path%", "http://host/path%" },
+ { "http://h%ost/path", "http://h%25ost/path" },
+ { "http://host/path%%", "http://host/path%%" },
+ { "http://host/path%%%", "http://host/path%%%" },
+ { "http://host/path%/x/", "http://host/path%/x/" },
+ { "http://host/path%0x/", "http://host/path%0x/" },
+ { "http://host/path%ax", "http://host/path%ax" }
+};
+static int num_abs_tests = G_N_ELEMENTS(abs_tests);
+
+/* From RFC 3986. */
+static const char *base = "http://a/b/c/d;p?q";
+static struct {
+ const char *uri_string, *result;
+} rel_tests[] = {
+ { "g:h", "g:h" },
+ { "g", "http://a/b/c/g" },
+ { "./g", "http://a/b/c/g" },
+ { "g/", "http://a/b/c/g/" },
+ { "/g", "http://a/g" },
+ { "//g", "http://g/" },
+ { "?y", "http://a/b/c/d;p?y" },
+ { "g?y", "http://a/b/c/g?y" },
+ { "#s", "http://a/b/c/d;p?q#s" },
+ { "g#s", "http://a/b/c/g#s" },
+ { "g?y#s", "http://a/b/c/g?y#s" },
+ { ";x", "http://a/b/c/;x" },
+ { "g;x", "http://a/b/c/g;x" },
+ { "g;x?y#s", "http://a/b/c/g;x?y#s" },
+ { ".", "http://a/b/c/" },
+ { "./", "http://a/b/c/" },
+ { "..", "http://a/b/" },
+ { "../", "http://a/b/" },
+ { "../g", "http://a/b/g" },
+ { "../..", "http://a/" },
+ { "../../", "http://a/" },
+ { "../../g", "http://a/g" },
+ { "", "http://a/b/c/d;p?q" },
+ { "../../../g", "http://a/g" },
+ { "../../../../g", "http://a/g" },
+ { "/./g", "http://a/g" },
+ { "/../g", "http://a/g" },
+ { "g.", "http://a/b/c/g." },
+ { ".g", "http://a/b/c/.g" },
+ { "g..", "http://a/b/c/g.." },
+ { "..g", "http://a/b/c/..g" },
+ { "./../g", "http://a/b/g" },
+ { "./g/.", "http://a/b/c/g/" },
+ { "g/./h", "http://a/b/c/g/h" },
+ { "g/../h", "http://a/b/c/h" },
+ { "g;x=1/./y", "http://a/b/c/g;x=1/y" },
+ { "g;x=1/../y", "http://a/b/c/y" },
+ { "g?y/./x", "http://a/b/c/g?y/./x" },
+ { "g?y/../x", "http://a/b/c/g?y/../x" },
+ { "g#s/./x", "http://a/b/c/g#s/./x" },
+ { "g#s/../x", "http://a/b/c/g#s/../x" },
+
+ /* RFC 3986 notes that some old parsers will parse this as
+ * a relative URL ("http://a/b/c/g"), but it should be
+ * interpreted as absolute. libsoup should parse it
+ * correctly as being absolute, but then reject it since it's
+ * an http URL with no host.
+ */
+ { "http:g", NULL }
+};
+static int num_rel_tests = G_N_ELEMENTS(rel_tests);
+
+static struct {
+ const char *one, *two;
+} eq_tests[] = {
+ { "example://a/b/c/%7Bfoo%7D", "eXAMPLE://a/./b/../b/%63/%7Bfoo%7D" },
+ { "http://example.com", "http://example.com/" },
+ /* From RFC 2616 */
+ { "http://abc.com:80/~smith/home.html", "http://abc.com:80/~smith/home.html" },
+ { "http://abc.com:80/~smith/home.html", "http://ABC.com/%7Esmith/home.html" },
+ { "http://abc.com:80/~smith/home.html", "http://ABC.com:/%7esmith/home.html" },
+};
+static int num_eq_tests = G_N_ELEMENTS(eq_tests);
+
+static gboolean
+do_uri (SoupURI *base_uri, const char *base_str,
+ const char *in_uri, const char *out_uri)
+{
+ SoupURI *uri;
+ char *uri_string;
+
+ if (base_uri) {
+ debug_printf (1, "<%s> + <%s> = <%s>? ", base_str, in_uri,
+ out_uri ? out_uri : "ERR");
+ uri = soup_uri_new_with_base (base_uri, in_uri);
+ } else {
+ debug_printf (1, "<%s> => <%s>? ", in_uri,
+ out_uri ? out_uri : "ERR");
+ uri = soup_uri_new (in_uri);
+ }
+
+ if (!uri) {
+ if (out_uri) {
+ debug_printf (1, "ERR\n Could not parse %s\n", in_uri);
+ return FALSE;
+ } else {
+ debug_printf (1, "OK\n");
+ return TRUE;
+ }
+ }
+
+ uri_string = soup_uri_to_string (uri, FALSE);
+ soup_uri_free (uri);
+
+ if (!out_uri) {
+ debug_printf (1, "ERR\n Got %s\n", uri_string);
+ return FALSE;
+ }
+
+ if (strcmp (uri_string, out_uri) != 0) {
+ debug_printf (1, "NO\n Unparses to <%s>\n", uri_string);
+ g_free (uri_string);
+ return FALSE;
+ }
+ g_free (uri_string);
+
+ debug_printf (1, "OK\n");
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupURI *base_uri, *uri1, *uri2;
+ char *uri_string;
+ int i;
+
+ test_init (argc, argv, NULL);
+
+ debug_printf (1, "Absolute URI parsing\n");
+ for (i = 0; i < num_abs_tests; i++) {
+ if (!do_uri (NULL, NULL, abs_tests[i].uri_string,
+ abs_tests[i].result))
+ errors++;
+ }
+
+ debug_printf (1, "\nRelative URI parsing\n");
+ base_uri = soup_uri_new (base);
+ if (!base_uri) {
+ fprintf (stderr, "Could not parse %s!\n", base);
+ exit (1);
+ }
+
+ uri_string = soup_uri_to_string (base_uri, FALSE);
+ if (strcmp (uri_string, base) != 0) {
+ fprintf (stderr, "URI <%s> unparses to <%s>\n",
+ base, uri_string);
+ errors++;
+ }
+ g_free (uri_string);
+
+ for (i = 0; i < num_rel_tests; i++) {
+ if (!do_uri (base_uri, base, rel_tests[i].uri_string,
+ rel_tests[i].result))
+ errors++;
+ }
+ soup_uri_free (base_uri);
+
+ debug_printf (1, "\nURI equality testing\n");
+ for (i = 0; i < num_eq_tests; i++) {
+ uri1 = soup_uri_new (eq_tests[i].one);
+ uri2 = soup_uri_new (eq_tests[i].two);
+ debug_printf (1, "<%s> == <%s>? ", eq_tests[i].one, eq_tests[i].two);
+ if (soup_uri_equal (uri1, uri2))
+ debug_printf (1, "OK\n");
+ else {
+ debug_printf (1, "NO\n");
+ debug_printf (1, "%s : %s : %s\n%s : %s : %s\n",
+ uri1->scheme, uri1->host, uri1->path,
+ uri2->scheme, uri2->host, uri2->path);
+ errors++;
+ }
+ soup_uri_free (uri1);
+ soup_uri_free (uri2);
+ }
+
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+GMainLoop *loop;
+
+static void
+type_error (SoupMessage *msg, GType expected, GValueArray *params, int bad_value)
+{
+ soup_xmlrpc_set_fault (msg,
+ SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS,
+ "Bad parameter #%d: expected %s, got %s",
+ bad_value + 1, g_type_name (expected),
+ g_type_name (G_VALUE_TYPE (¶ms->values[bad_value])));
+}
+
+static void
+args_error (SoupMessage *msg, GValueArray *params, int expected)
+{
+ soup_xmlrpc_set_fault (msg,
+ SOUP_XMLRPC_FAULT_SERVER_ERROR_INVALID_METHOD_PARAMETERS,
+ "Wrong number of parameters: expected %d, got %d",
+ expected, params->n_values);
+}
+
+static void
+do_sum (SoupMessage *msg, GValueArray *params)
+{
+ int sum = 0, i, val;
+ GValueArray *nums;
+
+ if (params->n_values != 1) {
+ args_error (msg, params, 1);
+ return;
+ }
+ if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &nums)) {
+ type_error (msg, G_TYPE_VALUE_ARRAY, params, 0);
+ return;
+ }
+
+ for (i = 0; i < nums->n_values; i++) {
+ if (!soup_value_array_get_nth (nums, i, G_TYPE_INT, &val)) {
+ type_error (msg, G_TYPE_INT, nums, i);
+ return;
+ }
+ sum += val;
+ }
+
+ soup_xmlrpc_set_response (msg, G_TYPE_INT, sum);
+
+}
+
+static void
+do_countBools (SoupMessage *msg, GValueArray *params)
+{
+ int i, trues = 0, falses = 0;
+ GValueArray *bools;
+ GHashTable *ret = soup_value_hash_new ();
+ gboolean val;
+
+ if (params->n_values != 1) {
+ args_error (msg, params, 1);
+ return;
+ }
+ if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &bools)) {
+ type_error (msg, G_TYPE_VALUE_ARRAY, params, 0);
+ return;
+ }
+
+ for (i = 0; i < bools->n_values; i++) {
+ if (!soup_value_array_get_nth (bools, i, G_TYPE_BOOLEAN, &val)) {
+ type_error (msg, G_TYPE_BOOLEAN, params, i);
+ return;
+ }
+ if (val)
+ trues++;
+ else
+ falses++;
+ }
+
+ soup_value_hash_insert (ret, "true", G_TYPE_INT, trues);
+ soup_value_hash_insert (ret, "false", G_TYPE_INT, falses);
+ soup_xmlrpc_set_response (msg, G_TYPE_HASH_TABLE, ret);
+ g_hash_table_destroy (ret);
+
+}
+
+static void
+do_md5sum (SoupMessage *msg, GValueArray *params)
+{
+ GChecksum *checksum;
+ GByteArray *data, *digest;
+ gsize digest_len = 16;
+
+ if (params->n_values != 1) {
+ args_error (msg, params, 1);
+ return;
+ }
+
+ if (!soup_value_array_get_nth (params, 0, SOUP_TYPE_BYTE_ARRAY, &data)) {
+ type_error (msg, SOUP_TYPE_BYTE_ARRAY, params, 0);
+ return;
+ }
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, data->data, data->len);
+ digest = g_byte_array_new ();
+ g_byte_array_set_size (digest, digest_len);
+ g_checksum_get_digest (checksum, digest->data, &digest_len);
+ g_checksum_free (checksum);
+
+ soup_xmlrpc_set_response (msg, SOUP_TYPE_BYTE_ARRAY, digest);
+ g_byte_array_free (digest, TRUE);
+}
+
+
+static void
+do_dateChange (SoupMessage *msg, GValueArray *params)
+{
+ GHashTable *arg;
+ SoupDate *date;
+ int val;
+
+ if (params->n_values != 2) {
+ args_error (msg, params, 2);
+ return;
+ }
+
+ if (!soup_value_array_get_nth (params, 0, SOUP_TYPE_DATE, &date)) {
+ type_error (msg, SOUP_TYPE_DATE, params, 0);
+ return;
+ }
+ if (!soup_value_array_get_nth (params, 1, G_TYPE_HASH_TABLE, &arg)) {
+ type_error (msg, G_TYPE_HASH_TABLE, params, 1);
+ return;
+ }
+
+ if (soup_value_hash_lookup (arg, "tm_year", G_TYPE_INT, &val))
+ date->year = val + 1900;
+ if (soup_value_hash_lookup (arg, "tm_mon", G_TYPE_INT, &val))
+ date->month = val + 1;
+ if (soup_value_hash_lookup (arg, "tm_mday", G_TYPE_INT, &val))
+ date->day = val;
+ if (soup_value_hash_lookup (arg, "tm_hour", G_TYPE_INT, &val))
+ date->hour = val;
+ if (soup_value_hash_lookup (arg, "tm_min", G_TYPE_INT, &val))
+ date->minute = val;
+ if (soup_value_hash_lookup (arg, "tm_sec", G_TYPE_INT, &val))
+ date->second = val;
+
+ soup_xmlrpc_set_response (msg, SOUP_TYPE_DATE, date);
+}
+
+static void
+do_echo (SoupMessage *msg, GValueArray *params)
+{
+ int i;
+ const char *val;
+ GValueArray *in, *out;
+
+ if (!soup_value_array_get_nth (params, 0, G_TYPE_VALUE_ARRAY, &in)) {
+ type_error (msg, G_TYPE_VALUE_ARRAY, params, 0);
+ return;
+ }
+
+ out = g_value_array_new (in->n_values);
+ for (i = 0; i < in->n_values; i++) {
+ if (!soup_value_array_get_nth (in, i, G_TYPE_STRING, &val)) {
+ type_error (msg, G_TYPE_STRING, in, i);
+ return;
+ }
+ soup_value_array_append (out, G_TYPE_STRING, val);
+ }
+
+ soup_xmlrpc_set_response (msg, G_TYPE_VALUE_ARRAY, out);
+ g_value_array_free (out);
+
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ char *method_name;
+ GValueArray *params;
+
+ if (msg->method != SOUP_METHOD_POST) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+
+ if (!soup_xmlrpc_parse_method_call (msg->request_body->data,
+ msg->request_body->length,
+ &method_name, ¶ms)) {
+ soup_xmlrpc_set_fault (msg, SOUP_XMLRPC_FAULT_PARSE_ERROR_NOT_WELL_FORMED,
+ "Could not parse method call");
+ return;
+ }
+
+ if (!strcmp (method_name, "sum"))
+ do_sum (msg, params);
+ else if (!strcmp (method_name, "countBools"))
+ do_countBools (msg, params);
+ else if (!strcmp (method_name, "md5sum"))
+ do_md5sum (msg, params);
+ else if (!strcmp (method_name, "dateChange"))
+ do_dateChange (msg, params);
+ else if (!strcmp (method_name, "echo"))
+ do_echo (msg, params);
+ else {
+ soup_xmlrpc_set_fault (msg, SOUP_XMLRPC_FAULT_SERVER_ERROR_REQUESTED_METHOD_NOT_FOUND,
+ "Unknown method %s", method_name);
+ }
+
+ g_free (method_name);
+ g_value_array_free (params);
+}
+
+static void
+xmlrpc_test_exited (GPid pid, int status, gpointer data)
+{
+ errors = WIFEXITED (status) ? WEXITSTATUS (status) : 1;
+ g_main_loop_quit (loop);
+}
+
+static gboolean
+xmlrpc_test_print (GIOChannel *io, GIOCondition cond, gpointer data)
+{
+ char *line;
+ gsize len;
+ GIOStatus status;
+
+ if (!(cond & G_IO_IN))
+ return FALSE;
+
+ status = g_io_channel_read_line (io, &line, &len, NULL, NULL);
+ if (status == G_IO_STATUS_NORMAL) {
+ /* Don't print the exit status, just the debug stuff */
+ if (strncmp (line, "xmlrpc-test:", strlen ("xmlrpc-test:")) != 0)
+ printf ("%s", line);
+ g_free (line);
+ return TRUE;
+ } else if (status == G_IO_STATUS_AGAIN)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+do_xmlrpc_tests (SoupURI *uri)
+{
+ char *argv[8];
+ int arg, out;
+ gboolean ok;
+ GPid pid;
+ GError *error = NULL;
+ GIOChannel *child_out;
+
+ argv[0] = "./xmlrpc-test";
+ argv[1] = "-s";
+ argv[2] = "-u";
+ argv[3] = soup_uri_to_string (uri, FALSE);
+
+ for (arg = 0; arg < debug_level && arg < 3; arg++)
+ argv[arg + 4] = "-d";
+ argv[arg + 4] = NULL;
+
+ ok = g_spawn_async_with_pipes (NULL, argv, NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, &pid,
+ NULL, &out, NULL,
+ &error);
+ g_free (argv[3]);
+
+ if (!ok) {
+ printf ("Could not run xmlrpc-test: %s\n", error->message);
+ errors++;
+ return;
+ }
+
+ g_child_watch_add (pid, xmlrpc_test_exited, NULL);
+ child_out = g_io_channel_unix_new (out);
+ g_io_add_watch (child_out, G_IO_IN | G_IO_ERR | G_IO_HUP,
+ xmlrpc_test_print, NULL);
+ g_io_channel_unref (child_out);
+}
+
+gboolean run_tests = TRUE;
+
+static GOptionEntry no_test_entry[] = {
+ { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &run_tests,
+ "Don't run tests, just run the test server", NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ SoupURI *uri;
+
+ test_init (argc, argv, no_test_entry);
+
+ server = soup_test_server_new (FALSE);
+ soup_server_add_handler (server, "/xmlrpc-server.php",
+ server_callback, NULL, NULL);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ if (run_tests) {
+ uri = soup_uri_new ("http://127.0.0.1/xmlrpc-server.php");
+ soup_uri_set_port (uri, soup_server_get_port (server));
+ do_xmlrpc_tests (uri);
+ soup_uri_free (uri);
+ } else
+ printf ("Listening on port %d\n", soup_server_get_port (server));
+
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ soup_test_server_quit_unref (server);
+ if (run_tests)
+ test_cleanup ();
+ return errors != 0;
+}
--- /dev/null
+<?php
+
+function paramfault ()
+{
+ # xmlrpc-epi-php translates this into a real <fault>
+ $fault["faultCode"] = -32602;
+ $fault["faultString"] = "bad parameter";
+ return $fault;
+}
+
+# We only check the params in sum(), because that's the one that
+# xmlrpc-test tests will fail if given bad args
+
+function sum ($method_name, $params, $app_data)
+{
+ if (xmlrpc_get_type ($params[0]) != "array")
+ return paramfault();
+
+ $sum = 0;
+ foreach ($params[0] as $val) {
+ if (xmlrpc_get_type ($val) != "int")
+ return paramfault();
+
+ $sum = $sum + $val;
+ }
+ return $sum;
+}
+
+function countBools ($method_name, $params, $app_data)
+{
+ $counts["true"] = $counts["false"] = 0;
+ foreach ($params[0] as $val) {
+ if ($val)
+ $counts["true"] = $counts["true"] + 1;
+ else
+ $counts["false"] = $counts["false"] + 1;
+ }
+ return $counts;
+}
+
+function md5sum ($method_name, $params, $app_data)
+{
+ $val = md5 ($params[0]->scalar, true);
+ xmlrpc_set_type ($val, "base64");
+ return $val;
+}
+
+function dateChange ($method_name, $params, $app_data)
+{
+ $date_str = $params[0]->scalar;
+ $date = strptime ($date_str, "%Y%m%dT%H:%M:%S");
+
+ foreach ($params[1] as $name => $val) {
+ if ($name == "date")
+ continue;
+ $date[$name] = $val;
+ }
+
+ $ret = sprintf ("%04d%02d%02dT%02d:%02d:%02d",
+ $date["tm_year"] + 1900, $date["tm_mon"] + 1,
+ $date["tm_mday"], $date["tm_hour"],
+ $date["tm_min"], $date["tm_sec"]);
+ xmlrpc_set_type ($ret, "datetime");
+ return $ret;
+}
+
+function echo_ ($method_name, $params, $app_data)
+{
+ return $params[0];
+}
+
+# Work around xmlrpc-epi-php lossage; otherwise the datetime values
+# we return will sometimes get a DST adjustment we don't want.
+putenv ("TZ=");
+
+$xmlrpc_server = xmlrpc_server_create ();
+xmlrpc_server_register_method($xmlrpc_server, "sum", "sum");
+xmlrpc_server_register_method($xmlrpc_server, "countBools", "countBools");
+xmlrpc_server_register_method($xmlrpc_server, "md5sum", "md5sum");
+xmlrpc_server_register_method($xmlrpc_server, "dateChange", "dateChange");
+xmlrpc_server_register_method($xmlrpc_server, "echo", "echo_");
+
+$response = xmlrpc_server_call_method ($xmlrpc_server,
+ implode("\r\n", file('php://input')),
+ 0, array ("output_type" => "xml"));
+echo ($response);
+
+xmlrpc_server_destroy ($xmlrpc_server);
+
+?>
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+static SoupSession *session;
+static const char *default_uri = "http://127.0.0.1:47524/xmlrpc-server.php";
+static const char *uri = NULL;
+static gboolean server_test = FALSE;
+
+static const char *const value_type[] = {
+ "BAD",
+ "int",
+ "boolean",
+ "string",
+ "double",
+ "datetime",
+ "base64",
+ "struct",
+ "array"
+};
+
+static gboolean
+do_xmlrpc (const char *method, GValue *retval, ...)
+{
+ SoupMessage *msg;
+ va_list args;
+ GValueArray *params;
+ GError *err = NULL;
+ char *body;
+
+ va_start (args, retval);
+ params = soup_value_array_from_args (args);
+ va_end (args);
+
+ body = soup_xmlrpc_build_method_call (method, params->values,
+ params->n_values);
+ g_value_array_free (params);
+ if (!body)
+ return FALSE;
+
+ msg = soup_message_new ("POST", uri);
+ soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
+ body, strlen (body));
+ soup_session_send_message (session, msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, "ERROR: %d %s\n", msg->status_code,
+ msg->reason_phrase);
+ g_object_unref (msg);
+ return FALSE;
+ }
+
+ if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
+ msg->response_body->length,
+ retval, &err)) {
+ if (err) {
+ debug_printf (1, "FAULT: %d %s\n", err->code, err->message);
+ g_error_free (err);
+ } else
+ debug_printf (1, "ERROR: could not parse response\n");
+ g_object_unref (msg);
+ return FALSE;
+ }
+ g_object_unref (msg);
+
+ return TRUE;
+}
+
+static gboolean
+check_xmlrpc (GValue *value, GType type, ...)
+{
+ va_list args;
+
+ if (!G_VALUE_HOLDS (value, type)) {
+ debug_printf (1, "ERROR: could not parse response\n");
+ g_value_unset (value);
+ return FALSE;
+ }
+
+ va_start (args, type);
+ SOUP_VALUE_GETV (value, type, args);
+ va_end (args);
+ return TRUE;
+}
+
+static gboolean
+test_sum (void)
+{
+ GValueArray *ints;
+ int i, val, sum, result;
+ GValue retval;
+ gboolean ok;
+
+ debug_printf (1, "sum (array of int -> int): ");
+
+ ints = g_value_array_new (10);
+ for (i = sum = 0; i < 10; i++) {
+ val = rand () % 100;
+ debug_printf (2, "%s%d", i == 0 ? "[" : ", ", val);
+ soup_value_array_append (ints, G_TYPE_INT, val);
+ sum += val;
+ }
+ debug_printf (2, "] -> ");
+
+ ok = (do_xmlrpc ("sum", &retval,
+ G_TYPE_VALUE_ARRAY, ints,
+ G_TYPE_INVALID) &&
+ check_xmlrpc (&retval, G_TYPE_INT, &result));
+ g_value_array_free (ints);
+
+ if (!ok)
+ return FALSE;
+
+ debug_printf (2, "%d: ", result);
+ debug_printf (1, "%s\n", result == sum ? "OK!" : "WRONG!");
+ return result == sum;
+}
+
+static gboolean
+test_countBools (void)
+{
+ GValueArray *bools;
+ int i, trues, falses;
+ GValue retval;
+ int ret_trues, ret_falses;
+ gboolean val, ok;
+ GHashTable *result;
+
+ debug_printf (1, "countBools (array of boolean -> struct of ints): ");
+
+ bools = g_value_array_new (10);
+ for (i = trues = falses = 0; i < 10; i++) {
+ val = rand () > (RAND_MAX / 2);
+ debug_printf (2, "%s%c", i == 0 ? "[" : ", ", val ? 'T' : 'F');
+ soup_value_array_append (bools, G_TYPE_BOOLEAN, val);
+ if (val)
+ trues++;
+ else
+ falses++;
+ }
+ debug_printf (2, "] -> ");
+
+ ok = (do_xmlrpc ("countBools", &retval,
+ G_TYPE_VALUE_ARRAY, bools,
+ G_TYPE_INVALID) &&
+ check_xmlrpc (&retval, G_TYPE_HASH_TABLE, &result));
+ g_value_array_free (bools);
+ if (!ok)
+ return FALSE;
+
+ if (!soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)) {
+ debug_printf (1, "NO 'true' value in response\n");
+ return FALSE;
+ }
+ if (!soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)) {
+ debug_printf (1, "NO 'false' value in response\n");
+ return FALSE;
+ }
+ g_hash_table_destroy (result);
+
+ debug_printf (2, "{ true: %d, false: %d } ", ret_trues, ret_falses);
+ ok = (trues == ret_trues) && (falses == ret_falses);
+ debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
+ return ok;
+}
+
+static gboolean
+test_md5sum (void)
+{
+ GByteArray *data, *result;
+ int i;
+ GChecksum *checksum;
+ guchar digest[16];
+ gsize digest_len = sizeof (digest);
+ GValue retval;
+ gboolean ok;
+
+ debug_printf (1, "md5sum (base64 -> base64): ");
+
+ data = g_byte_array_new ();
+ g_byte_array_set_size (data, 256);
+ for (i = 0; i < data->len; i++)
+ data->data[i] = (char)(rand ());
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, data->data, data->len);
+ g_checksum_get_digest (checksum, digest, &digest_len);
+ g_checksum_free (checksum);
+
+ ok = (do_xmlrpc ("md5sum", &retval,
+ SOUP_TYPE_BYTE_ARRAY, data,
+ G_TYPE_INVALID) &&
+ check_xmlrpc (&retval, SOUP_TYPE_BYTE_ARRAY, &result));
+ g_byte_array_free (data, TRUE);
+ if (!ok)
+ return FALSE;
+
+ if (result->len != digest_len) {
+ debug_printf (1, "result has WRONG length (%d)\n", result->len);
+ g_byte_array_free (result, TRUE);
+ return FALSE;
+ }
+
+ ok = (memcmp (digest, result->data, digest_len) == 0);
+ debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
+ g_byte_array_free (result, TRUE);
+ return ok;
+}
+
+static gboolean
+test_dateChange (void)
+{
+ GHashTable *structval;
+ SoupDate *date, *result;
+ char *timestamp;
+ GValue retval;
+ gboolean ok;
+
+ debug_printf (1, "dateChange (date, struct of ints -> time): ");
+
+ date = soup_date_new (1970 + (rand () % 50),
+ 1 + rand () % 12,
+ 1 + rand () % 28,
+ rand () % 24,
+ rand () % 60,
+ rand () % 60);
+ if (debug_level >= 2) {
+ timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
+ debug_printf (2, "date: %s, {", timestamp);
+ g_free (timestamp);
+ }
+
+ structval = soup_value_hash_new ();
+
+ if (rand () % 3) {
+ date->year = 1970 + (rand () % 50);
+ debug_printf (2, "tm_year: %d, ", date->year - 1900);
+ soup_value_hash_insert (structval, "tm_year",
+ G_TYPE_INT, date->year - 1900);
+ }
+ if (rand () % 3) {
+ date->month = 1 + rand () % 12;
+ debug_printf (2, "tm_mon: %d, ", date->month - 1);
+ soup_value_hash_insert (structval, "tm_mon",
+ G_TYPE_INT, date->month - 1);
+ }
+ if (rand () % 3) {
+ date->day = 1 + rand () % 28;
+ debug_printf (2, "tm_mday: %d, ", date->day);
+ soup_value_hash_insert (structval, "tm_mday",
+ G_TYPE_INT, date->day);
+ }
+ if (rand () % 3) {
+ date->hour = rand () % 24;
+ debug_printf (2, "tm_hour: %d, ", date->hour);
+ soup_value_hash_insert (structval, "tm_hour",
+ G_TYPE_INT, date->hour);
+ }
+ if (rand () % 3) {
+ date->minute = rand () % 60;
+ debug_printf (2, "tm_min: %d, ", date->minute);
+ soup_value_hash_insert (structval, "tm_min",
+ G_TYPE_INT, date->minute);
+ }
+ if (rand () % 3) {
+ date->second = rand () % 60;
+ debug_printf (2, "tm_sec: %d, ", date->second);
+ soup_value_hash_insert (structval, "tm_sec",
+ G_TYPE_INT, date->second);
+ }
+
+ debug_printf (2, "} -> ");
+
+ ok = (do_xmlrpc ("dateChange", &retval,
+ SOUP_TYPE_DATE, date,
+ G_TYPE_HASH_TABLE, structval,
+ G_TYPE_INVALID) &&
+ check_xmlrpc (&retval, SOUP_TYPE_DATE, &result));
+ g_hash_table_destroy (structval);
+ if (!ok) {
+ soup_date_free (date);
+ return FALSE;
+ }
+
+ if (debug_level >= 2) {
+ timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC);
+ debug_printf (2, "%s: ", timestamp);
+ g_free (timestamp);
+ }
+
+ ok = ((date->year == result->year) &&
+ (date->month == result->month) &&
+ (date->day == result->day) &&
+ (date->hour == result->hour) &&
+ (date->minute == result->minute) &&
+ (date->second == result->second));
+ soup_date_free (date);
+ soup_date_free (result);
+
+ debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
+ return ok;
+}
+
+static const char *const echo_strings[] = {
+ "This is a test",
+ "& so is this",
+ "and so is <this>",
+ "& so is <this>"
+};
+#define N_ECHO_STRINGS G_N_ELEMENTS (echo_strings)
+
+static const char *const echo_strings_broken[] = {
+ "This is a test",
+ " so is this",
+ "and so is this",
+ "amp; so is lt;thisgt;"
+};
+
+static gboolean
+test_echo (void)
+{
+ GValueArray *originals, *echoes;
+ GValue retval;
+ int i;
+ gboolean php_bug = FALSE;
+
+ debug_printf (1, "echo (array of string -> array of string): ");
+
+ originals = g_value_array_new (N_ECHO_STRINGS);
+ for (i = 0; i < N_ECHO_STRINGS; i++) {
+ soup_value_array_append (originals, G_TYPE_STRING, echo_strings[i]);
+ debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", echo_strings[i]);
+ }
+ debug_printf (2, "] -> ");
+
+ if (!(do_xmlrpc ("echo", &retval,
+ G_TYPE_VALUE_ARRAY, originals,
+ G_TYPE_INVALID) &&
+ check_xmlrpc (&retval, G_TYPE_VALUE_ARRAY, &echoes))) {
+ g_value_array_free (originals);
+ return FALSE;
+ }
+ g_value_array_free (originals);
+
+ if (debug_level >= 2) {
+ for (i = 0; i < echoes->n_values; i++) {
+ debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ",
+ g_value_get_string (&echoes->values[i]));
+ }
+ debug_printf (2, "] -> ");
+ }
+
+ if (echoes->n_values != N_ECHO_STRINGS) {
+ debug_printf (1, " WRONG! Wrong number of return strings");
+ g_value_array_free (echoes);
+ return FALSE;
+ }
+
+ for (i = 0; i < echoes->n_values; i++) {
+ if (strcmp (echo_strings[i], g_value_get_string (&echoes->values[i])) != 0) {
+ if (!server_test && strcmp (echo_strings_broken[i], g_value_get_string (&echoes->values[i])) == 0)
+ php_bug = TRUE;
+ else {
+ debug_printf (1, " WRONG! Mismatch at %d\n", i + 1);
+ g_value_array_free (echoes);
+ return FALSE;
+ }
+ }
+ }
+
+ if (php_bug)
+ debug_printf (1, "WRONG, but it's php's fault\n");
+ else
+ debug_printf (1, "OK!\n");
+ g_value_array_free (echoes);
+ return TRUE;
+}
+
+static gboolean
+do_bad_xmlrpc (const char *body)
+{
+ SoupMessage *msg;
+ GError *err = NULL;
+ GValue retval;
+
+ msg = soup_message_new ("POST", uri);
+ soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY,
+ body, strlen (body));
+ soup_session_send_message (session, msg);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ debug_printf (1, "ERROR: %d %s\n", msg->status_code,
+ msg->reason_phrase);
+ g_object_unref (msg);
+ return FALSE;
+ }
+
+ if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
+ msg->response_body->length,
+ &retval, &err)) {
+ if (err) {
+ debug_printf (1, "FAULT: %d %s (OK!)\n",
+ err->code, err->message);
+ g_error_free (err);
+ g_object_unref (msg);
+ return TRUE;
+ } else
+ debug_printf (1, "ERROR: could not parse response\n");
+ } else
+ debug_printf (1, "Unexpectedly got successful response!\n");
+
+ g_object_unref (msg);
+ return FALSE;
+}
+
+static gboolean
+test_fault_malformed (void)
+{
+ debug_printf (1, "malformed request: ");
+
+ return do_bad_xmlrpc ("<methodCall/>");
+}
+
+static gboolean
+test_fault_method (void)
+{
+ debug_printf (1, "request to non-existent method: ");
+
+ return do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
+}
+
+static gboolean
+test_fault_args (void)
+{
+ debug_printf (1, "request with invalid args: ");
+
+ return do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
+}
+
+static GOptionEntry xmlrpc_entries[] = {
+ { "uri", 'u', 0, G_OPTION_ARG_STRING, &uri,
+ "Alternate URI for server", NULL },
+ { "server-test", 's', 0, G_OPTION_ARG_NONE, &server_test,
+ "If this is being run from xmlrpc-server-test", NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ test_init (argc, argv, xmlrpc_entries);
+
+ if (!uri) {
+ apache_init ();
+ uri = default_uri;
+ }
+
+ srand (time (NULL));
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+
+ if (!test_sum ())
+ errors++;
+ if (!test_countBools ())
+ errors++;
+ if (!test_md5sum ())
+ errors++;
+ if (!test_dateChange ())
+ errors++;
+ if (!test_echo ())
+ errors++;
+ if (!test_fault_malformed ())
+ errors++;
+ if (!test_fault_method ())
+ errors++;
+ if (!test_fault_args ())
+ errors++;
+
+ soup_test_session_abort_unref (session);
+
+ test_cleanup ();
+ return errors != 0;
+}