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