├── COPYING ├── Jamroot ├── LICENSE_1_0.txt ├── README ├── build └── Jamfile ├── doc ├── Jamfile ├── doxy2qbk.pl ├── index.html ├── index.xml ├── reference.dox ├── reference.qbk ├── reference.xsl ├── std_dox.txt ├── urdl.png └── urdl.qbk ├── example ├── Jamfile ├── get1.cpp ├── get2.cpp ├── multiget1.cpp └── multiget2.cpp ├── include └── urdl │ ├── detail │ ├── abi_prefix.hpp │ ├── abi_suffix.hpp │ ├── config.hpp │ ├── connect.hpp │ ├── coroutine.hpp │ ├── file_read_stream.hpp │ ├── handshake.hpp │ ├── http_read_stream.hpp │ ├── parsers.hpp │ └── scoped_ptr.hpp │ ├── http.hpp │ ├── impl │ ├── http.ipp │ ├── istreambuf.ipp │ ├── option_set.ipp │ └── url.ipp │ ├── istream.hpp │ ├── istreambuf.hpp │ ├── option_set.hpp │ ├── read_stream.hpp │ └── url.hpp ├── src └── urdl.cpp └── test ├── Jamfile ├── http_server.hpp ├── istream.cpp ├── istreambuf.cpp ├── option_set.cpp ├── read_stream.cpp ├── unit_test.hpp └── url.cpp /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 2 | 3 | Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | -------------------------------------------------------------------------------- /Jamroot: -------------------------------------------------------------------------------- 1 | import modules ; 2 | import package ; 3 | 4 | path-constant URDL_ROOT : . ; 5 | 6 | BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; 7 | if $(BOOST_ROOT) 8 | { 9 | use-project /boost : $(BOOST_ROOT) ; 10 | } 11 | 12 | rule handle-static-runtime ( properties * ) 13 | { 14 | if shared in $(properties) && static in $(properties) 15 | { 16 | return no ; 17 | } 18 | } 19 | 20 | project 21 | : 22 | build-dir build/bin 23 | : 24 | requirements 25 | multi 26 | @handle-static-runtime 27 | : 28 | default-build 29 | debug release 30 | multi 31 | shared static 32 | shared static 33 | ; 34 | 35 | install lib 36 | : 37 | build 38 | : 39 | lib 40 | LIB 41 | ; 42 | 43 | local patterns = *.hpp *.ipp ; 44 | 45 | local dirs = include/urdl include/urdl/* ; 46 | 47 | package.install install 48 | : 49 | include 50 | : 51 | : 52 | build 53 | : 54 | [ glob $(dirs)/$(patterns) ] 55 | ; 56 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | urdl version 0.1 2 | Released Wednesday, 03 June 2009. 3 | 4 | See doc/index.html for build information and API documentation. 5 | -------------------------------------------------------------------------------- /build/Jamfile: -------------------------------------------------------------------------------- 1 | import common ; 2 | import os ; 3 | import modules ; 4 | 5 | if [ os.name ] = SOLARIS 6 | { 7 | lib socket ; 8 | lib nsl ; 9 | } 10 | else if [ os.name ] = NT 11 | { 12 | lib ws2_32 ; 13 | lib mswsock ; 14 | } 15 | else if [ os.name ] = HPUX 16 | { 17 | lib ipv6 ; 18 | } 19 | 20 | local SSL_OPTIONS ; 21 | if [ modules.peek : URDL_DISABLE_SSL ] = 1 22 | { 23 | SSL_OPTIONS = URDL_DISABLE_SSL=1 ; 24 | } 25 | else if [ os.name ] = NT 26 | { 27 | OPENSSL_ROOT = [ modules.peek : OPENSSL_ROOT ] ; 28 | lib user32 ; 29 | lib advapi32 ; 30 | lib gdi32 ; 31 | SSL_OPTIONS = $(OPENSSL_ROOT)/inc32 32 | $(OPENSSL_ROOT)/out32dll/libeay32.lib 33 | $(OPENSSL_ROOT)/out32dll/ssleay32.lib 34 | user32 advapi32 gdi32 ; 35 | } 36 | else 37 | { 38 | lib ssl ; 39 | lib crypto ; 40 | SSL_OPTIONS = ssl crypto ; 41 | } 42 | 43 | project urdl 44 | : 45 | source-location ../src 46 | : 47 | requirements 48 | multi 49 | : 50 | usage-requirements 51 | shared:URDL_DYN_LINK=1 52 | static:URDL_STATIC_LINK=1 53 | $(URDL_ROOT)/include 54 | /boost/system//boost_system 55 | /boost/date_time//boost_date_time 56 | BOOST_ALL_NO_LIB=1 57 | SOLARIS:socket 58 | SOLARIS:nsl 59 | NT:_WIN32_WINNT=0x0501 60 | NT:WIN32_LEAN_AND_MEAN=1 61 | NT,msvc:_SCL_SECURE_NO_WARNINGS=1 62 | NT,msvc:_CRT_SECURE_NO_WARNINGS=1 63 | NT,gcc:ws2_32 64 | NT,gcc:mswsock 65 | NT,gcc-cygwin:__USE_W32_SOCKETS 66 | HPUX,gcc:_XOPEN_SOURCE_EXTENDED 67 | HPUX:ipv6 68 | $(SSL_OPTIONS) 69 | ; 70 | 71 | lib urdl 72 | : 73 | urdl.cpp 74 | : 75 | shared:BOOST_ALL_DYN_LINK=1 76 | static:BOOST_ALL_STATIC_LINK=1 77 | $(URDL_ROOT)/include 78 | /boost/system//boost_system 79 | /boost/date_time//boost_date_time 80 | BOOST_ALL_NO_LIB=1 81 | SOLARIS:socket 82 | SOLARIS:nsl 83 | NT:_WIN32_WINNT=0x0501 84 | NT,msvc:_SCL_SECURE_NO_WARNINGS=1 85 | NT,msvc:_CRT_SECURE_NO_WARNINGS=1 86 | NT,gcc:ws2_32 87 | NT,gcc:mswsock 88 | NT,gcc-cygwin:__USE_W32_SOCKETS 89 | HPUX,gcc:_XOPEN_SOURCE_EXTENDED 90 | HPUX:ipv6 91 | on 92 | @tag 93 | $(SSL_OPTIONS) 94 | ; 95 | 96 | rule tag ( name : type ? : property-set ) 97 | { 98 | local result = [ common.format-name 99 | 100 | : $(name) : $(type) : $(property-set) ] ; 101 | return "$(result)" ; 102 | } 103 | -------------------------------------------------------------------------------- /doc/Jamfile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | # 7 | 8 | using quickbook ; 9 | 10 | project 11 | : 12 | default-build 13 | debug 14 | multi 15 | shared 16 | shared 17 | ; 18 | 19 | xml standalone_doc 20 | : 21 | urdl.qbk 22 | ; 23 | 24 | boostbook standalone 25 | : 26 | standalone_doc 27 | : 28 | boost.image.src=urdl.png 29 | boost.image.alt="Urdl C++ Library" 30 | boost.image.w=160 31 | boost.image.h=60 32 | nav.layout=none 33 | navig.graphics.path="" 34 | admon.graphics.path="" 35 | chapter.autolabel=0 36 | chunk.section.depth=8 37 | chunk.first.sections=1 38 | toc.section.depth=3 39 | toc.max.depth=3 40 | generate.section.toc.level=2 41 | generate.toc="chapter toc section toc" 42 | ; 43 | 44 | install html 45 | : 46 | /boost//doc/src/boostbook.css 47 | /boost//doc/src/images/blank.png 48 | /boost//doc/src/images/caution.png 49 | /boost//doc/src/images/draft.png 50 | /boost//doc/src/images/home.png 51 | /boost//doc/src/images/important.png 52 | /boost//doc/src/images/next_disabled.png 53 | /boost//doc/src/images/next.png 54 | /boost//doc/src/images/note.png 55 | /boost//doc/src/images/prev_disabled.png 56 | /boost//doc/src/images/prev.png 57 | /boost//doc/src/images/tip.png 58 | /boost//doc/src/images/up_disabled.png 59 | /boost//doc/src/images/up.png 60 | /boost//doc/src/images/warning.png 61 | urdl.png 62 | ; 63 | -------------------------------------------------------------------------------- /doc/doxy2qbk.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | # Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | use strict; 9 | 10 | system("doxygen reference.dox"); 11 | chdir("xml"); 12 | system("xsltproc combine.xslt index.xml > all.xml"); 13 | chdir(".."); 14 | system("xsltproc reference.xsl xml/all.xml > reference.qbk"); 15 | system("rm -rf xml"); 16 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Urdl 4 | 5 | 6 | 7 |

8 | Automatic redirection failed, please go to 9 | html/index.html 10 |

11 |
12 |

13 | Copyright (c) 2009-2013 Christopher M. Kohlhoff 14 |

15 |

16 | Distributed under the Boost Software License, Version 1.0. (See accompanying 17 | file LICENSE_1_0.txt or copy at 18 | www.boost.org/LICENSE_1_0.txt) 19 |

20 | 21 | 22 | -------------------------------------------------------------------------------- /doc/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 |
12 | Index 13 | 14 | 15 | 16 |
17 | -------------------------------------------------------------------------------- /doc/reference.dox: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.4.5 2 | 3 | #--------------------------------------------------------------------------- 4 | # Project related configuration options 5 | #--------------------------------------------------------------------------- 6 | PROJECT_NAME = "Urdl Reference" 7 | PROJECT_NUMBER = 8 | OUTPUT_DIRECTORY = . 9 | CREATE_SUBDIRS = NO 10 | OUTPUT_LANGUAGE = English 11 | USE_WINDOWS_ENCODING = NO 12 | BRIEF_MEMBER_DESC = YES 13 | REPEAT_BRIEF = YES 14 | ABBREVIATE_BRIEF = 15 | ALWAYS_DETAILED_SEC = YES 16 | INLINE_INHERITED_MEMB = YES 17 | FULL_PATH_NAMES = YES 18 | STRIP_FROM_PATH = ./../../../ 19 | STRIP_FROM_INC_PATH = 20 | SHORT_NAMES = NO 21 | JAVADOC_AUTOBRIEF = NO 22 | MULTILINE_CPP_IS_BRIEF = YES 23 | DETAILS_AT_TOP = YES 24 | INHERIT_DOCS = NO 25 | SEPARATE_MEMBER_PAGES = NO 26 | TAB_SIZE = 2 27 | ALIASES = 28 | OPTIMIZE_OUTPUT_FOR_C = NO 29 | OPTIMIZE_OUTPUT_JAVA = NO 30 | BUILTIN_STL_SUPPORT = NO 31 | DISTRIBUTE_GROUP_DOC = NO 32 | SUBGROUPING = YES 33 | #--------------------------------------------------------------------------- 34 | # Build related configuration options 35 | #--------------------------------------------------------------------------- 36 | EXTRACT_ALL = YES 37 | EXTRACT_PRIVATE = YES 38 | EXTRACT_STATIC = YES 39 | EXTRACT_LOCAL_CLASSES = NO 40 | EXTRACT_LOCAL_METHODS = NO 41 | HIDE_UNDOC_MEMBERS = YES 42 | HIDE_UNDOC_CLASSES = YES 43 | HIDE_FRIEND_COMPOUNDS = NO 44 | HIDE_IN_BODY_DOCS = NO 45 | INTERNAL_DOCS = NO 46 | CASE_SENSE_NAMES = YES 47 | HIDE_SCOPE_NAMES = NO 48 | SHOW_INCLUDE_FILES = NO 49 | INLINE_INFO = NO 50 | SORT_MEMBER_DOCS = NO 51 | SORT_BRIEF_DOCS = NO 52 | SORT_BY_SCOPE_NAME = NO 53 | GENERATE_TODOLIST = NO 54 | GENERATE_TESTLIST = NO 55 | GENERATE_BUGLIST = NO 56 | GENERATE_DEPRECATEDLIST= NO 57 | ENABLED_SECTIONS = 58 | MAX_INITIALIZER_LINES = 30 59 | SHOW_USED_FILES = NO 60 | SHOW_DIRECTORIES = NO 61 | FILE_VERSION_FILTER = 62 | #--------------------------------------------------------------------------- 63 | # configuration options related to warning and progress messages 64 | #--------------------------------------------------------------------------- 65 | QUIET = NO 66 | WARNINGS = YES 67 | WARN_IF_UNDOCUMENTED = YES 68 | WARN_IF_DOC_ERROR = YES 69 | WARN_NO_PARAMDOC = NO 70 | WARN_FORMAT = "$file:$line: $text" 71 | WARN_LOGFILE = 72 | #--------------------------------------------------------------------------- 73 | # configuration options related to the input files 74 | #--------------------------------------------------------------------------- 75 | INPUT = ./../include/urdl std_dox.txt 76 | FILE_PATTERNS = *.hpp 77 | RECURSIVE = NO 78 | EXCLUDE = 79 | EXCLUDE_SYMLINKS = NO 80 | EXCLUDE_PATTERNS = 81 | EXAMPLE_PATH = 82 | EXAMPLE_PATTERNS = 83 | EXAMPLE_RECURSIVE = YES 84 | IMAGE_PATH = 85 | INPUT_FILTER = 86 | FILTER_PATTERNS = 87 | FILTER_SOURCE_FILES = NO 88 | #--------------------------------------------------------------------------- 89 | # configuration options related to source browsing 90 | #--------------------------------------------------------------------------- 91 | SOURCE_BROWSER = NO 92 | INLINE_SOURCES = NO 93 | STRIP_CODE_COMMENTS = YES 94 | REFERENCED_BY_RELATION = NO 95 | REFERENCES_RELATION = NO 96 | USE_HTAGS = NO 97 | VERBATIM_HEADERS = NO 98 | #--------------------------------------------------------------------------- 99 | # configuration options related to the alphabetical class index 100 | #--------------------------------------------------------------------------- 101 | ALPHABETICAL_INDEX = YES 102 | COLS_IN_ALPHA_INDEX = 1 103 | IGNORE_PREFIX = 104 | #--------------------------------------------------------------------------- 105 | # configuration options related to the HTML output 106 | #--------------------------------------------------------------------------- 107 | GENERATE_HTML = NO 108 | HTML_OUTPUT = . 109 | HTML_FILE_EXTENSION = .html 110 | HTML_HEADER = 111 | HTML_FOOTER = 112 | HTML_STYLESHEET = 113 | HTML_ALIGN_MEMBERS = YES 114 | GENERATE_HTMLHELP = NO 115 | CHM_FILE = 116 | HHC_LOCATION = 117 | GENERATE_CHI = NO 118 | BINARY_TOC = NO 119 | TOC_EXPAND = NO 120 | DISABLE_INDEX = YES 121 | ENUM_VALUES_PER_LINE = 1 122 | GENERATE_TREEVIEW = NO 123 | TREEVIEW_WIDTH = 250 124 | #--------------------------------------------------------------------------- 125 | # configuration options related to the LaTeX output 126 | #--------------------------------------------------------------------------- 127 | GENERATE_LATEX = NO 128 | LATEX_OUTPUT = latex 129 | LATEX_CMD_NAME = latex 130 | MAKEINDEX_CMD_NAME = makeindex 131 | COMPACT_LATEX = NO 132 | PAPER_TYPE = a4wide 133 | EXTRA_PACKAGES = 134 | LATEX_HEADER = 135 | PDF_HYPERLINKS = NO 136 | USE_PDFLATEX = NO 137 | LATEX_BATCHMODE = NO 138 | LATEX_HIDE_INDICES = NO 139 | #--------------------------------------------------------------------------- 140 | # configuration options related to the RTF output 141 | #--------------------------------------------------------------------------- 142 | GENERATE_RTF = NO 143 | RTF_OUTPUT = rtf 144 | COMPACT_RTF = NO 145 | RTF_HYPERLINKS = NO 146 | RTF_STYLESHEET_FILE = 147 | RTF_EXTENSIONS_FILE = 148 | #--------------------------------------------------------------------------- 149 | # configuration options related to the man page output 150 | #--------------------------------------------------------------------------- 151 | GENERATE_MAN = NO 152 | MAN_OUTPUT = man 153 | MAN_EXTENSION = .3 154 | MAN_LINKS = NO 155 | #--------------------------------------------------------------------------- 156 | # configuration options related to the XML output 157 | #--------------------------------------------------------------------------- 158 | GENERATE_XML = YES 159 | XML_OUTPUT = xml 160 | XML_SCHEMA = 161 | XML_DTD = 162 | XML_PROGRAMLISTING = NO 163 | #--------------------------------------------------------------------------- 164 | # configuration options for the AutoGen Definitions output 165 | #--------------------------------------------------------------------------- 166 | GENERATE_AUTOGEN_DEF = NO 167 | #--------------------------------------------------------------------------- 168 | # configuration options related to the Perl module output 169 | #--------------------------------------------------------------------------- 170 | GENERATE_PERLMOD = NO 171 | PERLMOD_LATEX = NO 172 | PERLMOD_PRETTY = YES 173 | PERLMOD_MAKEVAR_PREFIX = 174 | #--------------------------------------------------------------------------- 175 | # Configuration options related to the preprocessor 176 | #--------------------------------------------------------------------------- 177 | ENABLE_PREPROCESSING = YES 178 | MACRO_EXPANSION = YES 179 | EXPAND_ONLY_PREDEF = YES 180 | SEARCH_INCLUDES = YES 181 | INCLUDE_PATH = 182 | INCLUDE_FILE_PATTERNS = 183 | PREDEFINED = URDL_DECL 184 | EXPAND_AS_DEFINED = 185 | SKIP_FUNCTION_MACROS = YES 186 | #--------------------------------------------------------------------------- 187 | # Configuration::additions related to external references 188 | #--------------------------------------------------------------------------- 189 | TAGFILES = 190 | GENERATE_TAGFILE = 191 | ALLEXTERNALS = NO 192 | EXTERNAL_GROUPS = YES 193 | PERL_PATH = /usr/bin/perl 194 | #--------------------------------------------------------------------------- 195 | # Configuration options related to the dot tool 196 | #--------------------------------------------------------------------------- 197 | CLASS_DIAGRAMS = NO 198 | HIDE_UNDOC_RELATIONS = YES 199 | HAVE_DOT = YES 200 | CLASS_GRAPH = YES 201 | COLLABORATION_GRAPH = NO 202 | GROUP_GRAPHS = NO 203 | UML_LOOK = NO 204 | TEMPLATE_RELATIONS = YES 205 | INCLUDE_GRAPH = NO 206 | INCLUDED_BY_GRAPH = NO 207 | CALL_GRAPH = NO 208 | GRAPHICAL_HIERARCHY = NO 209 | DIRECTORY_GRAPH = NO 210 | DOT_IMAGE_FORMAT = png 211 | DOT_PATH = 212 | DOTFILE_DIRS = 213 | MAX_DOT_GRAPH_WIDTH = 640 214 | MAX_DOT_GRAPH_HEIGHT = 640 215 | MAX_DOT_GRAPH_DEPTH = 0 216 | DOT_TRANSPARENT = NO 217 | DOT_MULTI_TARGETS = NO 218 | GENERATE_LEGEND = NO 219 | DOT_CLEANUP = YES 220 | #--------------------------------------------------------------------------- 221 | # Configuration::additions related to the search engine 222 | #--------------------------------------------------------------------------- 223 | SEARCHENGINE = NO 224 | -------------------------------------------------------------------------------- /doc/std_dox.txt: -------------------------------------------------------------------------------- 1 | /** 2 | \class std::basic_istream 3 | */ 4 | 5 | /** 6 | \class std::streambuf 7 | */ 8 | -------------------------------------------------------------------------------- /doc/urdl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chriskohlhoff/urdl/9f7ee4570d2a3d8948b7e4e0c94a15cdad0da7b5/doc/urdl.png -------------------------------------------------------------------------------- /doc/urdl.qbk: -------------------------------------------------------------------------------- 1 | [/ 2 | / Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 3 | / 4 | / Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | /] 7 | 8 | [library Urdl 9 | [quickbook 1.4] 10 | [copyright 2009-2013 Christopher M. Kohlhoff] 11 | [purpose Networking/HTTP library] 12 | [license 13 | Distributed under the Boost Software License, Version 1.0. 14 | (See accompanying file LICENSE_1_0.txt or copy at 15 | [@http://www.boost.org/LICENSE_1_0.txt]) 16 | ] 17 | [category template] 18 | [category generic] 19 | ] 20 | 21 | [template indexterm1[term1] ''''''[term1]''''''] 22 | [template indexterm2[term1 term2] ''''''[term1]''''''[term2]''''''] 23 | [def SyncReadStream [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/SyncReadStream.html SyncReadStream]] 24 | [def AsyncReadStream [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/AsyncReadStream.html AsyncReadStream]] 25 | [def boost::asio::read [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/read.html boost::asio::read]] 26 | [def boost::asio::read_until [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/read_until.html boost::asio::read_until]] 27 | [def boost::asio::async_read [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/async_read.html boost::asio::async_read]] 28 | [def boost::asio::async_read_until [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/async_read_until.html boost::asio::async_read_until]] 29 | 30 | [/=============================================================================] 31 | 32 | [heading What is Urdl?] 33 | 34 | Urdl is a cross-platform C++ library for downloading web content using a URL. 35 | It provides an easy-to-use extension to standard C++ iostreams and an 36 | asynchronous interface for use with Boost.Asio. 37 | 38 | [heading What can Urdl be used for?] 39 | 40 | Possible uses for Urdl include: 41 | 42 | * Downloading application configuration or data files. 43 | 44 | * Downloading software updates. 45 | 46 | * Accessing RSS feeds. 47 | 48 | * Web service clients (e.g. SOAP, XML-RPC or REST). 49 | 50 | * Web scraping. 51 | 52 | [heading Obtaining Urdl] 53 | 54 | The latest release of Urdl can be downloaded from 55 | [@http://sourceforge.net/project/showfiles.php?group_id=262090 SourceForge.net]. 56 | 57 | [/=============================================================================] 58 | 59 | [section:rationale Rationale] 60 | 61 | The primary goal of Urdl is: 62 | 63 | * To provide a simple abstraction for accessing and downloading internet 64 | resources. 65 | 66 | Some secondary goals include: 67 | 68 | * To act as an example of using Boost.Asio to create client-side abstractions. 69 | 70 | * To act as an example of how to extend the Boost.System `error_code` facility. 71 | 72 | * To explore the use of coroutine for implementing protocols using Boost.Asio. 73 | 74 | [endsect] 75 | 76 | [/=============================================================================] 77 | 78 | [section:features Features] 79 | 80 | * [*Support for HTTP, HTTPS and local files.] 81 | 82 | [:Resources may be downloaded using HTTP 1.0 (using URLs of the form 83 | `"http://..."`), HTTPS (`"https://..."`). Local files are also supported 84 | (`"file://..."`). See [link urdl.planned_features Planned Features] for 85 | limitations in the current protocol implementations.] 86 | 87 | * [*Extension to C++ IOStreams.] 88 | 89 | [:The [link urdl.reference.core.istream urdl::istream] class provides a way to 90 | easily access content using standard C++ I/O facilities.] 91 | 92 | * [*Integration with Boost.Asio.] 93 | 94 | [:The [link urdl.reference.core.read_stream urdl::read_stream] class allows 95 | Urdl functionality to be used with other Boost.Asio features such as sockets 96 | and timers.] 97 | 98 | [endsect] 99 | 100 | [/=============================================================================] 101 | 102 | [section:planned_features Planned features] 103 | 104 | The following features are planned for a future release of Urdl: 105 | 106 | * [*Support for HTTP chunking.] 107 | 108 | [:Add the ability to handle HTTP responses where the [^Transfer-Encoding] is 109 | [^chunked].] 110 | 111 | * [*Support for HTTP proxies.] 112 | 113 | [:Add support for making HTTP and HTTPS requests through a proxy.] 114 | 115 | * [*Support for FTP.] 116 | 117 | [:Add support for downloading files using FTP.] 118 | 119 | * [*SSL options to control peer verification.] 120 | 121 | [:Add new options for controlling the way Urdl uses SSL to implement HTTPS, e.g. 122 | the ability to specify a certificate authority file or directory, whether 123 | peers are verified, etc. The current implementation always verifies the 124 | peer using OpenSSL's default certificate chains.] 125 | 126 | * [*Support for wildcard SSL certificates.] 127 | 128 | [:The current implementation of SSL peer verification supports only certificates 129 | issued to a single hostname.] 130 | 131 | * [*Status function callback.] 132 | 133 | [:Allow the user to register a `boost::function<>` object using `set_option()`. 134 | This will be called by the implementation to provide progress updates (e.g. 135 | resolving, connection, reading, etc).] 136 | 137 | * [*Runtime polymorphism and user-supplied backends.] 138 | 139 | [:Change the underlying implementation to use runtime polymorphism (abstract 140 | base classes) to invoke the appropriate protocol implementation. The user 141 | will be able to register a factory object to provide custom support for 142 | additional protocols.] 143 | 144 | [endsect] 145 | 146 | [/=============================================================================] 147 | 148 | [section:prerequisites Prerequisites] 149 | 150 | Urdl depends on the following libraries: 151 | 152 | * [@http://www.boost.org/ Boost] 1.38 or later. 153 | 154 | * [@http://www.openssl.org/ OpenSSL] (optional) for HTTPS support. 155 | 156 | [endsect] 157 | 158 | [/=============================================================================] 159 | 160 | [section:platforms Supported platforms] 161 | 162 | Urdl has been tested on the following platforms and compilers: 163 | 164 | * Debian Linux 5.0 using g++ 4.1. 165 | 166 | * Debian Linux 5.0 using g++ 4.3. 167 | 168 | * Mac OS X 10.4 using g++ 4.0.1 169 | 170 | * Windows using MinGW / g++ 3.4.5 171 | 172 | * Windows using Visual C++ 8.0 173 | 174 | * Windows using Visual C++ 9.0 175 | 176 | [endsect] 177 | 178 | [/=============================================================================] 179 | 180 | [section:getting_started Getting started] 181 | 182 | [section:building Building and using Urdl] 183 | 184 | There are three options for using Urdl in an application: 185 | 186 | * as a separately compiled library; 187 | 188 | * by compiling the source directly into the application; or 189 | 190 | * as a header-file-only library. 191 | 192 | [heading Using Urdl as a separately compiled library] 193 | 194 | Urdl uses [^bjam] and Boost.Build to build shared and static libraries. To 195 | build the libraries, perform the following steps: 196 | 197 | # Set a [^BOOST_ROOT] environment variable pointing to the location of Boost. 198 | 199 | # If building on Windows, set an [^OPENSSL_ROOT] environment variable pointing 200 | to the location of OpenSSL. When building on UNIX, the build scripts assume 201 | that the OpenSSL headers and libraries may be found in the system's include 202 | and library paths, respectively. To disable Urdl's SSL support, define 203 | [^URDL_DISABLE_SSL=1] as an environment variable. 204 | 205 | # Run [^bjam] in the top-level directory of the Urdl distribution. Libraries 206 | should be built into the [^lib] subdirectory. 207 | 208 | Then, to use Urdl in an application: 209 | 210 | # Add the [^include] subdirectory to your compiler's include path. 211 | 212 | # Add the [^lib] subdirectory to your linker's library path. 213 | 214 | # Add the Boost directory to your compiler's include path. 215 | 216 | # Add the Boost library directory (e.g. [^stage/lib]) to your linker's library 217 | path. Urdl needs the application to link against the Boost.System and 218 | Boost.Date_Time libraries. 219 | 220 | # If disabling Urdl's SSL support, add [^URDL_DISABLE_SSL=1] to your compiler's 221 | preprocessor definitions. 222 | 223 | # If using the DLL version of Urdl on Windows, add [^URDL_DYN_LINK=1] to your 224 | compiler's preprocessor definitions. 225 | 226 | [heading Compiling Urdl directly into an application] 227 | 228 | To compile Urdl directly into an application, perform the following steps: 229 | 230 | # Add the [^include] subdirectory to your compiler's include path. 231 | 232 | # Add the file [^src/urdl.cpp] to your project or makefile. 233 | 234 | # Add the Boost directory to your compiler's include path. 235 | 236 | # Add the Boost library directory (e.g. [^stage/lib]) to your linker's library 237 | path. Urdl needs the application to link against the Boost.System and 238 | Boost.Date_Time libraries. 239 | 240 | # If disabling Urdl's SSL support, add [^URDL_DISABLE_SSL=1] to your compiler's 241 | preprocessor definitions. 242 | 243 | # If building on Windows, add [^URDL_NO_LIB=1] to your compiler's preprocessor 244 | definitions to disable autolinking. 245 | 246 | [heading Using Urdl as a header-only library] 247 | 248 | To use Urdl as a header-only library, perform the following steps: 249 | 250 | # Add the [^include] subdirectory to your compiler's include path. 251 | 252 | # Add the Boost directory to your compiler's include path. 253 | 254 | # Add the Boost library directory (e.g. [^stage/lib]) to your linker's library 255 | path. Urdl needs the application to link against the Boost.System library and 256 | possibly the Boost.Date_Time library. 257 | 258 | # If disabling Urdl's SSL support, add [^URDL_DISABLE_SSL=1] to your compiler's 259 | preprocessor definitions. 260 | 261 | # Add [^URDL_HEADER_ONLY=1] to your compiler's preprocessor definitions. 262 | 263 | [endsect] 264 | 265 | [section Downloading a file using an istream] 266 | 267 | The simplest use of Urdl is to download a file or other resource using the 268 | [link urdl.reference.core.istream urdl::istream] class. 269 | 270 | // For std::cout and std::cerr. 271 | #include 272 | #include 273 | 274 | // For urdl::istream. Each of Urdl's core classes has its own header file. 275 | #include 276 | 277 | int main() 278 | { 279 | // Open the URL. The connection is established and the HTTP request is sent. 280 | urdl::istream is("http://www.boost.org/LICENSE_1_0.txt"); 281 | 282 | // Check whether we opened the URL successfully. 283 | if (!is) 284 | { 285 | std::cerr << "Unable to open URL" << std::endl; 286 | return 1; 287 | } 288 | 289 | // From here on, we can use urdl::istream like any other std::istream 290 | // object. Let's output the downloaded content one line at a time. 291 | std::string line; 292 | while (std::getline(is, line)) 293 | { 294 | std::cout << line << std::endl; 295 | } 296 | } 297 | 298 | [endsect] 299 | 300 | [section Checking for errors] 301 | 302 | If we are unable to open a URL for any reason, we can find out the last error 303 | from the [link urdl.reference.core.istream urdl::istream] class: 304 | 305 | urdl::istream is("http://somehost/path"); 306 | if (!is) 307 | { 308 | std::cerr << "Unable to open URL: "; 309 | std::cerr << is.error().message() << std::endl; 310 | return 1; 311 | } 312 | 313 | Alternatively, we may test for a specific error: 314 | 315 | // For the HTTP error codes. 316 | #include 317 | 318 | ... 319 | 320 | urdl::istream is("http://somehost/path"); 321 | if (!is) 322 | { 323 | if (is.error() == urdl::http::errc::not_found) 324 | { 325 | // Hmm, maybe we can try downloading the file from somewhere else... 326 | } 327 | } 328 | 329 | [endsect] 330 | 331 | [section Setting options to perform an HTTP POST] 332 | 333 | To upload over HTTP, we set the [link urdl.reference.opt.http__request_method 334 | http::request_method], [link urdl.reference.opt.http__request_content 335 | http::request_content] and [link urdl.reference.opt.http__request_content_type 336 | http::request_content_type] options on the stream prior to opening the URL: 337 | 338 | // For the HTTP options. 339 | #include 340 | 341 | ... 342 | 343 | urdl::istream is; 344 | 345 | // We're doing an HTTP POST ... 346 | is.set_option(urdl::http::request_method("POST")); 347 | 348 | // ... where the MIME type indicates plain text ... 349 | is.set_option(urdl::http::request_content_type("text/plain")); 350 | 351 | // ... and here's the content. 352 | is.set_option(urdl::http::request_content("Hello, world!")); 353 | 354 | // All options set, so now we can open the URL. 355 | is.open("http://somehost/path"); 356 | 357 | [endsect] 358 | 359 | [section Grouping and reusing options] 360 | 361 | Options may be grouped using an [link urdl.reference.core.option_set 362 | urdl::option_set] object. This allows the options to be reused across multiple 363 | requests. 364 | 365 | // For urdl::option_set. 366 | #include 367 | 368 | ... 369 | 370 | urdl::option_set common_options; 371 | 372 | // Prevent HTTP redirections. 373 | common_options.set_option(urdl::http::max_redirects(0)); 374 | 375 | // Tell the web server about Urdl. 376 | common_options.set_option(urdl::http::user_agent("Urdl")); 377 | 378 | // Open a URL using only the common options. 379 | urdl::istream is1("http://somehost/path1", common_options); 380 | 381 | // Open a URL with additional options. In this case, the common options are 382 | // applied to the stream as if we had called set_option invidiually for each 383 | // option in the set. 384 | urdl::istream is2; 385 | is2.set_option(urdl::http::request_method("POST")); 386 | is2.set_option(urdl::http::request_content_type("text/plain")); 387 | is2.set_option(urdl::http::request_content("Hello, world!")); 388 | is2.set_options(common_options); 389 | 390 | [endsect] 391 | 392 | [section Specifying timeouts] 393 | 394 | To prevent unresponsive servers from indefinitely hanging a program, the [link 395 | urdl.reference.core.istream urdl::istream] class uses a timeout when opening 396 | the stream and when reading content. 397 | 398 | urdl::istream is; 399 | 400 | // Fail if the URL cannot be opened within 60 seconds. 401 | is.open_timeout(60000); 402 | is.open("http://somehost/path"); 403 | 404 | if (!is) 405 | { 406 | // If the open operation timed out then: 407 | // is.error() == boost::system::errc::timed_out 408 | // holds true. 409 | } 410 | 411 | ... 412 | 413 | // Fail if an individual read does not complete within 30 seconds. 414 | is.read_timeout(30000); 415 | 416 | // From here on, use urdl::istream like any other std::istream object. 417 | std::string line; 418 | while (std::getline(is, line)) 419 | { 420 | std::cout << line << std::endl; 421 | } 422 | 423 | // If a read operation timed out then: 424 | // is.error() == boost::system::errc::timed_out 425 | // holds true. 426 | 427 | [endsect] 428 | 429 | [section Parsing URLs] 430 | 431 | The [link urdl.reference.core.url urdl::url] class gives us the ability to parse 432 | URLs and access their component parts. The default constructor: 433 | 434 | // For urdl::url. 435 | #include 436 | 437 | ... 438 | 439 | urdl::url url("http://somehost/path"); 440 | 441 | provides a conversion from `std::string` or `const char*` to URLs. If the URL 442 | does not parse correctly, the constructor throws an exception of type 443 | `boost::system::system_error`. It is this constructor that is used when we 444 | write: 445 | 446 | urdl::istream is("http://somehost/path"); 447 | 448 | We can also use the [link urdl.reference.core.url.from_string 449 | urdl::url::from_string] static member function to explicitly parse a URL, with 450 | the option of choosing a throwing overload: 451 | 452 | urdl::url url = urdl::url::from_string("http://somehost/path"); 453 | 454 | or an overload that does not throw an exception on failure: 455 | 456 | boost::system::error_code ec; 457 | urdl::url url = urdl::url::from_string("http://somehost/path", ec); 458 | 459 | [endsect] 460 | 461 | [section Integrating with Boost.Asio] 462 | 463 | The [link urdl.reference.core.read_stream urdl::read_stream] class allows 464 | applications to use Urdl's functionality in conjunction with Boost.Asio. 465 | 466 | To synchronously open a URL, we may use: 467 | 468 | // For urdl::read_stream. 469 | #include 470 | 471 | ... 472 | 473 | boost::asio::io_service io_service; 474 | 475 | ... 476 | 477 | // An urdl::read_stream must always have an associated io_service. 478 | urdl::read_stream stream(io_service); 479 | 480 | // Open the URL synchronously. Throws boost::system::system_error on failure. 481 | stream.open("http://somehost/path"); 482 | 483 | ... 484 | 485 | // Alternatively, open the URL synchronously without throwing on error. 486 | boost::system::error_code ec; 487 | stream.open("http://somehost/path", ec); 488 | 489 | To asynchronously open a URL, we can write: 490 | 491 | void open_handler(const boost::system::error_code& ec) 492 | { 493 | if (ec) 494 | { 495 | // URL successfully opened. 496 | ... 497 | } 498 | else 499 | { 500 | std::cerr << "Unable to open URL: "; 501 | std::cerr << is.error().message() << std::endl; 502 | } 503 | } 504 | 505 | ... 506 | 507 | stream.async_open("http://somehost/path", open_handler); 508 | 509 | and the callback function `open_handler` will be invoked once the asynchronous 510 | operation completes. 511 | 512 | The [link urdl.reference.core.read_stream urdl::read_stream] class meets 513 | Boost.Asio's SyncReadStream and AsyncReadStream type requirements. This means 514 | we can use it with the synchronous functions boost::asio::read and 515 | boost::asio::read_until: 516 | 517 | boost::array data; 518 | boost::asio::read(stream, boost::asio::buffer(data)); 519 | 520 | ... 521 | 522 | boost::asio::streambuf buffer; 523 | boost::asio::read_until(stream, buffer, "\r\n"); 524 | 525 | or with the asynchronous functions boost::asio::async_read and 526 | boost::asio::async_read_until: 527 | 528 | void read_handler(const boost::system::error_code& ec, std::size_t length) 529 | { 530 | ... 531 | } 532 | 533 | ... 534 | 535 | boost::array data; 536 | boost::asio::async_read(stream, boost::asio::buffer(data), read_handler); 537 | 538 | ... 539 | 540 | boost::asio::streambuf buffer; 541 | boost::asio::async_read_until(stream, buffer, "\r\n", read_handler); 542 | 543 | The asynchronous functions on the [link urdl.reference.core.read_stream 544 | urdl::read_stream] class allow concurrent access to multiple URLs without 545 | requiring additional threads. Furthermore, we can perform the operations 546 | concurrently with any of the other asynchronous facilities provided by 547 | Boost.Asio (sockets, timers and so on). 548 | 549 | [endsect] 550 | 551 | [endsect] 552 | 553 | [/=============================================================================] 554 | 555 | [include reference.qbk] 556 | [xinclude index.xml] 557 | -------------------------------------------------------------------------------- /example/Jamfile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | # 7 | 8 | project 9 | : 10 | requirements ../build//urdl 11 | : 12 | default-build 13 | debug 14 | multi 15 | shared 16 | shared 17 | ; 18 | 19 | exe get1 : get1.cpp ; 20 | exe get2 : get2.cpp ; 21 | exe multiget1 : multiget1.cpp ; 22 | 23 | explicit multiget2 ; 24 | exe multiget2 : multiget2.cpp 25 | /boost/context//boost_context 26 | /boost/coroutine//boost_coroutine 27 | ; 28 | -------------------------------------------------------------------------------- /example/get1.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // get1.cpp 3 | // ~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int main(int argc, char* argv[]) 17 | { 18 | try 19 | { 20 | if (argc != 3) 21 | { 22 | std::cerr << "Usage: get1 \n"; 23 | return 1; 24 | } 25 | 26 | urdl::istream is(argv[1]); 27 | if (!is) 28 | { 29 | std::cout << is.error().message() << std::endl; 30 | return 1; 31 | } 32 | 33 | std::ofstream os(argv[2], std::ios_base::out | std::ios_base::binary); 34 | if (is.content_length() != std::numeric_limits::max()) 35 | { 36 | boost::progress_display progress(is.content_length()); 37 | while (is && os) 38 | { 39 | char buffer[1024] = ""; 40 | is.read(buffer, sizeof(buffer)); 41 | os.write(buffer, is.gcount()); 42 | progress += is.gcount(); 43 | } 44 | std::cout << std::endl; 45 | } 46 | else 47 | { 48 | os << is.rdbuf(); 49 | } 50 | 51 | std::cout << is.error().message() << std::endl; 52 | } 53 | catch (std::exception& e) 54 | { 55 | std::cerr << "Exception: " << e.what() << std::endl; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example/get2.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // get2.cpp 3 | // ~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int main(int argc, char* argv[]) 17 | { 18 | try 19 | { 20 | if (argc != 3) 21 | { 22 | std::cerr << "Usage: get2 \n"; 23 | return 1; 24 | } 25 | 26 | boost::asio::io_service io_service; 27 | 28 | urdl::read_stream stream(io_service); 29 | stream.open(argv[1]); 30 | 31 | std::ofstream os(argv[2], std::ios_base::out | std::ios_base::binary); 32 | for (;;) 33 | { 34 | char data[1024]; 35 | boost::system::error_code ec; 36 | std::size_t length = stream.read_some(boost::asio::buffer(data), ec); 37 | if (ec == boost::asio::error::eof) 38 | break; 39 | if (ec) 40 | throw boost::system::system_error(ec); 41 | os.write(data, length); 42 | } 43 | } 44 | catch (std::exception& e) 45 | { 46 | std::cerr << "Exception: " << e.what() << std::endl; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/multiget1.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // multiget1.cpp 3 | // ~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class downloader 20 | : public boost::enable_shared_from_this 21 | { 22 | public: 23 | downloader(boost::asio::io_service& io_service) 24 | : read_stream_(io_service) 25 | { 26 | } 27 | 28 | void start(const urdl::url& url, const std::string& file) 29 | { 30 | file_ = file; 31 | read_stream_.async_open(url, 32 | boost::bind(&downloader::handle_open, 33 | shared_from_this(), _1)); 34 | } 35 | 36 | private: 37 | void handle_open(const boost::system::error_code& ec) 38 | { 39 | if (!ec) 40 | { 41 | ofstream_.open(file_.c_str(), std::ios_base::out | std::ios_base::binary); 42 | read_stream_.async_read_some( 43 | boost::asio::buffer(buffer_), 44 | boost::bind(&downloader::handle_read, 45 | shared_from_this(), _1, _2)); 46 | } 47 | } 48 | 49 | void handle_read(const boost::system::error_code& ec, std::size_t length) 50 | { 51 | if (!ec) 52 | { 53 | ofstream_.write(buffer_, length); 54 | read_stream_.async_read_some( 55 | boost::asio::buffer(buffer_), 56 | boost::bind(&downloader::handle_read, 57 | shared_from_this(), _1, _2)); 58 | } 59 | } 60 | 61 | urdl::read_stream read_stream_; 62 | std::string file_; 63 | std::ofstream ofstream_; 64 | char buffer_[1024]; 65 | }; 66 | 67 | int main(int argc, char* argv[]) 68 | { 69 | try 70 | { 71 | if (argc < 3 || argc % 2 == 0) 72 | { 73 | std::cerr << "Usage: multiget1 "; 74 | std::cerr << "[ ...]\n"; 75 | return 1; 76 | } 77 | 78 | boost::asio::io_service io_service; 79 | 80 | for (int i = 1; i < argc; i += 2) 81 | { 82 | boost::shared_ptr d(new downloader(io_service)); 83 | d->start(argv[i], argv[i + 1]); 84 | } 85 | 86 | io_service.run(); 87 | } 88 | catch (std::exception& e) 89 | { 90 | std::cerr << "Exception: " << e.what() << std::endl; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /example/multiget2.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // multiget2.cpp 3 | // ~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | void download(boost::asio::io_service& io_service, 20 | const urdl::url& url, const std::string& file, 21 | boost::asio::yield_context yield) 22 | { 23 | try 24 | { 25 | urdl::read_stream read_stream(io_service); 26 | read_stream.async_open(url, yield); 27 | 28 | std::ofstream ofstream(file.c_str(), 29 | std::ios_base::out | std::ios_base::binary); 30 | 31 | char buffer[1024]; 32 | std::size_t length; 33 | boost::system::error_code ec; 34 | 35 | do 36 | { 37 | length = read_stream.async_read_some( 38 | boost::asio::buffer(buffer), yield[ec]); 39 | ofstream.write(buffer, length); 40 | } while (length > 0); 41 | } 42 | catch (std::exception& e) 43 | { 44 | std::cerr << "Download exception: " << e.what() << std::endl; 45 | } 46 | } 47 | 48 | int main(int argc, char* argv[]) 49 | { 50 | try 51 | { 52 | if (argc < 3 || argc % 2 == 0) 53 | { 54 | std::cerr << "Usage: multiget2 "; 55 | std::cerr << "[ ...]\n"; 56 | return 1; 57 | } 58 | 59 | boost::asio::io_service io_service; 60 | 61 | for (int i = 1; i < argc; i += 2) 62 | { 63 | boost::asio::spawn(io_service, 64 | boost::bind(download, boost::ref(io_service), 65 | urdl::url(argv[i]), std::string(argv[i + 1]), _1)); 66 | } 67 | 68 | io_service.run(); 69 | } 70 | catch (std::exception& e) 71 | { 72 | std::cerr << "Exception: " << e.what() << std::endl; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /include/urdl/detail/abi_prefix.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // abi_prefix.hpp 3 | // ~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | // No include guard. 12 | 13 | #include 14 | 15 | // Disable some pesky MSVC warnings. 16 | #if defined(_MSC_VER) 17 | # pragma warning (push) 18 | # pragma warning (disable:4127) 19 | # pragma warning (disable:4251) 20 | # pragma warning (disable:4355) 21 | # pragma warning (disable:4512) 22 | # pragma warning (disable:4996) 23 | #endif // defined(_MSC_VER) 24 | 25 | // Force external visibility of all types. 26 | #if defined(__GNUC__) 27 | # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) 28 | # pragma GCC visibility push (default) 29 | # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) 30 | #endif // defined(__GNUC__) 31 | 32 | -------------------------------------------------------------------------------- /include/urdl/detail/abi_suffix.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // abi_suffix.hpp 3 | // ~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | // No include guard. 12 | 13 | #if defined(_MSC_VER) 14 | # pragma warning (pop) 15 | #endif // defined(_MSC_VER) 16 | 17 | #if defined(__GNUC__) 18 | # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) 19 | # pragma GCC visibility pop 20 | # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) 21 | #endif // defined(__GNUC__) 22 | 23 | #include 24 | -------------------------------------------------------------------------------- /include/urdl/detail/config.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // config.hpp 3 | // ~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | // No include guard. 12 | 13 | #include 14 | #include 15 | 16 | #if defined(URDL_HEADER_ONLY) 17 | # define URDL_DECL inline 18 | #else // defined(URDL_HEADER_ONLY) 19 | # if defined(BOOST_HAS_DECLSPEC) 20 | // We need to import/export our code only if the user has specifically asked 21 | // for it by defining either BOOST_ALL_DYN_LINK if they want all boost 22 | // libraries to be dynamically linked (and if boost is dynamically linked, urdl 23 | // must be dynamically linked too), or URDL_DYN_LINK if they want just urdl to 24 | // be dynamically liked. 25 | # if defined(BOOST_ALL_DYN_LINK) || defined(URDL_DYN_LINK) 26 | # if !defined(URDL_DYN_LINK) 27 | # define URDL_DYN_LINK 28 | # endif // !defined(URDL_DYN_LINK) 29 | // Export if this is our own source, otherwise import. 30 | # if defined(URDL_SOURCE) 31 | # define URDL_DECL __declspec(dllexport) 32 | # else // defined(URDL_SOURCE) 33 | # define URDL_DECL __declspec(dllimport) 34 | # endif // defined(URDL_SOURCE) 35 | # endif // defined(BOOST_ALL_DYN_LINK) || defined(URDL_DYN_LINK) 36 | # endif // defined(BOOST_HAS_DECLSPEC) 37 | #endif // defined(URDL_HEADER_ONLY) 38 | 39 | // If URDL_DECL isn't defined yet define it now. 40 | #if !defined(URDL_DECL) 41 | # define URDL_DECL 42 | #endif // !defined(URDL_DECL) 43 | 44 | #if !defined(URDL_ERROR_CATEGORY_NOEXCEPT) 45 | # if (BOOST_VERSION >= 105300) 46 | # define URDL_ERROR_CATEGORY_NOEXCEPT BOOST_NOEXCEPT 47 | # else // (BOOOST_VERSION >= 105300) 48 | # define URDL_ERROR_CATEGORY_NOEXCEPT 49 | # endif // (BOOOST_VERSION >= 105300) 50 | #endif // !defined(URDL_ERROR_CATEGORY_NOEXCEPT) 51 | 52 | #if (BOOST_VERSION >= 105400) 53 | # define URDL_INITFN_RESULT_TYPE(h, sig) BOOST_ASIO_INITFN_RESULT_TYPE(h, sig) 54 | #else // (BOOST_VERSION >= 105400) 55 | # define URDL_INITFN_RESULT_TYPE(h, sig) void 56 | #endif // (BOOST_VERSION >= 105400) 57 | 58 | // Enable library autolinking for MSVC. 59 | 60 | #if !defined(BOOST_ALL_NO_LIB) && !defined(URDL_NO_LIB) \ 61 | && !defined(URDL_SOURCE) && !defined(URDL_HEADER_ONLY) \ 62 | && defined(_MSC_VER) 63 | 64 | # if !defined(_MT) 65 | # error "You must use the multithreaded runtime." 66 | # endif 67 | 68 | # if (defined(_DLL) || defined(_RTLDLL)) && defined(URDL_DYN_LINK) 69 | # define URDL_LIB_PREFIX 70 | # elif defined(URDL_DYN_LINK) 71 | # error "Mixing a dll library with a static runtime is unsupported." 72 | # else 73 | # define URDL_LIB_PREFIX "lib" 74 | # endif 75 | 76 | # if defined(_DEBUG) 77 | # if defined(_DLL) 78 | # define URDL_LIB_SUFFIX "-gd" 79 | # else 80 | # define URDL_LIB_SUFFIX "-sgd" 81 | # endif 82 | # else 83 | # if defined(_DLL) 84 | # define URDL_LIB_SUFFIX 85 | # else 86 | # define URDL_LIB_SUFFIX "-s" 87 | # endif 88 | # endif 89 | 90 | # pragma comment(lib, URDL_LIB_PREFIX "urdl" URDL_LIB_SUFFIX ".lib") 91 | 92 | #endif // !defined(BOOST_ALL_NO_LIB) && !defined(URDL_NO_LIB) 93 | // && !defined(URDL_SOURCE) && !defined(URDL_HEADER_ONLY) 94 | // && defined(_MSC_VER) 95 | -------------------------------------------------------------------------------- /include/urdl/detail/connect.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // connect.hpp 3 | // ~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_DETAIL_CONNECT_HPP 12 | #define URDL_DETAIL_CONNECT_HPP 13 | 14 | #include 15 | #include 16 | #include "urdl/detail/coroutine.hpp" 17 | 18 | #include "urdl/detail/abi_prefix.hpp" 19 | 20 | namespace urdl { 21 | namespace detail { 22 | 23 | inline boost::system::error_code connect( 24 | boost::asio::ip::tcp::socket::lowest_layer_type& socket, 25 | boost::asio::ip::tcp::resolver& resolver, 26 | const url& u, boost::system::error_code& ec) 27 | { 28 | // Create a query that corresponds to the url. 29 | std::ostringstream port_string; 30 | port_string << u.port(); 31 | boost::asio::ip::tcp::resolver::query query(u.host(), port_string.str()); 32 | 33 | // Get a list of endpoints corresponding to the query. 34 | boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query, ec); 35 | if (ec) 36 | return ec; 37 | 38 | // Try each endpoint until we successfully establish a connection. 39 | ec = boost::asio::error::host_not_found; 40 | while (ec && iter != boost::asio::ip::tcp::resolver::iterator()) 41 | { 42 | socket.close(ec); 43 | socket.connect(*iter++, ec); 44 | } 45 | if (ec) 46 | return ec; 47 | 48 | // Disable the Nagle algorithm on all sockets. 49 | return socket.set_option(boost::asio::ip::tcp::no_delay(true), ec); 50 | } 51 | 52 | template 53 | class connect_coro : coroutine 54 | { 55 | public: 56 | connect_coro(Handler handler, 57 | boost::asio::ip::tcp::socket::lowest_layer_type& socket, 58 | boost::asio::ip::tcp::resolver& resolver) 59 | : handler_(handler), 60 | socket_(socket), 61 | resolver_(resolver) 62 | { 63 | } 64 | 65 | void operator()(boost::system::error_code ec, 66 | boost::asio::ip::tcp::resolver::iterator iter) 67 | { 68 | iter_ = iter; 69 | (*this)(ec); 70 | } 71 | 72 | void operator()(boost::system::error_code ec, 73 | const boost::asio::ip::tcp::resolver::query* query = 0) 74 | { 75 | URDL_CORO_BEGIN; 76 | 77 | // Open the socket to give the caller something to close to cancel the 78 | // asynchronous operation. 79 | socket_.open(boost::asio::ip::tcp::v4(), ec); 80 | if (ec) 81 | { 82 | URDL_CORO_YIELD(socket_.get_io_service().post( 83 | boost::asio::detail::bind_handler(*this, ec))); 84 | handler_(ec); 85 | return; 86 | } 87 | 88 | // Get a list of endpoints corresponding to the host name. 89 | URDL_CORO_YIELD(resolver_.async_resolve(*query, *this)); 90 | if (ec) 91 | { 92 | handler_(ec); 93 | return; 94 | } 95 | 96 | // Try each endpoint until we successfully establish a connection. 97 | ec = boost::asio::error::host_not_found; 98 | while (ec && iter_ != boost::asio::ip::tcp::resolver::iterator()) 99 | { 100 | // Check whether the operation has been cancelled. 101 | if (!socket_.is_open()) 102 | { 103 | ec = boost::asio::error::operation_aborted; 104 | handler_(ec); 105 | return; 106 | } 107 | 108 | // Try next endpoint. 109 | socket_.close(ec); 110 | endpoint_ = *iter_++; 111 | URDL_CORO_YIELD(socket_.async_connect(endpoint_, *this)); 112 | } 113 | if (ec) 114 | { 115 | handler_(ec); 116 | return; 117 | } 118 | 119 | // Check whether the operation has been cancelled. 120 | if (!socket_.is_open()) 121 | { 122 | ec = boost::asio::error::operation_aborted; 123 | handler_(ec); 124 | return; 125 | } 126 | 127 | // Disable the Nagle algorithm on all sockets. 128 | socket_.set_option(boost::asio::ip::tcp::no_delay(true), ec); 129 | 130 | handler_(ec); 131 | 132 | URDL_CORO_END; 133 | } 134 | 135 | friend void* asio_handler_allocate(std::size_t size, 136 | connect_coro* this_handler) 137 | { 138 | using boost::asio::asio_handler_allocate; 139 | return asio_handler_allocate(size, &this_handler->handler_); 140 | } 141 | 142 | friend void asio_handler_deallocate(void* pointer, std::size_t size, 143 | connect_coro* this_handler) 144 | { 145 | using boost::asio::asio_handler_deallocate; 146 | asio_handler_deallocate(pointer, size, &this_handler->handler_); 147 | } 148 | 149 | template 150 | friend void asio_handler_invoke(Function& function, 151 | connect_coro* this_handler) 152 | { 153 | using boost::asio::asio_handler_invoke; 154 | asio_handler_invoke(function, &this_handler->handler_); 155 | } 156 | 157 | template 158 | friend void asio_handler_invoke(const Function& function, 159 | connect_coro* this_handler) 160 | { 161 | using boost::asio::asio_handler_invoke; 162 | asio_handler_invoke(function, &this_handler->handler_); 163 | } 164 | 165 | private: 166 | Handler handler_; 167 | boost::asio::ip::tcp::socket::lowest_layer_type& socket_; 168 | boost::asio::ip::tcp::resolver& resolver_; 169 | boost::asio::ip::tcp::resolver::iterator iter_; 170 | boost::asio::ip::tcp::endpoint endpoint_; 171 | }; 172 | 173 | template 174 | void async_connect(boost::asio::ip::tcp::socket::lowest_layer_type& socket, 175 | boost::asio::ip::tcp::resolver& resolver, const url& u, Handler handler) 176 | { 177 | std::ostringstream port_string; 178 | port_string << u.port(); 179 | boost::asio::ip::tcp::resolver::query query(u.host(), port_string.str()); 180 | connect_coro(handler, socket, resolver)( 181 | boost::system::error_code(), &query); 182 | } 183 | 184 | } // namespace detail 185 | } // namespace urdl 186 | 187 | #include "urdl/detail/abi_suffix.hpp" 188 | 189 | #endif // URDL_DETAIL_CONNECT_HPP 190 | -------------------------------------------------------------------------------- /include/urdl/detail/coroutine.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // coroutine.hpp 3 | // ~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_DETAIL_COROUTINE_HPP 12 | #define URDL_DETAIL_COROUTINE_HPP 13 | 14 | #include "urdl/detail/abi_prefix.hpp" 15 | 16 | namespace urdl { 17 | namespace detail { 18 | 19 | class coroutine 20 | { 21 | protected: 22 | coroutine() : coro_value_(0) {} 23 | int coro_value_; 24 | }; 25 | 26 | #define URDL_CORO_BEGIN \ 27 | switch (this->coroutine::coro_value_) \ 28 | { \ 29 | case 0: 30 | 31 | #define URDL_CORO_YIELD_IMPL(s,n) \ 32 | do \ 33 | { \ 34 | this->coroutine::coro_value_ = n; \ 35 | s; \ 36 | return; \ 37 | case n: \ 38 | ; \ 39 | } while (0) 40 | 41 | #if defined(_MSC_VER) 42 | # define URDL_CORO_YIELD(s) URDL_CORO_YIELD_IMPL(s, __COUNTER__ + 1) 43 | #else // defined(_MSC_VER) 44 | # define URDL_CORO_YIELD(s) URDL_CORO_YIELD_IMPL(s, __LINE__) 45 | #endif // defined(_MSC_VER) 46 | 47 | #define URDL_CORO_END \ 48 | } 49 | 50 | } // namespace detail 51 | } // namespace urdl 52 | 53 | #include "urdl/detail/abi_suffix.hpp" 54 | 55 | #endif // URDL_DETAIL_COROUTINE_HPP 56 | -------------------------------------------------------------------------------- /include/urdl/detail/file_read_stream.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // file_read_stream.hpp 3 | // ~~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_DETAIL_FILE_READ_STREAM_HPP 12 | #define URDL_DETAIL_FILE_READ_STREAM_HPP 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "urdl/option_set.hpp" 21 | #include "urdl/url.hpp" 22 | 23 | #include "urdl/detail/abi_prefix.hpp" 24 | 25 | namespace urdl { 26 | namespace detail { 27 | 28 | class file_read_stream 29 | { 30 | public: 31 | explicit file_read_stream(boost::asio::io_service& io_service, 32 | option_set& options) 33 | : io_service_(io_service), 34 | options_(options) 35 | { 36 | } 37 | 38 | boost::system::error_code open(const url& u, boost::system::error_code& ec) 39 | { 40 | file_.clear(); 41 | std::string path = u.path(); 42 | #if defined(BOOST_WINDOWS) 43 | if (path.length() >= 3 && path[0] == '/' 44 | && std::isalpha(path[1]) && path[2] == ':') 45 | path = path.substr(1); 46 | #endif // defined(BOOST_WINDOWS) 47 | file_.open(path.c_str(), std::ios_base::in | std::ios_base::binary); 48 | if (!file_) 49 | { 50 | ec = make_error_code(boost::system::errc::no_such_file_or_directory); 51 | return ec; 52 | } 53 | ec = boost::system::error_code(); 54 | return ec; 55 | } 56 | 57 | template 58 | void async_open(const url& u, Handler handler) 59 | { 60 | boost::system::error_code ec; 61 | open(u, ec); 62 | io_service_.post(boost::asio::detail::bind_handler(handler, ec)); 63 | } 64 | 65 | boost::system::error_code close(boost::system::error_code& ec) 66 | { 67 | file_.close(); 68 | file_.clear(); 69 | ec = boost::system::error_code(); 70 | return ec; 71 | } 72 | 73 | bool is_open() const 74 | { 75 | // Some older versions of libstdc++ have a non-const is_open(). 76 | return const_cast(file_).is_open(); 77 | } 78 | 79 | template 80 | std::size_t read_some(const MutableBufferSequence& buffers, 81 | boost::system::error_code& ec) 82 | { 83 | if (!file_) 84 | { 85 | ec = boost::asio::error::eof; 86 | return 0; 87 | } 88 | 89 | typename MutableBufferSequence::const_iterator iter = buffers.begin(); 90 | typename MutableBufferSequence::const_iterator end = buffers.end(); 91 | for (; iter != end; ++iter) 92 | { 93 | boost::asio::mutable_buffer buffer(*iter); 94 | size_t length = boost::asio::buffer_size(buffer); 95 | if (length > 0) 96 | { 97 | file_.read(boost::asio::buffer_cast(buffer), length); 98 | length = file_.gcount(); 99 | if (length == 0 && !file_) 100 | ec = boost::asio::error::eof; 101 | return length; 102 | } 103 | } 104 | 105 | ec = boost::system::error_code(); 106 | return 0; 107 | } 108 | 109 | template 110 | void async_read_some(const MutableBufferSequence& buffers, Handler handler) 111 | { 112 | boost::system::error_code ec; 113 | std::size_t bytes_transferred = read_some(buffers, ec); 114 | io_service_.post(boost::asio::detail::bind_handler( 115 | handler, ec, bytes_transferred)); 116 | } 117 | 118 | private: 119 | boost::asio::io_service& io_service_; 120 | option_set& options_; 121 | std::ifstream file_; 122 | }; 123 | 124 | } // namespace detail 125 | } // namespace urdl 126 | 127 | #include "urdl/detail/abi_suffix.hpp" 128 | 129 | #endif // URDL_DETAIL_FILE_READ_STREAM_HPP 130 | -------------------------------------------------------------------------------- /include/urdl/detail/handshake.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // handshake.hpp 3 | // ~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_DETAIL_HANDSHAKE_HPP 12 | #define URDL_DETAIL_HANDSHAKE_HPP 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "urdl/detail/coroutine.hpp" 20 | 21 | #if !defined(URDL_DISABLE_SSL) 22 | # include 23 | # include 24 | #endif // !defined(URDL_DISABLE_SSL) 25 | 26 | #include "urdl/detail/abi_prefix.hpp" 27 | 28 | namespace urdl { 29 | namespace detail { 30 | 31 | inline boost::system::error_code handshake( 32 | boost::asio::ip::tcp::socket& /*socket*/, 33 | const std::string& /*host*/, boost::system::error_code& ec) 34 | { 35 | ec = boost::system::error_code(); 36 | return ec; 37 | } 38 | 39 | template 40 | void async_handshake(boost::asio::ip::tcp::socket& socket, 41 | const std::string& /*host*/, Handler handler) 42 | { 43 | boost::system::error_code ec; 44 | socket.get_io_service().post(boost::asio::detail::bind_handler(handler, ec)); 45 | } 46 | 47 | #if !defined(URDL_DISABLE_SSL) 48 | inline bool match_pattern(const char* pattern, 49 | std::size_t pattern_length, const char* host) 50 | { 51 | const char* p = pattern; 52 | const char* p_end = p + pattern_length; 53 | const char* h = host; 54 | 55 | while (p != p_end && *h) 56 | { 57 | if (*p == '*') 58 | { 59 | ++p; 60 | while (*h && *h != '.') 61 | if (match_pattern(p, p_end - p, h++)) 62 | return true; 63 | } 64 | else if (std::tolower(*p) == std::tolower(*h)) 65 | { 66 | ++p; 67 | ++h; 68 | } 69 | else 70 | { 71 | return false; 72 | } 73 | } 74 | 75 | return p == p_end && !*h; 76 | } 77 | 78 | inline bool certificate_matches_host(X509* cert, const std::string& host) 79 | { 80 | // Try converting host name to an address. If it is an address then we need 81 | // to look for an IP address in the certificate rather than a host name. 82 | boost::system::error_code ec; 83 | boost::asio::ip::address address 84 | = boost::asio::ip::address::from_string(host, ec); 85 | bool is_address = !ec; 86 | 87 | // Go through the alternate names in the certificate looking for matching DNS 88 | // or IP address entries. 89 | GENERAL_NAMES* gens = static_cast( 90 | X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0)); 91 | for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) 92 | { 93 | GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); 94 | if (gen->type == GEN_DNS && !is_address) 95 | { 96 | ASN1_IA5STRING* domain = gen->d.dNSName; 97 | if (domain->type == V_ASN1_IA5STRING 98 | && domain->data && domain->length) 99 | { 100 | const char* pattern = reinterpret_cast(domain->data); 101 | std::size_t pattern_length = domain->length; 102 | if (match_pattern(pattern, pattern_length, host.c_str())) 103 | { 104 | GENERAL_NAMES_free(gens); 105 | return true; 106 | } 107 | } 108 | } 109 | else if (gen->type == GEN_IPADD && is_address) 110 | { 111 | ASN1_OCTET_STRING* ip_address = gen->d.iPAddress; 112 | if (ip_address->type == V_ASN1_OCTET_STRING && ip_address->data) 113 | { 114 | if (address.is_v4() && ip_address->length == 4) 115 | { 116 | boost::asio::ip::address_v4::bytes_type address_bytes 117 | = address.to_v4().to_bytes(); 118 | if (std::memcmp(address_bytes.data(), ip_address->data, 4) == 0) 119 | { 120 | GENERAL_NAMES_free(gens); 121 | return true; 122 | } 123 | } 124 | else if (address.is_v6() && ip_address->length == 16) 125 | { 126 | boost::asio::ip::address_v6::bytes_type address_bytes 127 | = address.to_v6().to_bytes(); 128 | if (std::memcmp(address_bytes.data(), ip_address->data, 16) == 0) 129 | { 130 | GENERAL_NAMES_free(gens); 131 | return true; 132 | } 133 | } 134 | } 135 | } 136 | } 137 | GENERAL_NAMES_free(gens); 138 | 139 | // No match in the alternate names, so try the common names. We should only 140 | // use the "most specific" common name, which is the last one in the list. 141 | X509_NAME* name = X509_get_subject_name(cert); 142 | int i = -1; 143 | ASN1_STRING* common_name = 0; 144 | while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) 145 | { 146 | X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i); 147 | common_name = X509_NAME_ENTRY_get_data(name_entry); 148 | } 149 | if (common_name && common_name->data && common_name->length) 150 | { 151 | const char* pattern = reinterpret_cast(common_name->data); 152 | std::size_t pattern_length = common_name->length; 153 | if (match_pattern(pattern, pattern_length, host.c_str())) 154 | return true; 155 | } 156 | 157 | return false; 158 | } 159 | 160 | inline boost::system::error_code handshake( 161 | boost::asio::ssl::stream& socket, 162 | const std::string& host, boost::system::error_code& ec) 163 | { 164 | // Perform SSL handshake. 165 | socket.handshake(boost::asio::ssl::stream_base::client, ec); 166 | if (ec) 167 | return ec; 168 | 169 | // Verify the certificate returned by the host. 170 | if (X509* cert = SSL_get_peer_certificate(socket.impl()->ssl)) 171 | { 172 | if (SSL_get_verify_result(socket.impl()->ssl) == X509_V_OK) 173 | { 174 | if (certificate_matches_host(cert, host)) 175 | ec = boost::system::error_code(); 176 | else 177 | ec = make_error_code(boost::system::errc::permission_denied); 178 | } 179 | else 180 | ec = make_error_code(boost::system::errc::permission_denied); 181 | X509_free(cert); 182 | } 183 | else 184 | ec = make_error_code(boost::system::errc::permission_denied); 185 | 186 | return ec; 187 | } 188 | 189 | template 190 | class handshake_coro : coroutine 191 | { 192 | public: 193 | handshake_coro(Handler handler, 194 | boost::asio::ssl::stream& socket, 195 | const std::string& host) 196 | : handler_(handler), 197 | socket_(socket), 198 | host_(host) 199 | { 200 | } 201 | 202 | void operator()(boost::system::error_code ec) 203 | { 204 | URDL_CORO_BEGIN; 205 | 206 | // Perform SSL handshake. 207 | URDL_CORO_YIELD(socket_.async_handshake( 208 | boost::asio::ssl::stream_base::client, *this)); 209 | if (ec) 210 | { 211 | handler_(ec); 212 | return; 213 | } 214 | 215 | // Verify the certificate returned by the host. 216 | if (X509* cert = SSL_get_peer_certificate(socket_.impl()->ssl)) 217 | { 218 | if (SSL_get_verify_result(socket_.impl()->ssl) == X509_V_OK) 219 | { 220 | if (certificate_matches_host(cert, host_)) 221 | ec = boost::system::error_code(); 222 | else 223 | ec = make_error_code(boost::system::errc::permission_denied); 224 | } 225 | else 226 | ec = make_error_code(boost::system::errc::permission_denied); 227 | X509_free(cert); 228 | } 229 | else 230 | ec = make_error_code(boost::system::errc::permission_denied); 231 | 232 | handler_(ec); 233 | 234 | URDL_CORO_END; 235 | } 236 | 237 | friend void* asio_handler_allocate(std::size_t size, 238 | handshake_coro* this_handler) 239 | { 240 | using boost::asio::asio_handler_allocate; 241 | return asio_handler_allocate(size, &this_handler->handler_); 242 | } 243 | 244 | friend void asio_handler_deallocate(void* pointer, std::size_t size, 245 | handshake_coro* this_handler) 246 | { 247 | using boost::asio::asio_handler_deallocate; 248 | asio_handler_deallocate(pointer, size, &this_handler->handler_); 249 | } 250 | 251 | template 252 | friend void asio_handler_invoke(Function& function, 253 | handshake_coro* this_handler) 254 | { 255 | using boost::asio::asio_handler_invoke; 256 | asio_handler_invoke(function, &this_handler->handler_); 257 | } 258 | 259 | template 260 | friend void asio_handler_invoke(const Function& function, 261 | handshake_coro* this_handler) 262 | { 263 | using boost::asio::asio_handler_invoke; 264 | asio_handler_invoke(function, &this_handler->handler_); 265 | } 266 | 267 | private: 268 | Handler handler_; 269 | boost::asio::ssl::stream& socket_; 270 | std::string host_; 271 | }; 272 | 273 | template 274 | void async_handshake( 275 | boost::asio::ssl::stream& socket, 276 | const std::string& host, Handler handler) 277 | { 278 | handshake_coro(handler, socket, host)(boost::system::error_code()); 279 | } 280 | #endif // !defined(URDL_DISABLE_SSL) 281 | 282 | } // namespace detail 283 | } // namespace urdl 284 | 285 | #include "urdl/detail/abi_suffix.hpp" 286 | 287 | #endif // URDL_DETAIL_HANDSHAKE_HPP 288 | -------------------------------------------------------------------------------- /include/urdl/detail/http_read_stream.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // http_read_stream.hpp 3 | // ~~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_DETAIL_HTTP_READ_STREAM_HPP 12 | #define URDL_DETAIL_HTTP_READ_STREAM_HPP 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "urdl/http.hpp" 26 | #include "urdl/option_set.hpp" 27 | #include "urdl/url.hpp" 28 | #include "urdl/detail/connect.hpp" 29 | #include "urdl/detail/coroutine.hpp" 30 | #include "urdl/detail/handshake.hpp" 31 | #include "urdl/detail/parsers.hpp" 32 | 33 | #include "urdl/detail/abi_prefix.hpp" 34 | 35 | namespace urdl { 36 | namespace detail { 37 | 38 | template 39 | class http_read_stream 40 | { 41 | public: 42 | explicit http_read_stream(boost::asio::io_service& io_service, 43 | option_set& options) 44 | : resolver_(io_service), 45 | socket_(io_service), 46 | options_(options), 47 | content_length_(0) 48 | { 49 | } 50 | 51 | template 52 | http_read_stream(boost::asio::io_service& io_service, 53 | option_set& options, Arg& arg) 54 | : resolver_(io_service), 55 | socket_(io_service, arg), 56 | options_(options), 57 | content_length_(0) 58 | { 59 | } 60 | 61 | boost::system::error_code open(const url& u, boost::system::error_code& ec) 62 | { 63 | // Fail if the socket is already open. 64 | if (socket_.lowest_layer().is_open()) 65 | { 66 | ec = boost::asio::error::already_open; 67 | return ec; 68 | } 69 | 70 | // Establish a connection to the HTTP server. 71 | connect(socket_.lowest_layer(), resolver_, u, ec); 72 | if (ec) 73 | return ec; 74 | 75 | // Perform SSL handshake if required. 76 | handshake(socket_, u.host(), ec); 77 | if (ec) 78 | return ec; 79 | 80 | // Get the HTTP options used to build the request. 81 | std::string request_method 82 | = options_.get_option().value(); 83 | std::string request_content 84 | = options_.get_option().value(); 85 | std::string request_content_type 86 | = options_.get_option().value(); 87 | std::string user_agent 88 | = options_.get_option().value(); 89 | 90 | // Form the request. We specify the "Connection: close" header so that the 91 | // server will close the socket after transmitting the response. This will 92 | // allow us to treat all data up until the EOF as the content. 93 | std::ostream request_stream(&request_buffer_); 94 | request_stream << request_method << " "; 95 | request_stream << u.to_string(url::path_component | url::query_component); 96 | request_stream << " HTTP/1.0\r\n"; 97 | request_stream << "Host: "; 98 | request_stream << u.to_string(url::host_component | url::port_component); 99 | request_stream << "\r\n"; 100 | request_stream << "Accept: */*\r\n"; 101 | if (request_content.length()) 102 | { 103 | request_stream << "Content-Length: "; 104 | request_stream << request_content.length() << "\r\n"; 105 | if (request_content_type.length()) 106 | { 107 | request_stream << "Content-Type: "; 108 | request_stream << request_content_type << "\r\n"; 109 | } 110 | } 111 | if (user_agent.length()) 112 | request_stream << "User-Agent: " << user_agent << "\r\n"; 113 | request_stream << "Connection: close\r\n\r\n"; 114 | request_stream << request_content; 115 | 116 | // Send the request. 117 | boost::asio::write(socket_, request_buffer_, 118 | boost::asio::transfer_all(), ec); 119 | if (ec) 120 | return ec; 121 | 122 | int status_code = 0; 123 | for (;;) 124 | { 125 | // Read the reply status line. 126 | boost::asio::read_until(socket_, reply_buffer_, "\r\n", ec); 127 | if (ec) 128 | return ec; 129 | 130 | // Extract the response code from the status line. 131 | int version_major = 0; 132 | int version_minor = 0; 133 | if (!parse_http_status_line( 134 | std::istreambuf_iterator(&reply_buffer_), 135 | std::istreambuf_iterator(), 136 | version_major, version_minor, status_code)) 137 | { 138 | ec = http::errc::malformed_status_line; 139 | return ec; 140 | } 141 | 142 | // A "continue" header means we need to keep waiting. 143 | if (status_code != http::errc::continue_request) 144 | break; 145 | } 146 | 147 | // Read list of headers and save them. If there's anything left in the reply 148 | // buffer afterwards, it's the start of the content returned by the HTTP 149 | // server. 150 | std::size_t bytes_transferred = boost::asio::read_until( 151 | socket_, reply_buffer_, "\r\n\r\n", ec); 152 | headers_.resize(bytes_transferred); 153 | reply_buffer_.sgetn(&headers_[0], bytes_transferred); 154 | if (ec) 155 | return ec; 156 | 157 | // Parse the headers to get Content-Type and Content-Length. 158 | if (!parse_http_headers(headers_.begin(), headers_.end(), 159 | content_type_, content_length_, location_)) 160 | { 161 | ec = http::errc::malformed_response_headers; 162 | return ec; 163 | } 164 | 165 | // Check the response code to see if we got the page correctly. 166 | if (status_code != http::errc::ok) 167 | ec = make_error_code(static_cast(status_code)); 168 | 169 | return ec; 170 | } 171 | 172 | template 173 | class open_coro : coroutine 174 | { 175 | public: 176 | open_coro(Handler handler, boost::asio::ip::tcp::resolver& resolver, 177 | Stream& socket, const option_set& options, 178 | boost::asio::streambuf& request_buffer, 179 | boost::asio::streambuf& reply_buffer, const url& u, 180 | std::string& headers, std::string& content_type, 181 | std::size_t& content_length, std::string& location) 182 | : handler_(handler), 183 | resolver_(resolver), 184 | socket_(socket), 185 | options_(options), 186 | request_buffer_(request_buffer), 187 | reply_buffer_(reply_buffer), 188 | url_(u), 189 | headers_(headers), 190 | status_code_(0), 191 | content_type_(content_type), 192 | content_length_(content_length), 193 | location_(location) 194 | { 195 | } 196 | 197 | void operator()(boost::system::error_code ec, 198 | std::size_t bytes_transferred = 0) 199 | { 200 | URDL_CORO_BEGIN; 201 | 202 | // Fail if the socket is already open. 203 | if (socket_.lowest_layer().is_open()) 204 | { 205 | ec = boost::asio::error::already_open; 206 | URDL_CORO_YIELD(socket_.get_io_service().post( 207 | boost::asio::detail::bind_handler(*this, ec))); 208 | handler_(ec); 209 | return; 210 | } 211 | 212 | // Establish a connection to the HTTP server. 213 | URDL_CORO_YIELD(async_connect(socket_.lowest_layer(), 214 | resolver_, url_, *this)); 215 | if (ec) 216 | { 217 | handler_(ec); 218 | return; 219 | } 220 | 221 | // Perform SSL handshake if required. 222 | URDL_CORO_YIELD(async_handshake(socket_, url_.host(), *this)); 223 | if (ec) 224 | { 225 | handler_(ec); 226 | return; 227 | } 228 | 229 | { 230 | // Get the HTTP options used to build the request. 231 | std::string request_method 232 | = options_.get_option().value(); 233 | std::string request_content 234 | = options_.get_option().value(); 235 | std::string request_content_type 236 | = options_.get_option().value(); 237 | std::string user_agent 238 | = options_.get_option().value(); 239 | 240 | // Form the request. We specify the "Connection: close" header so that 241 | // the server will close the socket after transmitting the response. 242 | // This will allow us to treat all data up until the EOF as the 243 | // content. 244 | std::ostream request_stream(&request_buffer_); 245 | request_stream << request_method << " "; 246 | request_stream << url_.to_string( 247 | url::path_component | url::query_component); 248 | request_stream << " HTTP/1.0\r\n"; 249 | request_stream << "Host: "; 250 | request_stream << url_.to_string( 251 | url::host_component | url::port_component); 252 | request_stream << "\r\n"; 253 | request_stream << "Accept: */*\r\n"; 254 | if (request_content.length()) 255 | { 256 | request_stream << "Content-Length: "; 257 | request_stream << request_content.length() << "\r\n"; 258 | if (request_content_type.length()) 259 | { 260 | request_stream << "Content-Type: "; 261 | request_stream << request_content_type << "\r\n"; 262 | } 263 | } 264 | if (user_agent.length()) 265 | request_stream << "User-Agent: " << user_agent << "\r\n"; 266 | request_stream << "Connection: close\r\n\r\n"; 267 | request_stream << request_content; 268 | } 269 | 270 | // Send the request. 271 | URDL_CORO_YIELD(boost::asio::async_write(socket_, 272 | request_buffer_, boost::asio::transfer_all(), *this)); 273 | if (ec) 274 | { 275 | handler_(ec); 276 | return; 277 | } 278 | 279 | for (;;) 280 | { 281 | // Read the reply status line. 282 | URDL_CORO_YIELD(boost::asio::async_read_until(socket_, 283 | reply_buffer_, "\r\n", *this)); 284 | if (ec) 285 | { 286 | handler_(ec); 287 | return; 288 | } 289 | 290 | // Check the response code to see if we got the page correctly. 291 | { 292 | int version_major = 0; 293 | int version_minor = 0; 294 | if (!parse_http_status_line( 295 | std::istreambuf_iterator(&reply_buffer_), 296 | std::istreambuf_iterator(), 297 | version_major, version_minor, status_code_)) 298 | { 299 | ec = http::errc::malformed_status_line; 300 | handler_(ec); 301 | return; 302 | } 303 | } 304 | 305 | // A "continue" header means we need to keep waiting. 306 | if (status_code_ != http::errc::continue_request) 307 | break; 308 | } 309 | 310 | // Read list of headers and save them. If there's anything left in the 311 | // reply buffer afterwards, it's the start of the content returned by the 312 | // HTTP server. 313 | URDL_CORO_YIELD(boost::asio::async_read_until(socket_, 314 | reply_buffer_, "\r\n\r\n", *this)); 315 | headers_.resize(bytes_transferred); 316 | reply_buffer_.sgetn(&headers_[0], bytes_transferred); 317 | if (ec) 318 | { 319 | handler_(ec); 320 | return; 321 | } 322 | 323 | // Parse the headers to get Content-Type and Content-Length. 324 | if (!parse_http_headers(headers_.begin(), headers_.end(), 325 | content_type_, content_length_, location_)) 326 | { 327 | ec = http::errc::malformed_response_headers; 328 | handler_(ec); 329 | return; 330 | } 331 | 332 | // Check the response code to see if we got the page correctly. 333 | if (status_code_ != http::errc::ok) 334 | ec = make_error_code(static_cast(status_code_)); 335 | 336 | handler_(ec); 337 | 338 | URDL_CORO_END; 339 | } 340 | 341 | friend void* asio_handler_allocate(std::size_t size, 342 | open_coro* this_handler) 343 | { 344 | using boost::asio::asio_handler_allocate; 345 | return asio_handler_allocate(size, &this_handler->handler_); 346 | } 347 | 348 | friend void asio_handler_deallocate(void* pointer, std::size_t size, 349 | open_coro* this_handler) 350 | { 351 | using boost::asio::asio_handler_deallocate; 352 | asio_handler_deallocate(pointer, size, &this_handler->handler_); 353 | } 354 | 355 | template 356 | friend void asio_handler_invoke(Function& function, 357 | open_coro* this_handler) 358 | { 359 | using boost::asio::asio_handler_invoke; 360 | asio_handler_invoke(function, &this_handler->handler_); 361 | } 362 | 363 | template 364 | friend void asio_handler_invoke(const Function& function, 365 | open_coro* this_handler) 366 | { 367 | using boost::asio::asio_handler_invoke; 368 | asio_handler_invoke(function, &this_handler->handler_); 369 | } 370 | 371 | private: 372 | Handler handler_; 373 | boost::asio::ip::tcp::resolver& resolver_; 374 | Stream& socket_; 375 | const option_set& options_; 376 | boost::asio::streambuf& request_buffer_; 377 | boost::asio::streambuf& reply_buffer_; 378 | url url_; 379 | std::string& headers_; 380 | int status_code_; 381 | std::string& content_type_; 382 | std::size_t& content_length_; 383 | std::string& location_; 384 | }; 385 | 386 | template 387 | void async_open(const url& u, Handler handler) 388 | { 389 | open_coro(handler, resolver_, socket_, options_, request_buffer_, 390 | reply_buffer_, u, headers_, content_type_, content_length_, location_)( 391 | boost::system::error_code(), 0); 392 | } 393 | 394 | boost::system::error_code close(boost::system::error_code& ec) 395 | { 396 | resolver_.cancel(); 397 | if (!socket_.lowest_layer().close(ec)) 398 | { 399 | request_buffer_.consume(request_buffer_.size()); 400 | reply_buffer_.consume(reply_buffer_.size()); 401 | headers_.clear(); 402 | content_type_.clear(); 403 | content_length_ = 0; 404 | location_.clear(); 405 | } 406 | return ec; 407 | } 408 | 409 | bool is_open() const 410 | { 411 | return socket_.lowest_layer().is_open(); 412 | } 413 | 414 | std::string content_type() const 415 | { 416 | return content_type_; 417 | } 418 | 419 | std::size_t content_length() const 420 | { 421 | return content_length_; 422 | } 423 | 424 | std::string location() const 425 | { 426 | return location_; 427 | } 428 | 429 | std::string headers() const 430 | { 431 | return headers_; 432 | } 433 | 434 | template 435 | std::size_t read_some(const MutableBufferSequence& buffers, 436 | boost::system::error_code& ec) 437 | { 438 | // If we have any data in the reply_buffer_, return that first. 439 | if (reply_buffer_.size() > 0) 440 | { 441 | std::size_t bytes_transferred = 0; 442 | typename MutableBufferSequence::const_iterator iter = buffers.begin(); 443 | typename MutableBufferSequence::const_iterator end = buffers.end(); 444 | for (; iter != end && reply_buffer_.size() > 0; ++iter) 445 | { 446 | boost::asio::mutable_buffer buffer(*iter); 447 | size_t length = boost::asio::buffer_size(buffer); 448 | if (length > 0) 449 | { 450 | bytes_transferred += reply_buffer_.sgetn( 451 | boost::asio::buffer_cast(buffer), length); 452 | } 453 | } 454 | ec = boost::system::error_code(); 455 | return bytes_transferred; 456 | } 457 | 458 | // Otherwise we forward the call to the underlying socket. 459 | std::size_t bytes_transferred = socket_.read_some(buffers, ec); 460 | if (ec == boost::asio::error::shut_down) 461 | ec = boost::asio::error::eof; 462 | return bytes_transferred; 463 | } 464 | 465 | template 466 | class read_handler 467 | { 468 | public: 469 | read_handler(Handler handler) 470 | : handler_(handler) 471 | { 472 | } 473 | 474 | void operator()(boost::system::error_code ec, std::size_t bytes_transferred) 475 | { 476 | if (ec == boost::asio::error::shut_down) 477 | ec = boost::asio::error::eof; 478 | handler_(ec, bytes_transferred); 479 | } 480 | 481 | friend void* asio_handler_allocate(std::size_t size, 482 | read_handler* this_handler) 483 | { 484 | using boost::asio::asio_handler_allocate; 485 | return asio_handler_allocate(size, &this_handler->handler_); 486 | } 487 | 488 | friend void asio_handler_deallocate(void* pointer, std::size_t size, 489 | read_handler* this_handler) 490 | { 491 | using boost::asio::asio_handler_deallocate; 492 | asio_handler_deallocate(pointer, size, &this_handler->handler_); 493 | } 494 | 495 | template 496 | friend void asio_handler_invoke(Function& function, 497 | read_handler* this_handler) 498 | { 499 | using boost::asio::asio_handler_invoke; 500 | asio_handler_invoke(function, &this_handler->handler_); 501 | } 502 | 503 | template 504 | friend void asio_handler_invoke(const Function& function, 505 | read_handler* this_handler) 506 | { 507 | using boost::asio::asio_handler_invoke; 508 | asio_handler_invoke(function, &this_handler->handler_); 509 | } 510 | 511 | private: 512 | Handler handler_; 513 | }; 514 | 515 | template 516 | void async_read_some(const MutableBufferSequence& buffers, Handler handler) 517 | { 518 | // If we have any data in the reply_buffer_, return that first. 519 | if (reply_buffer_.size() > 0) 520 | { 521 | boost::system::error_code ec; 522 | std::size_t bytes_transferred = read_some(buffers, ec); 523 | socket_.get_io_service().post(boost::asio::detail::bind_handler( 524 | handler, ec, bytes_transferred)); 525 | return; 526 | } 527 | 528 | // Otherwise we forward the call to the underlying socket. 529 | socket_.async_read_some(buffers, read_handler(handler)); 530 | } 531 | 532 | private: 533 | boost::asio::ip::tcp::resolver resolver_; 534 | Stream socket_; 535 | option_set& options_; 536 | boost::asio::streambuf request_buffer_; 537 | boost::asio::streambuf reply_buffer_; 538 | std::string headers_; 539 | std::string content_type_; 540 | std::size_t content_length_; 541 | std::string location_; 542 | }; 543 | 544 | } // namespace detail 545 | } // namespace urdl 546 | 547 | #include "urdl/detail/abi_suffix.hpp" 548 | 549 | #endif // URDL_DETAIL_HTTP_READ_STREAM_HPP 550 | -------------------------------------------------------------------------------- /include/urdl/detail/parsers.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // parsers.hpp 3 | // ~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_DETAIL_PARSERS_HPP 12 | #define URDL_DETAIL_PARSERS_HPP 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "urdl/detail/abi_prefix.hpp" 20 | 21 | namespace urdl { 22 | namespace detail { 23 | 24 | inline bool is_char(int c) 25 | { 26 | return c >= 0 && c <= 127; 27 | } 28 | 29 | inline bool is_ctl(int c) 30 | { 31 | return (c >= 0 && c <= 31) || c == 127; 32 | } 33 | 34 | inline bool is_tspecial(int c) 35 | { 36 | switch (c) 37 | { 38 | case '(': case ')': case '<': case '>': case '@': 39 | case ',': case ';': case ':': case '\\': case '"': 40 | case '/': case '[': case ']': case '?': case '=': 41 | case '{': case '}': case ' ': case '\t': 42 | return true; 43 | default: 44 | return false; 45 | } 46 | } 47 | 48 | inline bool is_digit(int c) 49 | { 50 | return c >= '0' && c <= '9'; 51 | } 52 | 53 | inline bool tolower_compare(char a, char b) 54 | { 55 | return std::tolower(a) == std::tolower(b); 56 | } 57 | 58 | inline bool headers_equal(const std::string& a, const std::string& b) 59 | { 60 | if (a.length() != b.length()) 61 | return false; 62 | return std::equal(a.begin(), a.end(), b.begin(), tolower_compare); 63 | } 64 | 65 | inline void check_header(const std::string& name, const std::string& value, 66 | std::string& content_type, std::size_t& content_length, 67 | std::string& location) 68 | { 69 | if (headers_equal(name, "Content-Type")) 70 | content_type = value; 71 | else if (headers_equal(name, "Content-Length")) 72 | content_length = std::atoi(value.c_str()); 73 | else if (headers_equal(name, "Location")) 74 | location = value; 75 | } 76 | 77 | template 78 | bool parse_http_status_line(Iterator begin, Iterator end, 79 | int& version_major, int& version_minor, int& status) 80 | { 81 | enum 82 | { 83 | http_version_h, 84 | http_version_t_1, 85 | http_version_t_2, 86 | http_version_p, 87 | http_version_slash, 88 | http_version_major_start, 89 | http_version_major, 90 | http_version_minor_start, 91 | http_version_minor, 92 | status_code_start, 93 | status_code, 94 | reason_phrase, 95 | linefeed, 96 | fail 97 | } state = http_version_h; 98 | 99 | Iterator iter = begin; 100 | std::string reason; 101 | while (iter != end && state != fail) 102 | { 103 | char c = *iter++; 104 | switch (state) 105 | { 106 | case http_version_h: 107 | state = (c == 'H') ? http_version_t_1 : fail; 108 | break; 109 | case http_version_t_1: 110 | state = (c == 'T') ? http_version_t_2 : fail; 111 | break; 112 | case http_version_t_2: 113 | state = (c == 'T') ? http_version_p : fail; 114 | break; 115 | case http_version_p: 116 | state = (c == 'P') ? http_version_slash : fail; 117 | break; 118 | case http_version_slash: 119 | state = (c == '/') ? http_version_major_start : fail; 120 | break; 121 | case http_version_major_start: 122 | if (is_digit(c)) 123 | { 124 | version_major = version_major * 10 + c - '0'; 125 | state = http_version_major; 126 | } 127 | else 128 | state = fail; 129 | break; 130 | case http_version_major: 131 | if (c == '.') 132 | state = http_version_minor_start; 133 | else if (is_digit(c)) 134 | version_major = version_major * 10 + c - '0'; 135 | else 136 | state = fail; 137 | break; 138 | case http_version_minor_start: 139 | if (is_digit(c)) 140 | { 141 | version_minor = version_minor * 10 + c - '0'; 142 | state = http_version_minor; 143 | } 144 | else 145 | state = fail; 146 | break; 147 | case http_version_minor: 148 | if (c == ' ') 149 | state = status_code_start; 150 | else if (is_digit(c)) 151 | version_minor = version_minor * 10 + c - '0'; 152 | else 153 | state = fail; 154 | break; 155 | case status_code_start: 156 | if (is_digit(c)) 157 | { 158 | status = status * 10 + c - '0'; 159 | state = status_code; 160 | } 161 | else 162 | state = fail; 163 | break; 164 | case status_code: 165 | if (c == ' ') 166 | state = reason_phrase; 167 | else if (is_digit(c)) 168 | status = status * 10 + c - '0'; 169 | else 170 | state = fail; 171 | break; 172 | case reason_phrase: 173 | if (c == '\r') 174 | state = linefeed; 175 | else if (is_ctl(c)) 176 | state = fail; 177 | else 178 | reason.push_back(c); 179 | break; 180 | case linefeed: 181 | return (c == '\n'); 182 | default: 183 | return false; 184 | } 185 | } 186 | return false; 187 | } 188 | 189 | template 190 | bool parse_http_headers(Iterator begin, Iterator end, 191 | std::string& content_type, std::size_t& content_length, 192 | std::string& location) 193 | { 194 | enum 195 | { 196 | first_header_line_start, 197 | header_line_start, 198 | header_lws, 199 | header_name, 200 | space_before_header_value, 201 | header_value, 202 | linefeed, 203 | final_linefeed, 204 | fail 205 | } state = first_header_line_start; 206 | 207 | Iterator iter = begin; 208 | std::string reason; 209 | std::string name; 210 | std::string value; 211 | while (iter != end && state != fail) 212 | { 213 | char c = *iter++; 214 | switch (state) 215 | { 216 | case first_header_line_start: 217 | if (c == '\r') 218 | state = final_linefeed; 219 | else if (!is_char(c) || is_ctl(c) || is_tspecial(c)) 220 | state = fail; 221 | else 222 | { 223 | name.push_back(c); 224 | state = header_name; 225 | } 226 | break; 227 | case header_line_start: 228 | if (c == '\r') 229 | { 230 | check_header(name, value, content_type, content_length, location); 231 | name.clear(); 232 | value.clear(); 233 | state = final_linefeed; 234 | } 235 | else if (c == ' ' || c == '\t') 236 | state = header_lws; 237 | else if (!is_char(c) || is_ctl(c) || is_tspecial(c)) 238 | state = fail; 239 | else 240 | { 241 | check_header(name, value, content_type, content_length, location); 242 | name.clear(); 243 | value.clear(); 244 | name.push_back(c); 245 | state = header_name; 246 | } 247 | break; 248 | case header_lws: 249 | if (c == '\r') 250 | state = linefeed; 251 | else if (c == ' ' || c == '\t') 252 | ; // Discard character. 253 | else if (is_ctl(c)) 254 | state = fail; 255 | else 256 | { 257 | state = header_value; 258 | value.push_back(c); 259 | } 260 | break; 261 | case header_name: 262 | if (c == ':') 263 | state = space_before_header_value; 264 | else if (!is_char(c) || is_ctl(c) || is_tspecial(c)) 265 | state = fail; 266 | else 267 | name.push_back(c); 268 | break; 269 | case space_before_header_value: 270 | state = (c == ' ') ? header_value : fail; 271 | break; 272 | case header_value: 273 | if (c == '\r') 274 | state = linefeed; 275 | else if (is_ctl(c)) 276 | state = fail; 277 | else 278 | value.push_back(c); 279 | break; 280 | case linefeed: 281 | state = (c == '\n') ? header_line_start : fail; 282 | break; 283 | case final_linefeed: 284 | return (c == '\n'); 285 | default: 286 | return false; 287 | } 288 | } 289 | return false; 290 | } 291 | 292 | } // namespace detail 293 | } // namespace urdl 294 | 295 | #include "urdl/detail/abi_suffix.hpp" 296 | 297 | #endif // URDL_DETAIL_PARSERS_HPP 298 | -------------------------------------------------------------------------------- /include/urdl/detail/scoped_ptr.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // detail/scoped_ptr.hpp 3 | // ~~~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_DETAIL_SCOPED_PTR_HPP 12 | #define URDL_DETAIL_SCOPED_PTR_HPP 13 | 14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 15 | # pragma once 16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 17 | 18 | #include "urdl/detail/abi_prefix.hpp" 19 | 20 | namespace urdl { 21 | namespace detail { 22 | 23 | template 24 | class scoped_ptr 25 | { 26 | public: 27 | // Constructor. 28 | explicit scoped_ptr(T* p = 0) 29 | : p_(p) 30 | { 31 | } 32 | 33 | // Destructor. 34 | ~scoped_ptr() 35 | { 36 | delete p_; 37 | } 38 | 39 | // Access. 40 | T* get() const 41 | { 42 | return p_; 43 | } 44 | 45 | // Access. 46 | T* operator->() 47 | { 48 | return p_; 49 | } 50 | 51 | // Dereference. 52 | T& operator*() 53 | { 54 | return *p_; 55 | } 56 | 57 | // Reset pointer. 58 | void reset(T* p = 0) 59 | { 60 | delete p_; 61 | p_ = p; 62 | } 63 | 64 | // Release pointer. 65 | T* release() 66 | { 67 | T* p = p_; 68 | p_ = 0; 69 | return p; 70 | } 71 | 72 | private: 73 | // Disallow copying and assignment. 74 | scoped_ptr(const scoped_ptr&); 75 | scoped_ptr& operator=(const scoped_ptr&); 76 | 77 | T* p_; 78 | }; 79 | 80 | } // namespace detail 81 | } // namespace urdl 82 | 83 | #include "urdl/detail/abi_suffix.hpp" 84 | 85 | #endif // URDL_DETAIL_SCOPED_PTR_HPP 86 | -------------------------------------------------------------------------------- /include/urdl/http.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // http.hpp 3 | // ~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_HTTP_HPP 12 | #define URDL_HTTP_HPP 13 | 14 | #include 15 | #include 16 | #include "urdl/detail/config.hpp" 17 | 18 | #include "urdl/detail/abi_prefix.hpp" 19 | 20 | namespace urdl { 21 | namespace http { 22 | 23 | /// Gets the error category for HTTP errors. 24 | /** 25 | * @returns The @c boost::system::error_category used for HTTP errors. 26 | * 27 | * @par Requirements 28 | * @e Header: @c @n 29 | * @e Namespace: @c urdl::http 30 | */ 31 | URDL_DECL const boost::system::error_category& error_category(); 32 | 33 | /// Option to specify the HTTP request method. 34 | /** 35 | * @par Remarks 36 | * The default request method is "GET". 37 | * 38 | * @par Example 39 | * To set the request method for an object of class @c urdl::istream: 40 | * @code 41 | * urdl::istream is; 42 | * is.set_option(urdl::http::request_method("HEAD")); 43 | * is.open("http://www.boost.org"); 44 | * @endcode 45 | * 46 | * To set the request method for an object of class @c urdl::read_stream: 47 | * @code 48 | * urdl::read_stream stream; 49 | * stream.set_option(urdl::http::request_method("HEAD")); 50 | * stream.open("http://www.boost.org"); 51 | * @endcode 52 | * 53 | * @par Requirements 54 | * @e Header: @c @n 55 | * @e Namespace: @c urdl::http 56 | */ 57 | class request_method 58 | { 59 | public: 60 | /// Constructs an object of class @c request_method. 61 | /** 62 | * @par Remarks 63 | * Postcondition: value() == "GET". 64 | */ 65 | request_method() 66 | : value_("GET") 67 | { 68 | } 69 | 70 | /// Constructs an object of class @c request_method. 71 | /** 72 | * @param v The desired value for the option. 73 | * 74 | * @par Remarks 75 | * Postcondition: value() == v 76 | */ 77 | explicit request_method(const std::string& v) 78 | : value_(v) 79 | { 80 | } 81 | 82 | /// Gets the value of the option. 83 | /** 84 | * @returns The value of the option. 85 | */ 86 | std::string value() const 87 | { 88 | return value_; 89 | } 90 | 91 | /// Sets the value of the option. 92 | /** 93 | * @param v The desired value for the option. 94 | * 95 | * @par Remarks 96 | * Postcondition: value() == v 97 | */ 98 | void value(const std::string& v) 99 | { 100 | value_ = v; 101 | } 102 | 103 | private: 104 | std::string value_; 105 | }; 106 | 107 | /// Option to specify content to accompany an HTTP request. 108 | /** 109 | * @par Remarks 110 | * The default is for no content to be sent. 111 | * 112 | * @par Example 113 | * To add content to the HTTP request using an object of class @c urdl::istream: 114 | * @code 115 | * urdl::istream is; 116 | * is.set_option(urdl::http::request_method("POST")); 117 | * is.set_option(urdl::http::request_content("Hello, world!")); 118 | * is.set_option(urdl::http::request_content_type("text/plain")); 119 | * is.open("http://host/path"); 120 | * @endcode 121 | * 122 | * To add content to the HTTP request using an object of class 123 | * @c urdl::read_stream: 124 | * @code 125 | * urdl::read_stream stream; 126 | * stream.set_option(urdl::http::request_method("POST")); 127 | * stream.set_option(urdl::http::request_content("Hello, world!")); 128 | * stream.set_option(urdl::http::request_content_type("text/plain")); 129 | * stream.open("http://host/path"); 130 | * @endcode 131 | * 132 | * @par Requirements 133 | * @e Header: @c @n 134 | * @e Namespace: @c urdl::http 135 | */ 136 | class request_content 137 | { 138 | public: 139 | /// Constructs an object of class @c request_content. 140 | /** 141 | * @par Remarks 142 | * Postcondition: value() == "". 143 | */ 144 | request_content() 145 | : value_("") 146 | { 147 | } 148 | 149 | /// Constructs an object of class @c request_content. 150 | /** 151 | * @param v The desired value for the option. 152 | * 153 | * @par Remarks 154 | * Postcondition: value() == v 155 | */ 156 | explicit request_content(const std::string& v) 157 | : value_(v) 158 | { 159 | } 160 | 161 | /// Gets the value of the option. 162 | /** 163 | * @returns The value of the option. 164 | */ 165 | std::string value() const 166 | { 167 | return value_; 168 | } 169 | 170 | /// Sets the value of the option. 171 | /** 172 | * @param v The desired value for the option. 173 | * 174 | * @par Remarks 175 | * Postcondition: value() == v 176 | */ 177 | void value(const std::string& v) 178 | { 179 | value_ = v; 180 | } 181 | 182 | private: 183 | std::string value_; 184 | }; 185 | 186 | /// Option to specify the type of the content that accompanies an HTTP request. 187 | /** 188 | * @par Remarks 189 | * The default is for no content type to be specified in the request. 190 | * 191 | * @par Example 192 | * To add content to the HTTP request using an object of class @c urdl::istream: 193 | * @code 194 | * urdl::istream is; 195 | * is.set_option(urdl::http::request_method("POST")); 196 | * is.set_option(urdl::http::request_content("Hello, world!")); 197 | * is.set_option(urdl::http::request_content_type("text/plain")); 198 | * is.open("http://host/path"); 199 | * @endcode 200 | * 201 | * To add content to the HTTP request using an object of class 202 | * @c urdl::read_stream: 203 | * @code 204 | * urdl::read_stream stream; 205 | * stream.set_option(urdl::http::request_method("POST")); 206 | * stream.set_option(urdl::http::request_content("Hello, world!")); 207 | * stream.set_option(urdl::http::request_content_type("text/plain")); 208 | * stream.open("http://host/path"); 209 | * @endcode 210 | * 211 | * @par Requirements 212 | * @e Header: @c @n 213 | * @e Namespace: @c urdl::http 214 | */ 215 | class request_content_type 216 | { 217 | public: 218 | /// Constructs an object of class @c request_content_type. 219 | /** 220 | * @par Remarks 221 | * Postcondition: value() == "". 222 | */ 223 | request_content_type() 224 | : value_("") 225 | { 226 | } 227 | 228 | /// Constructs an object of class @c request_content_type. 229 | /** 230 | * @param v The desired value for the option. 231 | * 232 | * @par Remarks 233 | * Postcondition: value() == v 234 | */ 235 | explicit request_content_type(const std::string& v) 236 | : value_(v) 237 | { 238 | } 239 | 240 | /// Gets the value of the option. 241 | /** 242 | * @returns The value of the option. 243 | */ 244 | std::string value() const 245 | { 246 | return value_; 247 | } 248 | 249 | /// Sets the value of the option. 250 | /** 251 | * @param v The desired value for the option. 252 | * 253 | * @par Remarks 254 | * Postcondition: value() == v 255 | */ 256 | void value(const std::string& v) 257 | { 258 | value_ = v; 259 | } 260 | 261 | private: 262 | std::string value_; 263 | }; 264 | 265 | /// Option to specify the maximum number of allowed HTTP redirects. 266 | /** 267 | * @par Remarks 268 | * The default value is for there to be no limit on the number of allowed 269 | * redirects. Set the option to 0 to disable HTTP redirects. 270 | * 271 | * @par Example 272 | * To set maximum number of redirects for an object of class @c urdl::istream: 273 | * @code 274 | * urdl::istream is; 275 | * is.set_option(urdl::http::max_redirects(1)); 276 | * is.open("http://www.boost.org"); 277 | * @endcode 278 | * 279 | * To set maximum number of redirects for an object of class 280 | * @c urdl::read_stream: 281 | * @code 282 | * urdl::read_stream stream; 283 | * stream.set_option(urdl::http::max_redirects(1)); 284 | * stream.open("http://www.boost.org"); 285 | * @endcode 286 | * 287 | * @par Requirements 288 | * @e Header: @c @n 289 | * @e Namespace: @c urdl::http 290 | */ 291 | class max_redirects 292 | { 293 | public: 294 | /// Constructs an object of class @c max_redirects. 295 | /** 296 | * @par Remarks 297 | * Postcondition: value() == std::numeric_limits::max(). 298 | */ 299 | max_redirects() 300 | : value_(~std::size_t(0)) 301 | { 302 | } 303 | 304 | /// Constructs an object of class @c max_redirects. 305 | /** 306 | * @param v The desired value for the option. 307 | * 308 | * @par Remarks 309 | * Postcondition: value() == v 310 | */ 311 | explicit max_redirects(std::size_t v) 312 | : value_(v) 313 | { 314 | } 315 | 316 | /// Gets the value of the option. 317 | /** 318 | * @returns The value of the option. 319 | */ 320 | std::size_t value() const 321 | { 322 | return value_; 323 | } 324 | 325 | /// Sets the value of the option. 326 | /** 327 | * @param v The desired value for the option. 328 | * 329 | * @par Remarks 330 | * Postcondition: value() == v 331 | */ 332 | void value(std::size_t v) 333 | { 334 | value_ = v; 335 | } 336 | 337 | private: 338 | std::size_t value_; 339 | }; 340 | 341 | /// Option to specify the user agent identifier. 342 | /** 343 | * @par Remarks 344 | * The default is to not specify the user agent. 345 | * 346 | * @par Example 347 | * To set the user agent for an object of class @c urdl::istream: 348 | * @code 349 | * urdl::istream is; 350 | * is.set_option(urdl::http::user_agent("Urdl")); 351 | * is.open("http://www.boost.org"); 352 | * @endcode 353 | * 354 | * To set the user agent for an object of class @c urdl::read_stream: 355 | * @code 356 | * urdl::read_stream stream; 357 | * stream.set_option(urdl::http::user_agent("Urdl")); 358 | * stream.open("http://www.boost.org"); 359 | * @endcode 360 | * 361 | * @par Requirements 362 | * @e Header: @c @n 363 | * @e Namespace: @c urdl::http 364 | */ 365 | class user_agent 366 | { 367 | public: 368 | /// Constructs an object of class @c user_agent. 369 | /** 370 | * @par Remarks 371 | * Postcondition: value() == "". 372 | */ 373 | user_agent() 374 | : value_("") 375 | { 376 | } 377 | 378 | /// Constructs an object of class @c user_agent. 379 | /** 380 | * @param v The desired value for the option. 381 | * 382 | * @par Remarks 383 | * Postcondition: value() == v 384 | */ 385 | explicit user_agent(const std::string& v) 386 | : value_(v) 387 | { 388 | } 389 | 390 | /// Gets the value of the option. 391 | /** 392 | * @returns The value of the option. 393 | */ 394 | std::string value() const 395 | { 396 | return value_; 397 | } 398 | 399 | /// Sets the value of the option. 400 | /** 401 | * @param v The desired value for the option. 402 | * 403 | * @par Remarks 404 | * Postcondition: value() == v 405 | */ 406 | void value(const std::string& v) 407 | { 408 | value_ = v; 409 | } 410 | 411 | private: 412 | std::string value_; 413 | }; 414 | 415 | namespace errc { 416 | 417 | /// HTTP error codes. 418 | /** 419 | * The enumerators of type @c errc_t are implicitly convertible to objects of 420 | * type @c boost::system::error_code. 421 | * 422 | * @par Requirements 423 | * @e Header: @c @n 424 | * @e Namespace: @c urdl::http 425 | */ 426 | enum errc_t 427 | { 428 | // Client-generated errors. 429 | 430 | /// The response's status line was malformed. 431 | malformed_status_line = 1, 432 | 433 | /// The response's headers were malformed. 434 | malformed_response_headers = 2, 435 | 436 | // Server-generated status codes. 437 | 438 | /// The server-generated status code "100 Continue". 439 | continue_request = 100, 440 | 441 | /// The server-generated status code "101 Switching Protocols". 442 | switching_protocols = 101, 443 | 444 | /// The server-generated status code "200 OK". 445 | ok = 200, 446 | 447 | /// The server-generated status code "201 Created". 448 | created = 201, 449 | 450 | /// The server-generated status code "202 Accepted". 451 | accepted = 202, 452 | 453 | /// The server-generated status code "203 Non-Authoritative Information". 454 | non_authoritative_information = 203, 455 | 456 | /// The server-generated status code "204 No Content". 457 | no_content = 204, 458 | 459 | /// The server-generated status code "205 Reset Content". 460 | reset_content = 205, 461 | 462 | /// The server-generated status code "206 Partial Content". 463 | partial_content = 206, 464 | 465 | /// The server-generated status code "300 Multiple Choices". 466 | multiple_choices = 300, 467 | 468 | /// The server-generated status code "301 Moved Permanently". 469 | moved_permanently = 301, 470 | 471 | /// The server-generated status code "302 Found". 472 | found = 302, 473 | 474 | /// The server-generated status code "303 See Other". 475 | see_other = 303, 476 | 477 | /// The server-generated status code "304 Not Modified". 478 | not_modified = 304, 479 | 480 | /// The server-generated status code "305 Use Proxy". 481 | use_proxy = 305, 482 | 483 | /// The server-generated status code "307 Temporary Redirect". 484 | temporary_redirect = 307, 485 | 486 | /// The server-generated status code "400 Bad Request". 487 | bad_request = 400, 488 | 489 | /// The server-generated status code "401 Unauthorized". 490 | unauthorized = 401, 491 | 492 | /// The server-generated status code "402 Payment Required". 493 | payment_required = 402, 494 | 495 | /// The server-generated status code "403 Forbidden". 496 | forbidden = 403, 497 | 498 | /// The server-generated status code "404 Not Found". 499 | not_found = 404, 500 | 501 | /// The server-generated status code "405 Method Not Allowed". 502 | method_not_allowed = 405, 503 | 504 | /// The server-generated status code "406 Not Acceptable". 505 | not_acceptable = 406, 506 | 507 | /// The server-generated status code "407 Proxy Authentication Required". 508 | proxy_authentication_required = 407, 509 | 510 | /// The server-generated status code "408 Request Time-out". 511 | request_timeout = 408, 512 | 513 | /// The server-generated status code "409 Conflict". 514 | conflict = 409, 515 | 516 | /// The server-generated status code "410 Gone". 517 | gone = 410, 518 | 519 | /// The server-generated status code "411 Length Required". 520 | length_required = 411, 521 | 522 | /// The server-generated status code "412 Precondition Failed". 523 | precondition_failed = 412, 524 | 525 | /// The server-generated status code "413 Request Entity Too Large". 526 | request_entity_too_large = 413, 527 | 528 | /// The server-generated status code "414 Request URI Too Large". 529 | request_uri_too_large = 414, 530 | 531 | /// The server-generated status code "415 Unsupported Media Type". 532 | unsupported_media_type = 415, 533 | 534 | /// The server-generated status code "416 Requested Range Not Satisfiable". 535 | requested_range_not_satisfiable = 416, 536 | 537 | /// The server-generated status code "417 Expectation Failed". 538 | expectation_failed = 417, 539 | 540 | /// The server-generated status code "500 Internal Server Error". 541 | internal_server_error = 500, 542 | 543 | /// The server-generated status code "501 Not Implemented". 544 | not_implemented = 501, 545 | 546 | /// The server-generated status code "502 Bad Gateway". 547 | bad_gateway = 502, 548 | 549 | /// The server-generated status code "503 Service Unavailable". 550 | service_unavailable = 503, 551 | 552 | /// The server-generated status code "504 Gateway Timeout". 553 | gateway_timeout = 504, 554 | 555 | /// The server-generated status code "505 HTTP Version Not Supported". 556 | version_not_supported = 505 557 | }; 558 | 559 | /// Converts a value of type @c errc_t to a corresponding object of type 560 | /// @c boost::system::error_code. 561 | /** 562 | * @par Requirements 563 | * @e Header: @c @n 564 | * @e Namespace: @c urdl::http 565 | */ 566 | inline boost::system::error_code make_error_code(errc_t e) 567 | { 568 | return boost::system::error_code( 569 | static_cast(e), http::error_category()); 570 | } 571 | 572 | } // namespace errc 573 | } // namespace http 574 | } // namespace urdl 575 | 576 | namespace boost { 577 | namespace system { 578 | 579 | template <> 580 | struct is_error_code_enum 581 | { 582 | static const bool value = true; 583 | }; 584 | 585 | } // namespace system 586 | } // namespace boost 587 | 588 | #include "urdl/detail/abi_suffix.hpp" 589 | 590 | #if defined(URDL_HEADER_ONLY) 591 | # include "urdl/impl/http.ipp" 592 | #endif 593 | 594 | #endif // URDL_HTTP_HPP 595 | -------------------------------------------------------------------------------- /include/urdl/impl/http.ipp: -------------------------------------------------------------------------------- 1 | // 2 | // http.ipp 3 | // ~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_HTTP_IPP 12 | #define URDL_HTTP_IPP 13 | 14 | #include 15 | 16 | #include "urdl/detail/abi_prefix.hpp" 17 | 18 | namespace urdl { 19 | namespace http { 20 | namespace detail { 21 | 22 | class error_category_impl 23 | : public boost::system::error_category 24 | { 25 | virtual const char* name() const URDL_ERROR_CATEGORY_NOEXCEPT 26 | { 27 | return "HTTP"; 28 | } 29 | 30 | virtual std::string message(int e) const 31 | { 32 | switch (e) 33 | { 34 | case http::errc::malformed_status_line: 35 | return "Malformed status line"; 36 | case http::errc::malformed_response_headers: 37 | return "Malformed response headers"; 38 | case http::errc::continue_request: 39 | return "Continue"; 40 | case http::errc::switching_protocols: 41 | return "Switching protocols"; 42 | case http::errc::ok: 43 | return "OK"; 44 | case http::errc::created: 45 | return "Created"; 46 | case http::errc::accepted: 47 | return "Accepted"; 48 | case http::errc::non_authoritative_information: 49 | return "Non-authoritative information"; 50 | case http::errc::no_content: 51 | return "No content"; 52 | case http::errc::reset_content: 53 | return "Reset content"; 54 | case http::errc::partial_content: 55 | return "Partial content"; 56 | case http::errc::multiple_choices: 57 | return "Multiple choices"; 58 | case http::errc::moved_permanently: 59 | return "Moved permanently"; 60 | case http::errc::found: 61 | return "Found"; 62 | case http::errc::see_other: 63 | return "See other"; 64 | case http::errc::not_modified: 65 | return "Not modified"; 66 | case http::errc::use_proxy: 67 | return "Use proxy"; 68 | case http::errc::temporary_redirect: 69 | return "Temporary redirect"; 70 | case http::errc::bad_request: 71 | return "Bad request"; 72 | case http::errc::unauthorized: 73 | return "Unauthorized"; 74 | case http::errc::payment_required: 75 | return "Payment required"; 76 | case http::errc::forbidden: 77 | return "Forbidden"; 78 | case http::errc::not_found: 79 | return "Not found"; 80 | case http::errc::method_not_allowed: 81 | return "Method not allowed"; 82 | case http::errc::not_acceptable: 83 | return "Not acceptable"; 84 | case http::errc::proxy_authentication_required: 85 | return "Proxy authentication required"; 86 | case http::errc::request_timeout: 87 | return "Request time-out"; 88 | case http::errc::conflict: 89 | return "Conflict"; 90 | case http::errc::gone: 91 | return "Gone"; 92 | case http::errc::length_required: 93 | return "Length required"; 94 | case http::errc::precondition_failed: 95 | return "Precondition failed"; 96 | case http::errc::request_entity_too_large: 97 | return "Request entity too large"; 98 | case http::errc::request_uri_too_large: 99 | return "Request URI too large"; 100 | case http::errc::unsupported_media_type: 101 | return "Unsupported media type"; 102 | case http::errc::requested_range_not_satisfiable: 103 | return "Requested range not satisfiable"; 104 | case http::errc::expectation_failed: 105 | return "Expectation failed"; 106 | case http::errc::internal_server_error: 107 | return "Internal server error"; 108 | case http::errc::not_implemented: 109 | return "Not implemented"; 110 | case http::errc::bad_gateway: 111 | return "Bad gateway"; 112 | case http::errc::service_unavailable: 113 | return "Service unavailable"; 114 | case http::errc::gateway_timeout: 115 | return "Gateway time-out"; 116 | case http::errc::version_not_supported: 117 | return "HTTP version not supported"; 118 | default: 119 | return "Unknown HTTP error"; 120 | } 121 | } 122 | 123 | virtual boost::system::error_condition default_error_condition( 124 | int e) const URDL_ERROR_CATEGORY_NOEXCEPT 125 | { 126 | switch (e) 127 | { 128 | case http::errc::unauthorized: 129 | case http::errc::forbidden: 130 | return boost::system::errc::permission_denied; 131 | case http::errc::not_found: 132 | return boost::system::errc::no_such_file_or_directory; 133 | default: 134 | return boost::system::error_condition(e, *this); 135 | } 136 | } 137 | }; 138 | 139 | } // namespace detail 140 | 141 | const boost::system::error_category& error_category() 142 | { 143 | static detail::error_category_impl instance; 144 | return instance; 145 | } 146 | 147 | namespace detail { 148 | 149 | static const boost::system::error_category& category_instance 150 | = error_category(); 151 | 152 | } // namespace detail 153 | } // namespace http 154 | } // namespace urdl 155 | 156 | #include "urdl/detail/abi_suffix.hpp" 157 | 158 | #endif // URDL_HTTP_IPP 159 | -------------------------------------------------------------------------------- /include/urdl/impl/istreambuf.ipp: -------------------------------------------------------------------------------- 1 | // 2 | // istreambuf.ipp 3 | // ~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_IMPL_ISTREAMBUF_IPP 12 | #define URDL_IMPL_ISTREAMBUF_IPP 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "urdl/read_stream.hpp" 20 | 21 | #include "urdl/detail/abi_prefix.hpp" 22 | 23 | namespace urdl { 24 | 25 | struct istreambuf::body 26 | { 27 | enum { putback_max = 8 }; 28 | enum { buffer_size = 512 }; 29 | 30 | body() 31 | : read_stream_(io_service_), 32 | timer_(io_service_), 33 | open_timeout_(300 * 1000), 34 | read_timeout_(300 * 1000) 35 | { 36 | } 37 | 38 | boost::array get_buffer_; 39 | boost::asio::io_service io_service_; 40 | boost::system::error_code error_; 41 | read_stream read_stream_; 42 | boost::asio::deadline_timer timer_; 43 | std::size_t open_timeout_; 44 | std::size_t read_timeout_; 45 | }; 46 | 47 | namespace detail 48 | { 49 | struct istreambuf_open_handler 50 | { 51 | boost::system::error_code& error_; 52 | boost::asio::deadline_timer& timer_; 53 | void operator()(boost::system::error_code ec) 54 | { 55 | error_ = ec; 56 | timer_.cancel(); 57 | } 58 | }; 59 | 60 | struct istreambuf_read_handler 61 | { 62 | boost::system::error_code& error_; 63 | std::size_t& bytes_transferred_; 64 | boost::asio::deadline_timer& timer_; 65 | void operator()(boost::system::error_code ec, std::size_t bytes_transferred) 66 | { 67 | error_ = ec; 68 | bytes_transferred_ = bytes_transferred; 69 | timer_.cancel(); 70 | } 71 | }; 72 | 73 | struct istreambuf_timeout_handler 74 | { 75 | read_stream& read_stream_; 76 | void operator()(boost::system::error_code ec) 77 | { 78 | if (ec != boost::asio::error::operation_aborted) 79 | read_stream_.close(ec); 80 | } 81 | }; 82 | } // namespace detail 83 | 84 | istreambuf::istreambuf() 85 | : body_(new body) 86 | { 87 | init_buffers(); 88 | } 89 | 90 | istreambuf::~istreambuf() 91 | { 92 | try 93 | { 94 | delete body_; 95 | } 96 | catch (std::exception&) 97 | { 98 | // Swallow the exception. 99 | } 100 | } 101 | 102 | void istreambuf::set_options(const option_set& options) 103 | { 104 | body_->read_stream_.set_options(options); 105 | } 106 | 107 | option_set istreambuf::get_options() const 108 | { 109 | return body_->read_stream_.get_options(); 110 | } 111 | 112 | istreambuf* istreambuf::open(const url& u) 113 | { 114 | if (is_open()) 115 | return 0; 116 | 117 | init_buffers(); 118 | body_->read_stream_.close(body_->error_); 119 | 120 | detail::istreambuf_open_handler oh = { body_->error_, body_->timer_ }; 121 | body_->read_stream_.async_open(u, oh); 122 | 123 | detail::istreambuf_timeout_handler th = { body_->read_stream_ }; 124 | body_->timer_.expires_from_now( 125 | boost::posix_time::milliseconds(body_->open_timeout_)); 126 | body_->timer_.async_wait(th); 127 | 128 | body_->io_service_.reset(); 129 | body_->io_service_.run(); 130 | 131 | if (!body_->read_stream_.is_open()) 132 | body_->error_ = make_error_code(boost::system::errc::timed_out); 133 | 134 | return !body_->error_ ? this : 0; 135 | } 136 | 137 | bool istreambuf::is_open() const 138 | { 139 | return body_->read_stream_.is_open(); 140 | } 141 | 142 | istreambuf* istreambuf::close() 143 | { 144 | if (!is_open()) 145 | return 0; 146 | 147 | body_->read_stream_.close(body_->error_); 148 | if (!body_->error_) 149 | init_buffers(); 150 | return !body_->error_ ? this : 0; 151 | } 152 | 153 | const boost::system::error_code& istreambuf::puberror() const 154 | { 155 | return error(); 156 | } 157 | 158 | std::size_t istreambuf::open_timeout() const 159 | { 160 | return body_->open_timeout_; 161 | } 162 | 163 | void istreambuf::open_timeout(std::size_t milliseconds) 164 | { 165 | body_->open_timeout_ = milliseconds; 166 | } 167 | 168 | std::size_t istreambuf::read_timeout() const 169 | { 170 | return body_->read_timeout_; 171 | } 172 | 173 | void istreambuf::read_timeout(std::size_t milliseconds) 174 | { 175 | body_->read_timeout_ = milliseconds; 176 | } 177 | 178 | std::string istreambuf::content_type() const 179 | { 180 | return body_->read_stream_.content_type(); 181 | } 182 | 183 | std::size_t istreambuf::content_length() const 184 | { 185 | return body_->read_stream_.content_length(); 186 | } 187 | 188 | std::string istreambuf::headers() const 189 | { 190 | return body_->read_stream_.headers(); 191 | } 192 | 193 | std::streambuf::int_type istreambuf::underflow() 194 | { 195 | if (gptr() == egptr()) 196 | { 197 | std::size_t bytes_transferred = 0; 198 | detail::istreambuf_read_handler rh 199 | = { body_->error_, bytes_transferred, body_->timer_ }; 200 | body_->read_stream_.async_read_some(boost::asio::buffer( 201 | boost::asio::buffer(body_->get_buffer_) + body::putback_max), rh); 202 | 203 | detail::istreambuf_timeout_handler th = { body_->read_stream_ }; 204 | body_->timer_.expires_from_now( 205 | boost::posix_time::milliseconds(body_->read_timeout_)); 206 | body_->timer_.async_wait(th); 207 | 208 | body_->io_service_.reset(); 209 | body_->io_service_.run(); 210 | 211 | if (!body_->read_stream_.is_open()) 212 | body_->error_ = make_error_code(boost::system::errc::timed_out); 213 | 214 | if (body_->error_) 215 | { 216 | if (body_->error_ == boost::asio::error::eof) 217 | { 218 | body_->error_ = boost::system::error_code(); 219 | return traits_type::eof(); 220 | } 221 | boost::throw_exception(boost::system::system_error(body_->error_)); 222 | } 223 | 224 | setg(body_->get_buffer_.begin(), 225 | body_->get_buffer_.begin() + body::putback_max, 226 | body_->get_buffer_.begin() + body::putback_max + bytes_transferred); 227 | return traits_type::to_int_type(*gptr()); 228 | } 229 | else 230 | { 231 | return traits_type::eof(); 232 | } 233 | } 234 | 235 | const boost::system::error_code& istreambuf::error() const 236 | { 237 | return body_->error_; 238 | } 239 | 240 | void istreambuf::init_buffers() 241 | { 242 | setg(body_->get_buffer_.begin(), 243 | body_->get_buffer_.begin() + body::putback_max, 244 | body_->get_buffer_.begin() + body::putback_max); 245 | } 246 | 247 | } // namespace urdl 248 | 249 | #include "urdl/detail/abi_suffix.hpp" 250 | 251 | #endif // URDL_IMPL_ISTREAMBUF_IPP 252 | -------------------------------------------------------------------------------- /include/urdl/impl/option_set.ipp: -------------------------------------------------------------------------------- 1 | // 2 | // option_set.ipp 3 | // ~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_OPTION_SET_IPP 12 | #define URDL_OPTION_SET_IPP 13 | 14 | #include "urdl/detail/abi_prefix.hpp" 15 | 16 | namespace urdl { 17 | 18 | option_set::option_set() 19 | { 20 | } 21 | 22 | option_set::option_set(const option_set& other) 23 | { 24 | detail::scoped_ptr* prev_link = &head_; 25 | option_wrapper_base* node = other.head_.get(); 26 | while (node) 27 | { 28 | prev_link->reset(node->clone()); 29 | prev_link = &prev_link->get()->next; 30 | node = node->next.get(); 31 | } 32 | } 33 | 34 | option_set::~option_set() 35 | { 36 | } 37 | 38 | option_set& option_set::operator=(const option_set& other) 39 | { 40 | option_set tmp(other); 41 | head_.reset(tmp.head_.release()); 42 | return *this; 43 | } 44 | 45 | void option_set::set_options(const option_set& other) 46 | { 47 | option_wrapper_base* node = other.head_.get(); 48 | while (node) 49 | { 50 | set_option_wrapper_base(node->clone()); 51 | node = node->next.get(); 52 | } 53 | } 54 | 55 | void option_set::set_option_wrapper_base(option_set::option_wrapper_base* o) 56 | { 57 | detail::scoped_ptr* prev_link = &head_; 58 | option_wrapper_base* node = head_.get(); 59 | while (node) 60 | { 61 | if (o->type_info() == node->type_info()) 62 | { 63 | o->next.reset(node->next.release()); 64 | prev_link->reset(o); 65 | return; 66 | } 67 | prev_link = &node->next; 68 | node = node->next.get(); 69 | } 70 | prev_link->reset(o); 71 | } 72 | 73 | option_set::option_wrapper_base* option_set::get_option_wrapper_base( 74 | const std::type_info& ti) const 75 | { 76 | option_wrapper_base* node = head_.get(); 77 | while (node) 78 | { 79 | if (ti == node->type_info()) 80 | return node; 81 | node = node->next.get(); 82 | } 83 | return 0; 84 | } 85 | 86 | void option_set::clear_option_wrapper_base(const std::type_info& ti) 87 | { 88 | detail::scoped_ptr* prev_link = &head_; 89 | option_wrapper_base* node = head_.get(); 90 | while (node) 91 | { 92 | if (ti == node->type_info()) 93 | { 94 | prev_link->reset(node->next.release()); 95 | return; 96 | } 97 | prev_link = &node->next; 98 | node = node->next.get(); 99 | } 100 | } 101 | 102 | } // namespace urdl 103 | 104 | #include "urdl/detail/abi_suffix.hpp" 105 | 106 | #endif // URDL_OPTION_SET_IPP 107 | -------------------------------------------------------------------------------- /include/urdl/impl/url.ipp: -------------------------------------------------------------------------------- 1 | // 2 | // url.ipp 3 | // ~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_URL_IPP 12 | #define URDL_URL_IPP 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "urdl/detail/abi_prefix.hpp" 21 | 22 | namespace urdl { 23 | 24 | unsigned short url::port() const 25 | { 26 | if (!port_.empty()) 27 | return std::atoi(port_.c_str()); 28 | if (protocol_ == "http") 29 | return 80; 30 | if (protocol_ == "https") 31 | return 443; 32 | if (protocol_ == "ftp") 33 | return 21; 34 | return 0; 35 | } 36 | 37 | std::string url::path() const 38 | { 39 | std::string tmp_path; 40 | unescape_path(path_, tmp_path); 41 | return tmp_path; 42 | } 43 | 44 | std::string url::to_string(int components) const 45 | { 46 | std::string s; 47 | 48 | if ((components & protocol_component) != 0 && !protocol_.empty()) 49 | { 50 | s = protocol_; 51 | s += "://"; 52 | } 53 | 54 | if ((components & user_info_component) != 0 && !user_info_.empty()) 55 | { 56 | s += user_info_; 57 | s += "@"; 58 | } 59 | 60 | if ((components & host_component) != 0) 61 | { 62 | if (ipv6_host_) 63 | s += "["; 64 | s += host_; 65 | if (ipv6_host_) 66 | s += "]"; 67 | } 68 | 69 | if ((components & port_component) != 0 && !port_.empty()) 70 | { 71 | s += ":"; 72 | s += port_; 73 | } 74 | 75 | if ((components & path_component) != 0 && !path_.empty()) 76 | { 77 | s += path_; 78 | } 79 | 80 | if ((components & query_component) != 0 && !query_.empty()) 81 | { 82 | s += "?"; 83 | s += query_; 84 | } 85 | 86 | if ((components & fragment_component) != 0 && !fragment_.empty()) 87 | { 88 | s += "#"; 89 | s += fragment_; 90 | } 91 | 92 | return s; 93 | } 94 | 95 | url url::from_string(const char* s, boost::system::error_code& ec) 96 | { 97 | url new_url; 98 | 99 | // Protocol. 100 | std::size_t length = std::strcspn(s, ":"); 101 | new_url.protocol_.assign(s, s + length); 102 | for (std::size_t i = 0; i < new_url.protocol_.length(); ++i) 103 | new_url.protocol_[i] = std::tolower(new_url.protocol_[i]); 104 | s += length; 105 | 106 | // "://". 107 | if (*s++ != ':') 108 | { 109 | ec = make_error_code(boost::system::errc::invalid_argument); 110 | return url(); 111 | } 112 | if (*s++ != '/') 113 | { 114 | ec = make_error_code(boost::system::errc::invalid_argument); 115 | return url(); 116 | } 117 | if (*s++ != '/') 118 | { 119 | ec = make_error_code(boost::system::errc::invalid_argument); 120 | return url(); 121 | } 122 | 123 | // UserInfo. 124 | length = std::strcspn(s, "@:[/?#"); 125 | if (s[length] == '@') 126 | { 127 | new_url.user_info_.assign(s, s + length); 128 | s += length + 1; 129 | } 130 | else if (s[length] == ':') 131 | { 132 | std::size_t length2 = std::strcspn(s + length, "@/?#"); 133 | if (s[length + length2] == '@') 134 | { 135 | new_url.user_info_.assign(s, s + length + length2); 136 | s += length + length2 + 1; 137 | } 138 | } 139 | 140 | // Host. 141 | if (*s == '[') 142 | { 143 | length = std::strcspn(++s, "]"); 144 | if (s[length] != ']') 145 | { 146 | ec = make_error_code(boost::system::errc::invalid_argument); 147 | return url(); 148 | } 149 | new_url.host_.assign(s, s + length); 150 | new_url.ipv6_host_ = true; 151 | s += length + 1; 152 | if (std::strcspn(s, ":/?#") != 0) 153 | { 154 | ec = make_error_code(boost::system::errc::invalid_argument); 155 | return url(); 156 | } 157 | } 158 | else 159 | { 160 | length = std::strcspn(s, ":/?#"); 161 | new_url.host_.assign(s, s + length); 162 | s += length; 163 | } 164 | 165 | // Port. 166 | if (*s == ':') 167 | { 168 | length = std::strcspn(++s, "/?#"); 169 | if (length == 0) 170 | { 171 | ec = make_error_code(boost::system::errc::invalid_argument); 172 | return url(); 173 | } 174 | new_url.port_.assign(s, s + length); 175 | for (std::size_t i = 0; i < new_url.port_.length(); ++i) 176 | { 177 | if (!std::isdigit(new_url.port_[i])) 178 | { 179 | ec = make_error_code(boost::system::errc::invalid_argument); 180 | return url(); 181 | } 182 | } 183 | s += length; 184 | } 185 | 186 | // Path. 187 | if (*s == '/') 188 | { 189 | length = std::strcspn(s, "?#"); 190 | new_url.path_.assign(s, s + length); 191 | std::string tmp_path; 192 | if (!unescape_path(new_url.path_, tmp_path)) 193 | { 194 | ec = make_error_code(boost::system::errc::invalid_argument); 195 | return url(); 196 | } 197 | s += length; 198 | } 199 | else 200 | new_url.path_ = "/"; 201 | 202 | // Query. 203 | if (*s == '?') 204 | { 205 | length = std::strcspn(++s, "#"); 206 | new_url.query_.assign(s, s + length); 207 | s += length; 208 | } 209 | 210 | // Fragment. 211 | if (*s == '#') 212 | new_url.fragment_.assign(++s); 213 | 214 | ec = boost::system::error_code(); 215 | return new_url; 216 | } 217 | 218 | url url::from_string(const char* s) 219 | { 220 | boost::system::error_code ec; 221 | url new_url(from_string(s, ec)); 222 | if (ec) 223 | { 224 | boost::system::system_error ex(ec); 225 | boost::throw_exception(ex); 226 | } 227 | return new_url; 228 | } 229 | 230 | url url::from_string(const std::string& s, boost::system::error_code& ec) 231 | { 232 | return from_string(s.c_str(), ec); 233 | } 234 | 235 | url url::from_string(const std::string& s) 236 | { 237 | return from_string(s.c_str()); 238 | } 239 | 240 | bool url::unescape_path(const std::string& in, std::string& out) 241 | { 242 | out.clear(); 243 | out.reserve(in.size()); 244 | for (std::size_t i = 0; i < in.size(); ++i) 245 | { 246 | switch (in[i]) 247 | { 248 | case '%': 249 | if (i + 3 <= in.size()) 250 | { 251 | unsigned int value = 0; 252 | for (std::size_t j = i + 1; j < i + 3; ++j) 253 | { 254 | switch (in[j]) 255 | { 256 | case '0': case '1': case '2': case '3': case '4': 257 | case '5': case '6': case '7': case '8': case '9': 258 | value += in[j] - '0'; 259 | break; 260 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 261 | value += in[j] - 'a' + 10; 262 | break; 263 | case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 264 | value += in[j] - 'A' + 10; 265 | break; 266 | default: 267 | return false; 268 | } 269 | if (j == i + 1) 270 | value <<= 4; 271 | } 272 | out += static_cast(value); 273 | i += 2; 274 | } 275 | else 276 | return false; 277 | break; 278 | case '-': case '_': case '.': case '!': case '~': case '*': 279 | case '\'': case '(': case ')': case ':': case '@': case '&': 280 | case '=': case '+': case '$': case ',': case '/': case ';': 281 | out += in[i]; 282 | break; 283 | default: 284 | if (!std::isalnum(in[i])) 285 | return false; 286 | out += in[i]; 287 | break; 288 | } 289 | } 290 | return true; 291 | } 292 | 293 | bool operator==(const url& a, const url& b) 294 | { 295 | return a.protocol_ == b.protocol_ 296 | && a.user_info_ == b.user_info_ 297 | && a.host_ == b.host_ 298 | && a.port_ == b.port_ 299 | && a.path_ == b.path_ 300 | && a.query_ == b.query_ 301 | && a.fragment_ == b.fragment_; 302 | } 303 | 304 | bool operator!=(const url& a, const url& b) 305 | { 306 | return !(a == b); 307 | } 308 | 309 | bool operator<(const url& a, const url& b) 310 | { 311 | if (a.protocol_ < b.protocol_) 312 | return true; 313 | if (b.protocol_ < a.protocol_) 314 | return false; 315 | 316 | if (a.user_info_ < b.user_info_) 317 | return true; 318 | if (b.user_info_ < a.user_info_) 319 | return false; 320 | 321 | if (a.host_ < b.host_) 322 | return true; 323 | if (b.host_ < a.host_) 324 | return false; 325 | 326 | if (a.port_ < b.port_) 327 | return true; 328 | if (b.port_ < a.port_) 329 | return false; 330 | 331 | if (a.path_ < b.path_) 332 | return true; 333 | if (b.path_ < a.path_) 334 | return false; 335 | 336 | if (a.query_ < b.query_) 337 | return true; 338 | if (b.query_ < a.query_) 339 | return false; 340 | 341 | return a.fragment_ < b.fragment_; 342 | } 343 | 344 | } // namespace urdl 345 | 346 | #include "urdl/detail/abi_suffix.hpp" 347 | 348 | #endif // URDL_URL_IPP 349 | -------------------------------------------------------------------------------- /include/urdl/istream.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // istream.hpp 3 | // ~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | // 10 | 11 | #ifndef URDL_ISTREAM_HPP 12 | #define URDL_ISTREAM_HPP 13 | 14 | #include 15 | #include 16 | #include 17 | #include "urdl/istreambuf.hpp" 18 | 19 | #include "urdl/detail/abi_prefix.hpp" 20 | 21 | namespace urdl { 22 | 23 | /// The class @c istream supports reading content from a specified URL. 24 | /** 25 | * @par Remarks 26 | * The class stores an object of class @c istreambuf. 27 | * 28 | * Currently supported URL protocols are @c http, @c https and @c file. 29 | * 30 | * @par Example 31 | * To read the entire content of a resource located by a URL into a string: 32 | * @code 33 | * urdl::istream is("http://www.boost.org/LICENSE_1_0.txt"); 34 | * if (is) 35 | * { 36 | * std::string content; 37 | * if (std::getline(is, content, std::char_traits::eof())) 38 | * { 39 | * ... 40 | * } 41 | * } 42 | * @endcode 43 | * 44 | * @par Requirements 45 | * @e Header: @c @n 46 | * @e Namespace: @c urdl 47 | */ 48 | class istream 49 | : private boost::base_from_member, 50 | public std::basic_istream 51 | { 52 | public: 53 | /// Constructs an object of class @c istream. 54 | /** 55 | * @par Remarks 56 | * Initializes the base class with @c std::basic_istream(sb), 57 | * where sb is an object of class @c istreambuf stored within the class. 58 | */ 59 | istream() 60 | : std::basic_istream( 61 | &this->boost::base_from_member::member) 62 | { 63 | } 64 | 65 | /// Constructs an object of class @c istream. 66 | /** 67 | * @param u The URL to open. 68 | * 69 | * @par Remarks 70 | * Initializes the base class with @c std::basic_istream(sb), 71 | * where @c sb is an object of class @c istreambuf stored within the class. It 72 | * also opens @c sb by performing @c sb.open(u) and, if that fails (returns a 73 | * null pointer), calls @c setstate(failbit). 74 | */ 75 | explicit istream(const url& u) 76 | : std::basic_istream( 77 | &this->boost::base_from_member::member) 78 | { 79 | if (rdbuf()->open(u) == 0) 80 | setstate(std::ios_base::failbit); 81 | } 82 | 83 | /// Constructs an object of class @c istream. 84 | /** 85 | * @param u The URL to open. 86 | * 87 | * @param options The options to be set on the stream. 88 | * 89 | * @par Remarks 90 | * Initializes the base class with @c std::basic_istream(sb), where 91 | * @c sb is an object of class @c istreambuf stored within the class. It also 92 | * performs @c rdbuf()->set_options(options), then opens @c sb by performing 93 | * @c sb.open(u) and, if that fails (returns a null pointer), calls 94 | * @c setstate(failbit). 95 | * 96 | * @par Example 97 | * @code 98 | * urdl::option_set options; 99 | * options.set_option(urdl::http::max_redirects(1)); 100 | * urdl::istream is("http://www.boost.org", options); 101 | * @endcode 102 | */ 103 | explicit istream(const url& u, const option_set& options) 104 | : std::basic_istream( 105 | &this->boost::base_from_member::member) 106 | { 107 | rdbuf()->set_options(options); 108 | if (rdbuf()->open(u) == 0) 109 | setstate(std::ios_base::failbit); 110 | } 111 | 112 | /// Sets an option to control the behaviour of the stream. 113 | /** 114 | * @param option The option to be set on the stream. 115 | * 116 | * @par Remarks 117 | * Performs @c rdbuf()->set_option(option). Options are uniquely identified by 118 | * type. 119 | * 120 | * @par Example 121 | * @code 122 | * urdl::istream is; 123 | * is.set_option(urdl::http::max_redirects(1)); 124 | * @endcode 125 | */ 126 | template 127 | void set_option(const Option& option) 128 | { 129 | rdbuf()->set_option(option); 130 | } 131 | 132 | /// Sets options to control the behaviour of the stream. 133 | /** 134 | * @param options The options to be set on the stream. The options in the set 135 | * are added on top of any options already set on the stream. 136 | * 137 | * @par Remarks 138 | * Performs @c rdbuf()->set_options(options). 139 | * 140 | * @par Example 141 | * @code 142 | * urdl::istream is; 143 | * urdl::option_set options; 144 | * options.set_option(urdl::http::max_redirects(1)); 145 | * options.set_option(urdl::ssl::verify_peer(false)); 146 | * stream.set_options(options); 147 | * @endcode 148 | */ 149 | void set_options(const option_set& options) 150 | { 151 | rdbuf()->set_options(options); 152 | } 153 | 154 | /// Gets the current value of an option that controls the behaviour of the 155 | /// stream. 156 | /** 157 | * @returns The current value of the option. 158 | * 159 | * @par Remarks 160 | * Returns @c rdbuf()->get_option