Remove vendored copy of urdl

This commit is contained in:
Tristan Penman 2022-02-21 21:43:04 +11:00
parent 9e7dbd8460
commit 0e3f48c889
49 changed files with 0 additions and 13352 deletions

View File

@ -1,4 +0,0 @@
Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@ -1,55 +0,0 @@
import modules ;
import package ;
path-constant URDL_ROOT : . ;
BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ;
if $(BOOST_ROOT)
{
use-project /boost : $(BOOST_ROOT) ;
}
rule handle-static-runtime ( properties * )
{
if <link>shared in $(properties) && <runtime-link>static in $(properties)
{
return <build>no ;
}
}
project
:
build-dir build/bin
:
requirements
<threading>multi
<conditional>@handle-static-runtime
:
default-build
debug release
<threading>multi
<link>shared <link>static
<runtime-link>shared <runtime-link>static
;
install lib
:
build
:
<location>lib
<install-type>LIB
;
local patterns = *.hpp *.ipp ;
local dirs = include/urdl include/urdl/* ;
package.install install
:
<install-source-root>include
:
:
build
:
[ glob $(dirs)/$(patterns) ]
;

View File

@ -1,23 +0,0 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,4 +0,0 @@
urdl version 0.1
Released Wednesday, 03 June 2009.
See doc/index.html for build information and API documentation.

View File

@ -1,102 +0,0 @@
import common ;
import os ;
import modules ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
local SSL_OPTIONS ;
if [ modules.peek : URDL_DISABLE_SSL ] = 1
{
SSL_OPTIONS = <define>URDL_DISABLE_SSL=1 ;
}
else if [ os.name ] = NT
{
OPENSSL_ROOT = [ modules.peek : OPENSSL_ROOT ] ;
lib user32 ;
lib advapi32 ;
lib gdi32 ;
SSL_OPTIONS = <include>$(OPENSSL_ROOT)/inc32
<library>$(OPENSSL_ROOT)/out32dll/libeay32.lib
<library>$(OPENSSL_ROOT)/out32dll/ssleay32.lib
<library>user32 <library>advapi32 <library>gdi32 ;
}
else
{
lib ssl ;
lib crypto ;
SSL_OPTIONS = <library>ssl <library>crypto ;
}
project urdl
:
source-location ../src
:
requirements
<threading>multi
:
usage-requirements
<link>shared:<define>URDL_DYN_LINK=1
<link>static:<define>URDL_STATIC_LINK=1
<include>$(URDL_ROOT)/include
<library>/boost/system//boost_system
<library>/boost/date_time//boost_date_time
<define>BOOST_ALL_NO_LIB=1
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT:<define>WIN32_LEAN_AND_MEAN=1
<os>NT,<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<os>NT,<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
$(SSL_OPTIONS)
;
lib urdl
:
urdl.cpp
:
<link>shared:<define>BOOST_ALL_DYN_LINK=1
<link>static:<define>BOOST_ALL_STATIC_LINK=1
<include>$(URDL_ROOT)/include
<library>/boost/system//boost_system
<library>/boost/date_time//boost_date_time
<define>BOOST_ALL_NO_LIB=1
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<os>NT,<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<debug-symbols>on
<tag>@tag
$(SSL_OPTIONS)
;
rule tag ( name : type ? : property-set )
{
local result = [ common.format-name
<base> <runtime>
: $(name) : $(type) : $(property-set) ] ;
return "$(result)" ;
}

View File

@ -1,62 +0,0 @@
#
# Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
using quickbook ;
project
:
default-build
debug
<threading>multi
<link>shared
<runtime-link>shared
;
xml standalone_doc
:
urdl.qbk
;
boostbook standalone
:
standalone_doc
:
<xsl:param>boost.image.src=urdl.png
<xsl:param>boost.image.alt="Urdl C++ Library"
<xsl:param>boost.image.w=160
<xsl:param>boost.image.h=60
<xsl:param>nav.layout=none
<xsl:param>navig.graphics.path=""
<xsl:param>admon.graphics.path=""
<xsl:param>chapter.autolabel=0
<xsl:param>chunk.section.depth=8
<xsl:param>chunk.first.sections=1
<xsl:param>toc.section.depth=3
<xsl:param>toc.max.depth=3
<xsl:param>generate.section.toc.level=2
<xsl:param>generate.toc="chapter toc section toc"
;
install html
:
/boost//doc/src/boostbook.css
/boost//doc/src/images/blank.png
/boost//doc/src/images/caution.png
/boost//doc/src/images/draft.png
/boost//doc/src/images/home.png
/boost//doc/src/images/important.png
/boost//doc/src/images/next_disabled.png
/boost//doc/src/images/next.png
/boost//doc/src/images/note.png
/boost//doc/src/images/prev_disabled.png
/boost//doc/src/images/prev.png
/boost//doc/src/images/tip.png
/boost//doc/src/images/up_disabled.png
/boost//doc/src/images/up.png
/boost//doc/src/images/warning.png
urdl.png
;

View File

@ -1,15 +0,0 @@
#!/usr/bin/perl -w
# Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
use strict;
system("doxygen reference.dox");
chdir("xml");
system("xsltproc combine.xslt index.xml > all.xml");
chdir("..");
system("xsltproc reference.xsl xml/all.xml > reference.qbk");
system("rm -rf xml");

View File

@ -1,21 +0,0 @@
<html>
<head>
<title>Urdl</title>
<meta http-equiv="refresh" content="0; URL=html/index.html"/>
</head>
<body>
<p>
Automatic redirection failed, please go to
<a href="html/index.html">html/index.html</a>
</p>
<hr/>
<p>
Copyright (c) 2009-2013 Christopher M. Kohlhoff
</p>
<p>
Distributed under the Boost Software License, Version 1.0. (See accompanying
file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy at
<a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a>)
</p>
</body>
</html>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "../../boost/tools/boostbook/dtd/boostbook.dtd">
<!--
Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-->
<section id="urdl.index">
<title>Index</title>
<index>
<title></title>
</index>
</section>

View File

@ -1,223 +0,0 @@
# Doxyfile 1.4.5
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = "Urdl Reference"
PROJECT_NUMBER =
OUTPUT_DIRECTORY = .
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
USE_WINDOWS_ENCODING = NO
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = YES
INLINE_INHERITED_MEMB = YES
FULL_PATH_NAMES = YES
STRIP_FROM_PATH = ./../../../
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = YES
DETAILS_AT_TOP = YES
INHERIT_DOCS = NO
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 2
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
BUILTIN_STL_SUPPORT = NO
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO
HIDE_UNDOC_MEMBERS = YES
HIDE_UNDOC_CLASSES = YES
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = NO
INLINE_INFO = NO
SORT_MEMBER_DOCS = NO
SORT_BRIEF_DOCS = NO
SORT_BY_SCOPE_NAME = NO
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = NO
SHOW_DIRECTORIES = NO
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ./../include/urdl std_dox.txt
FILE_PATTERNS = *.hpp
RECURSIVE = NO
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = YES
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
USE_HTAGS = NO
VERBATIM_HEADERS = NO
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 1
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = NO
HTML_OUTPUT = .
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = YES
ENUM_VALUES_PER_LINE = 1
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = NO
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = YES
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = URDL_DECL
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = NO
GROUP_GRAPHS = NO
UML_LOOK = NO
TEMPLATE_RELATIONS = YES
INCLUDE_GRAPH = NO
INCLUDED_BY_GRAPH = NO
CALL_GRAPH = NO
GRAPHICAL_HIERARCHY = NO
DIRECTORY_GRAPH = NO
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_WIDTH = 640
MAX_DOT_GRAPH_HEIGHT = 640
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = NO
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
/**
\class std::basic_istream
*/
/**
\class std::streambuf
*/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,556 +0,0 @@
[/
/ Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
/
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/]
[library Urdl
[quickbook 1.4]
[copyright 2009-2013 Christopher M. Kohlhoff]
[purpose Networking/HTTP library]
[license
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
[@http://www.boost.org/LICENSE_1_0.txt])
]
[category template]
[category generic]
]
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
[def SyncReadStream [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/SyncReadStream.html SyncReadStream]]
[def AsyncReadStream [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/AsyncReadStream.html AsyncReadStream]]
[def boost::asio::read [@http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/read.html boost::asio::read]]
[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]]
[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]]
[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]]
[/=============================================================================]
[heading What is Urdl?]
Urdl is a cross-platform C++ library for downloading web content using a URL.
It provides an easy-to-use extension to standard C++ iostreams and an
asynchronous interface for use with Boost.Asio.
[heading What can Urdl be used for?]
Possible uses for Urdl include:
* Downloading application configuration or data files.
* Downloading software updates.
* Accessing RSS feeds.
* Web service clients (e.g. SOAP, XML-RPC or REST).
* Web scraping.
[heading Obtaining Urdl]
The latest release of Urdl can be downloaded from
[@http://sourceforge.net/project/showfiles.php?group_id=262090 SourceForge.net].
[/=============================================================================]
[section:rationale Rationale]
The primary goal of Urdl is:
* To provide a simple abstraction for accessing and downloading internet
resources.
Some secondary goals include:
* To act as an example of using Boost.Asio to create client-side abstractions.
* To act as an example of how to extend the Boost.System `error_code` facility.
* To explore the use of coroutine for implementing protocols using Boost.Asio.
[endsect]
[/=============================================================================]
[section:features Features]
* [*Support for HTTP, HTTPS and local files.]
[:Resources may be downloaded using HTTP 1.0 (using URLs of the form
`"http://..."`), HTTPS (`"https://..."`). Local files are also supported
(`"file://..."`). See [link urdl.planned_features Planned Features] for
limitations in the current protocol implementations.]
* [*Extension to C++ IOStreams.]
[:The [link urdl.reference.core.istream urdl::istream] class provides a way to
easily access content using standard C++ I/O facilities.]
* [*Integration with Boost.Asio.]
[:The [link urdl.reference.core.read_stream urdl::read_stream] class allows
Urdl functionality to be used with other Boost.Asio features such as sockets
and timers.]
[endsect]
[/=============================================================================]
[section:planned_features Planned features]
The following features are planned for a future release of Urdl:
* [*Support for HTTP chunking.]
[:Add the ability to handle HTTP responses where the [^Transfer-Encoding] is
[^chunked].]
* [*Support for HTTP proxies.]
[:Add support for making HTTP and HTTPS requests through a proxy.]
* [*Support for FTP.]
[:Add support for downloading files using FTP.]
* [*SSL options to control peer verification.]
[:Add new options for controlling the way Urdl uses SSL to implement HTTPS, e.g.
the ability to specify a certificate authority file or directory, whether
peers are verified, etc. The current implementation always verifies the
peer using OpenSSL's default certificate chains.]
* [*Support for wildcard SSL certificates.]
[:The current implementation of SSL peer verification supports only certificates
issued to a single hostname.]
* [*Status function callback.]
[:Allow the user to register a `boost::function<>` object using `set_option()`.
This will be called by the implementation to provide progress updates (e.g.
resolving, connection, reading, etc).]
* [*Runtime polymorphism and user-supplied backends.]
[:Change the underlying implementation to use runtime polymorphism (abstract
base classes) to invoke the appropriate protocol implementation. The user
will be able to register a factory object to provide custom support for
additional protocols.]
[endsect]
[/=============================================================================]
[section:prerequisites Prerequisites]
Urdl depends on the following libraries:
* [@http://www.boost.org/ Boost] 1.38 or later.
* [@http://www.openssl.org/ OpenSSL] (optional) for HTTPS support.
[endsect]
[/=============================================================================]
[section:platforms Supported platforms]
Urdl has been tested on the following platforms and compilers:
* Debian Linux 5.0 using g++ 4.1.
* Debian Linux 5.0 using g++ 4.3.
* Mac OS X 10.4 using g++ 4.0.1
* Windows using MinGW / g++ 3.4.5
* Windows using Visual C++ 8.0
* Windows using Visual C++ 9.0
[endsect]
[/=============================================================================]
[section:getting_started Getting started]
[section:building Building and using Urdl]
There are three options for using Urdl in an application:
* as a separately compiled library;
* by compiling the source directly into the application; or
* as a header-file-only library.
[heading Using Urdl as a separately compiled library]
Urdl uses [^bjam] and Boost.Build to build shared and static libraries. To
build the libraries, perform the following steps:
# Set a [^BOOST_ROOT] environment variable pointing to the location of Boost.
# If building on Windows, set an [^OPENSSL_ROOT] environment variable pointing
to the location of OpenSSL. When building on UNIX, the build scripts assume
that the OpenSSL headers and libraries may be found in the system's include
and library paths, respectively. To disable Urdl's SSL support, define
[^URDL_DISABLE_SSL=1] as an environment variable.
# Run [^bjam] in the top-level directory of the Urdl distribution. Libraries
should be built into the [^lib] subdirectory.
Then, to use Urdl in an application:
# Add the [^include] subdirectory to your compiler's include path.
# Add the [^lib] subdirectory to your linker's library path.
# Add the Boost directory to your compiler's include path.
# Add the Boost library directory (e.g. [^stage/lib]) to your linker's library
path. Urdl needs the application to link against the Boost.System and
Boost.Date_Time libraries.
# If disabling Urdl's SSL support, add [^URDL_DISABLE_SSL=1] to your compiler's
preprocessor definitions.
# If using the DLL version of Urdl on Windows, add [^URDL_DYN_LINK=1] to your
compiler's preprocessor definitions.
[heading Compiling Urdl directly into an application]
To compile Urdl directly into an application, perform the following steps:
# Add the [^include] subdirectory to your compiler's include path.
# Add the file [^src/urdl.cpp] to your project or makefile.
# Add the Boost directory to your compiler's include path.
# Add the Boost library directory (e.g. [^stage/lib]) to your linker's library
path. Urdl needs the application to link against the Boost.System and
Boost.Date_Time libraries.
# If disabling Urdl's SSL support, add [^URDL_DISABLE_SSL=1] to your compiler's
preprocessor definitions.
# If building on Windows, add [^URDL_NO_LIB=1] to your compiler's preprocessor
definitions to disable autolinking.
[heading Using Urdl as a header-only library]
To use Urdl as a header-only library, perform the following steps:
# Add the [^include] subdirectory to your compiler's include path.
# Add the Boost directory to your compiler's include path.
# Add the Boost library directory (e.g. [^stage/lib]) to your linker's library
path. Urdl needs the application to link against the Boost.System library and
possibly the Boost.Date_Time library.
# If disabling Urdl's SSL support, add [^URDL_DISABLE_SSL=1] to your compiler's
preprocessor definitions.
# Add [^URDL_HEADER_ONLY=1] to your compiler's preprocessor definitions.
[endsect]
[section Downloading a file using an istream]
The simplest use of Urdl is to download a file or other resource using the
[link urdl.reference.core.istream urdl::istream] class.
// For std::cout and std::cerr.
#include <iostream>
#include <ostream>
// For urdl::istream. Each of Urdl's core classes has its own header file.
#include <urdl/istream.hpp>
int main()
{
// Open the URL. The connection is established and the HTTP request is sent.
urdl::istream is("http://www.boost.org/LICENSE_1_0.txt");
// Check whether we opened the URL successfully.
if (!is)
{
std::cerr << "Unable to open URL" << std::endl;
return 1;
}
// From here on, we can use urdl::istream like any other std::istream
// object. Let's output the downloaded content one line at a time.
std::string line;
while (std::getline(is, line))
{
std::cout << line << std::endl;
}
}
[endsect]
[section Checking for errors]
If we are unable to open a URL for any reason, we can find out the last error
from the [link urdl.reference.core.istream urdl::istream] class:
urdl::istream is("http://somehost/path");
if (!is)
{
std::cerr << "Unable to open URL: ";
std::cerr << is.error().message() << std::endl;
return 1;
}
Alternatively, we may test for a specific error:
// For the HTTP error codes.
#include <urdl/http.hpp>
...
urdl::istream is("http://somehost/path");
if (!is)
{
if (is.error() == urdl::http::errc::not_found)
{
// Hmm, maybe we can try downloading the file from somewhere else...
}
}
[endsect]
[section Setting options to perform an HTTP POST]
To upload over HTTP, we set the [link urdl.reference.opt.http__request_method
http::request_method], [link urdl.reference.opt.http__request_content
http::request_content] and [link urdl.reference.opt.http__request_content_type
http::request_content_type] options on the stream prior to opening the URL:
// For the HTTP options.
#include <urdl/http.hpp>
...
urdl::istream is;
// We're doing an HTTP POST ...
is.set_option(urdl::http::request_method("POST"));
// ... where the MIME type indicates plain text ...
is.set_option(urdl::http::request_content_type("text/plain"));
// ... and here's the content.
is.set_option(urdl::http::request_content("Hello, world!"));
// All options set, so now we can open the URL.
is.open("http://somehost/path");
[endsect]
[section Grouping and reusing options]
Options may be grouped using an [link urdl.reference.core.option_set
urdl::option_set] object. This allows the options to be reused across multiple
requests.
// For urdl::option_set.
#include <urdl/option_set.hpp>
...
urdl::option_set common_options;
// Prevent HTTP redirections.
common_options.set_option(urdl::http::max_redirects(0));
// Tell the web server about Urdl.
common_options.set_option(urdl::http::user_agent("Urdl"));
// Open a URL using only the common options.
urdl::istream is1("http://somehost/path1", common_options);
// Open a URL with additional options. In this case, the common options are
// applied to the stream as if we had called set_option invidiually for each
// option in the set.
urdl::istream is2;
is2.set_option(urdl::http::request_method("POST"));
is2.set_option(urdl::http::request_content_type("text/plain"));
is2.set_option(urdl::http::request_content("Hello, world!"));
is2.set_options(common_options);
[endsect]
[section Specifying timeouts]
To prevent unresponsive servers from indefinitely hanging a program, the [link
urdl.reference.core.istream urdl::istream] class uses a timeout when opening
the stream and when reading content.
urdl::istream is;
// Fail if the URL cannot be opened within 60 seconds.
is.open_timeout(60000);
is.open("http://somehost/path");
if (!is)
{
// If the open operation timed out then:
// is.error() == boost::system::errc::timed_out
// holds true.
}
...
// Fail if an individual read does not complete within 30 seconds.
is.read_timeout(30000);
// From here on, use urdl::istream like any other std::istream object.
std::string line;
while (std::getline(is, line))
{
std::cout << line << std::endl;
}
// If a read operation timed out then:
// is.error() == boost::system::errc::timed_out
// holds true.
[endsect]
[section Parsing URLs]
The [link urdl.reference.core.url urdl::url] class gives us the ability to parse
URLs and access their component parts. The default constructor:
// For urdl::url.
#include <urdl/url.hpp>
...
urdl::url url("http://somehost/path");
provides a conversion from `std::string` or `const char*` to URLs. If the URL
does not parse correctly, the constructor throws an exception of type
`boost::system::system_error`. It is this constructor that is used when we
write:
urdl::istream is("http://somehost/path");
We can also use the [link urdl.reference.core.url.from_string
urdl::url::from_string] static member function to explicitly parse a URL, with
the option of choosing a throwing overload:
urdl::url url = urdl::url::from_string("http://somehost/path");
or an overload that does not throw an exception on failure:
boost::system::error_code ec;
urdl::url url = urdl::url::from_string("http://somehost/path", ec);
[endsect]
[section Integrating with Boost.Asio]
The [link urdl.reference.core.read_stream urdl::read_stream] class allows
applications to use Urdl's functionality in conjunction with Boost.Asio.
To synchronously open a URL, we may use:
// For urdl::read_stream.
#include <urdl/read_stream.hpp>
...
boost::asio::io_service io_service;
...
// An urdl::read_stream must always have an associated io_service.
urdl::read_stream stream(io_service);
// Open the URL synchronously. Throws boost::system::system_error on failure.
stream.open("http://somehost/path");
...
// Alternatively, open the URL synchronously without throwing on error.
boost::system::error_code ec;
stream.open("http://somehost/path", ec);
To asynchronously open a URL, we can write:
void open_handler(const boost::system::error_code& ec)
{
if (ec)
{
// URL successfully opened.
...
}
else
{
std::cerr << "Unable to open URL: ";
std::cerr << is.error().message() << std::endl;
}
}
...
stream.async_open("http://somehost/path", open_handler);
and the callback function `open_handler` will be invoked once the asynchronous
operation completes.
The [link urdl.reference.core.read_stream urdl::read_stream] class meets
Boost.Asio's SyncReadStream and AsyncReadStream type requirements. This means
we can use it with the synchronous functions boost::asio::read and
boost::asio::read_until:
boost::array<char, 512> data;
boost::asio::read(stream, boost::asio::buffer(data));
...
boost::asio::streambuf buffer;
boost::asio::read_until(stream, buffer, "\r\n");
or with the asynchronous functions boost::asio::async_read and
boost::asio::async_read_until:
void read_handler(const boost::system::error_code& ec, std::size_t length)
{
...
}
...
boost::array<char, 512> data;
boost::asio::async_read(stream, boost::asio::buffer(data), read_handler);
...
boost::asio::streambuf buffer;
boost::asio::async_read_until(stream, buffer, "\r\n", read_handler);
The asynchronous functions on the [link urdl.reference.core.read_stream
urdl::read_stream] class allow concurrent access to multiple URLs without
requiring additional threads. Furthermore, we can perform the operations
concurrently with any of the other asynchronous facilities provided by
Boost.Asio (sockets, timers and so on).
[endsect]
[endsect]
[/=============================================================================]
[include reference.qbk]
[xinclude index.xml]

View File

@ -1,27 +0,0 @@
#
# Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
project
:
requirements <library>../build//urdl
:
default-build
debug
<threading>multi
<link>shared
<runtime-link>shared
;
exe get1 : get1.cpp ;
exe get2 : get2.cpp ;
exe multiget1 : multiget1.cpp ;
explicit multiget2 ;
exe multiget2 : multiget2.cpp
/boost/context//boost_context
/boost/coroutine//boost_coroutine
;

View File

@ -1,57 +0,0 @@
//
// get1.cpp
// ~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <urdl/istream.hpp>
#include <boost/progress.hpp>
#include <iostream>
#include <fstream>
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cerr << "Usage: get1 <url> <outputfile>\n";
return 1;
}
urdl::istream is(argv[1]);
if (!is)
{
std::cout << is.error().message() << std::endl;
return 1;
}
std::ofstream os(argv[2], std::ios_base::out | std::ios_base::binary);
if (is.content_length() != std::numeric_limits<std::size_t>::max())
{
boost::progress_display progress(is.content_length());
while (is && os)
{
char buffer[1024] = "";
is.read(buffer, sizeof(buffer));
os.write(buffer, is.gcount());
progress += is.gcount();
}
std::cout << std::endl;
}
else
{
os << is.rdbuf();
}
std::cout << is.error().message() << std::endl;
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
}

View File

@ -1,48 +0,0 @@
//
// get2.cpp
// ~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <urdl/read_stream.hpp>
#include <boost/progress.hpp>
#include <iostream>
#include <fstream>
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cerr << "Usage: get2 <url> <outputfile>\n";
return 1;
}
boost::asio::io_service io_service;
urdl::read_stream stream(io_service);
stream.open(argv[1]);
std::ofstream os(argv[2], std::ios_base::out | std::ios_base::binary);
for (;;)
{
char data[1024];
boost::system::error_code ec;
std::size_t length = stream.read_some(boost::asio::buffer(data), ec);
if (ec == boost::asio::error::eof)
break;
if (ec)
throw boost::system::system_error(ec);
os.write(data, length);
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
}

View File

@ -1,92 +0,0 @@
//
// multiget1.cpp
// ~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <urdl/read_stream.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <iostream>
#include <fstream>
#include <string>
class downloader
: public boost::enable_shared_from_this<downloader>
{
public:
downloader(boost::asio::io_service& io_service)
: read_stream_(io_service)
{
}
void start(const urdl::url& url, const std::string& file)
{
file_ = file;
read_stream_.async_open(url,
boost::bind(&downloader::handle_open,
shared_from_this(), _1));
}
private:
void handle_open(const boost::system::error_code& ec)
{
if (!ec)
{
ofstream_.open(file_.c_str(), std::ios_base::out | std::ios_base::binary);
read_stream_.async_read_some(
boost::asio::buffer(buffer_),
boost::bind(&downloader::handle_read,
shared_from_this(), _1, _2));
}
}
void handle_read(const boost::system::error_code& ec, std::size_t length)
{
if (!ec)
{
ofstream_.write(buffer_, length);
read_stream_.async_read_some(
boost::asio::buffer(buffer_),
boost::bind(&downloader::handle_read,
shared_from_this(), _1, _2));
}
}
urdl::read_stream read_stream_;
std::string file_;
std::ofstream ofstream_;
char buffer_[1024];
};
int main(int argc, char* argv[])
{
try
{
if (argc < 3 || argc % 2 == 0)
{
std::cerr << "Usage: multiget1 <url> <outputfile> ";
std::cerr << "[<url> <outputfile> ...]\n";
return 1;
}
boost::asio::io_service io_service;
for (int i = 1; i < argc; i += 2)
{
boost::shared_ptr<downloader> d(new downloader(io_service));
d->start(argv[i], argv[i + 1]);
}
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
}

View File

@ -1,74 +0,0 @@
//
// multiget2.cpp
// ~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <urdl/read_stream.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <iostream>
#include <fstream>
#include <string>
void download(boost::asio::io_service& io_service,
const urdl::url& url, const std::string& file,
boost::asio::yield_context yield)
{
try
{
urdl::read_stream read_stream(io_service);
read_stream.async_open(url, yield);
std::ofstream ofstream(file.c_str(),
std::ios_base::out | std::ios_base::binary);
char buffer[1024];
std::size_t length;
boost::system::error_code ec;
do
{
length = read_stream.async_read_some(
boost::asio::buffer(buffer), yield[ec]);
ofstream.write(buffer, length);
} while (length > 0);
}
catch (std::exception& e)
{
std::cerr << "Download exception: " << e.what() << std::endl;
}
}
int main(int argc, char* argv[])
{
try
{
if (argc < 3 || argc % 2 == 0)
{
std::cerr << "Usage: multiget2 <url> <outputfile> ";
std::cerr << "[<url> <outputfile> ...]\n";
return 1;
}
boost::asio::io_service io_service;
for (int i = 1; i < argc; i += 2)
{
boost::asio::spawn(io_service,
boost::bind(download, boost::ref(io_service),
urdl::url(argv[i]), std::string(argv[i + 1]), _1));
}
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
}

View File

@ -1,31 +0,0 @@
//
// abi_prefix.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// No include guard.
#include <boost/config/abi_prefix.hpp>
// Disable some pesky MSVC warnings.
#if defined(_MSC_VER)
# pragma warning (push)
# pragma warning (disable:4127)
# pragma warning (disable:4251)
# pragma warning (disable:4355)
# pragma warning (disable:4512)
# pragma warning (disable:4996)
#endif // defined(_MSC_VER)
// Force external visibility of all types.
#if defined(__GNUC__)
# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
# pragma GCC visibility push (default)
# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
#endif // defined(__GNUC__)

View File

@ -1,23 +0,0 @@
//
// abi_suffix.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// No include guard.
#if defined(_MSC_VER)
# pragma warning (pop)
#endif // defined(_MSC_VER)
#if defined(__GNUC__)
# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
# pragma GCC visibility pop
# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
#endif // defined(__GNUC__)
#include <boost/config/abi_suffix.hpp>

View File

@ -1,94 +0,0 @@
//
// config.hpp
// ~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// No include guard.
#include <boost/config.hpp>
#include <boost/version.hpp>
#if defined(URDL_HEADER_ONLY)
# define URDL_DECL inline
#else // defined(URDL_HEADER_ONLY)
# if defined(BOOST_HAS_DECLSPEC)
// We need to import/export our code only if the user has specifically asked
// for it by defining either BOOST_ALL_DYN_LINK if they want all boost
// libraries to be dynamically linked (and if boost is dynamically linked, urdl
// must be dynamically linked too), or URDL_DYN_LINK if they want just urdl to
// be dynamically liked.
# if defined(BOOST_ALL_DYN_LINK) || defined(URDL_DYN_LINK)
# if !defined(URDL_DYN_LINK)
# define URDL_DYN_LINK
# endif // !defined(URDL_DYN_LINK)
// Export if this is our own source, otherwise import.
# if defined(URDL_SOURCE)
# define URDL_DECL __declspec(dllexport)
# else // defined(URDL_SOURCE)
# define URDL_DECL __declspec(dllimport)
# endif // defined(URDL_SOURCE)
# endif // defined(BOOST_ALL_DYN_LINK) || defined(URDL_DYN_LINK)
# endif // defined(BOOST_HAS_DECLSPEC)
#endif // defined(URDL_HEADER_ONLY)
// If URDL_DECL isn't defined yet define it now.
#if !defined(URDL_DECL)
# define URDL_DECL
#endif // !defined(URDL_DECL)
#if !defined(URDL_ERROR_CATEGORY_NOEXCEPT)
# if (BOOST_VERSION >= 105300)
# define URDL_ERROR_CATEGORY_NOEXCEPT BOOST_NOEXCEPT
# else // (BOOOST_VERSION >= 105300)
# define URDL_ERROR_CATEGORY_NOEXCEPT
# endif // (BOOOST_VERSION >= 105300)
#endif // !defined(URDL_ERROR_CATEGORY_NOEXCEPT)
#if (BOOST_VERSION >= 105400)
# define URDL_INITFN_RESULT_TYPE(h, sig) BOOST_ASIO_INITFN_RESULT_TYPE(h, sig)
#else // (BOOST_VERSION >= 105400)
# define URDL_INITFN_RESULT_TYPE(h, sig) void
#endif // (BOOST_VERSION >= 105400)
// Enable library autolinking for MSVC.
#if !defined(BOOST_ALL_NO_LIB) && !defined(URDL_NO_LIB) \
&& !defined(URDL_SOURCE) && !defined(URDL_HEADER_ONLY) \
&& defined(_MSC_VER)
# if !defined(_MT)
# error "You must use the multithreaded runtime."
# endif
# if (defined(_DLL) || defined(_RTLDLL)) && defined(URDL_DYN_LINK)
# define URDL_LIB_PREFIX
# elif defined(URDL_DYN_LINK)
# error "Mixing a dll library with a static runtime is unsupported."
# else
# define URDL_LIB_PREFIX "lib"
# endif
# if defined(_DEBUG)
# if defined(_DLL)
# define URDL_LIB_SUFFIX "-gd"
# else
# define URDL_LIB_SUFFIX "-sgd"
# endif
# else
# if defined(_DLL)
# define URDL_LIB_SUFFIX
# else
# define URDL_LIB_SUFFIX "-s"
# endif
# endif
# pragma comment(lib, URDL_LIB_PREFIX "urdl" URDL_LIB_SUFFIX ".lib")
#endif // !defined(BOOST_ALL_NO_LIB) && !defined(URDL_NO_LIB)
// && !defined(URDL_SOURCE) && !defined(URDL_HEADER_ONLY)
// && defined(_MSC_VER)

View File

@ -1,181 +0,0 @@
//
// connect.hpp
// ~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_DETAIL_CONNECT_HPP
#define URDL_DETAIL_CONNECT_HPP
#include <boost/asio/ip/tcp.hpp>
#include <sstream>
#include "urdl/detail/coroutine.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace detail {
inline boost::system::error_code connect(
boost::asio::ip::tcp::socket::lowest_layer_type& socket,
boost::asio::ip::tcp::resolver& resolver,
const url& u, boost::system::error_code& ec)
{
// Create a query that corresponds to the url.
std::ostringstream port_string;
port_string << u.port();
boost::asio::ip::tcp::resolver::query query(u.host(), port_string.str());
// Get a list of endpoints corresponding to the query.
boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query, ec);
if (ec)
return ec;
// Try each endpoint until we successfully establish a connection.
ec = boost::asio::error::host_not_found;
while (ec && iter != boost::asio::ip::tcp::resolver::iterator())
{
socket.close(ec);
socket.connect(*iter++, ec);
}
if (ec)
return ec;
// Disable the Nagle algorithm on all sockets.
return socket.set_option(boost::asio::ip::tcp::no_delay(true), ec);
}
template <typename Handler>
class connect_coro : coroutine
{
public:
connect_coro(Handler handler,
boost::asio::ip::tcp::socket::lowest_layer_type& socket,
boost::asio::ip::tcp::resolver& resolver)
: handler_(handler),
socket_(socket),
resolver_(resolver)
{
}
void operator()(boost::system::error_code ec,
boost::asio::ip::tcp::resolver::iterator iter)
{
iter_ = iter;
(*this)(ec);
}
void operator()(boost::system::error_code ec,
const boost::asio::ip::tcp::resolver::query* query = 0)
{
URDL_CORO_BEGIN;
// Open the socket to give the caller something to close to cancel the
// asynchronous operation.
socket_.open(boost::asio::ip::tcp::v4(), ec);
if (ec)
{
URDL_CORO_YIELD(socket_.get_io_service().post(
boost::asio::detail::bind_handler(*this, ec)));
handler_(ec);
return;
}
// Get a list of endpoints corresponding to the host name.
URDL_CORO_YIELD(resolver_.async_resolve(*query, *this));
if (ec)
{
handler_(ec);
return;
}
// Try each endpoint until we successfully establish a connection.
ec = boost::asio::error::host_not_found;
while (ec && iter_ != boost::asio::ip::tcp::resolver::iterator())
{
// Check whether the operation has been cancelled.
if (!socket_.is_open())
{
ec = boost::asio::error::operation_aborted;
handler_(ec);
return;
}
// Try next endpoint.
socket_.close(ec);
endpoint_ = *iter_++;
URDL_CORO_YIELD(socket_.async_connect(endpoint_, *this));
}
if (ec)
{
handler_(ec);
return;
}
// Check whether the operation has been cancelled.
if (!socket_.is_open())
{
ec = boost::asio::error::operation_aborted;
handler_(ec);
return;
}
// Disable the Nagle algorithm on all sockets.
socket_.set_option(boost::asio::ip::tcp::no_delay(true), ec);
handler_(ec);
URDL_CORO_END;
}
friend void* asio_handler_allocate(std::size_t size,
connect_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size, &this_handler->handler_);
}
friend void asio_handler_deallocate(void* pointer, std::size_t size,
connect_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(pointer, size, &this_handler->handler_);
}
template <typename Function>
friend void asio_handler_invoke(const Function& function,
connect_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, &this_handler->handler_);
}
private:
Handler handler_;
boost::asio::ip::tcp::socket::lowest_layer_type& socket_;
boost::asio::ip::tcp::resolver& resolver_;
boost::asio::ip::tcp::resolver::iterator iter_;
boost::asio::ip::tcp::endpoint endpoint_;
};
template <typename Handler>
void async_connect(boost::asio::ip::tcp::socket::lowest_layer_type& socket,
boost::asio::ip::tcp::resolver& resolver, const url& u, Handler handler)
{
std::ostringstream port_string;
port_string << u.port();
boost::asio::ip::tcp::resolver::query query(u.host(), port_string.str());
connect_coro<Handler>(handler, socket, resolver)(
boost::system::error_code(), &query);
}
} // namespace detail
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_DETAIL_CONNECT_HPP

View File

@ -1,55 +0,0 @@
//
// coroutine.hpp
// ~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_DETAIL_COROUTINE_HPP
#define URDL_DETAIL_COROUTINE_HPP
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace detail {
class coroutine
{
protected:
coroutine() : coro_value_(0) {}
int coro_value_;
};
#define URDL_CORO_BEGIN \
switch (this->coroutine::coro_value_) \
{ \
case 0:
#define URDL_CORO_YIELD_IMPL(s,n) \
do \
{ \
this->coroutine::coro_value_ = n; \
s; \
return; \
case n: \
; \
} while (0)
#if defined(_MSC_VER)
# define URDL_CORO_YIELD(s) URDL_CORO_YIELD_IMPL(s, __COUNTER__ + 1)
#else // defined(_MSC_VER)
# define URDL_CORO_YIELD(s) URDL_CORO_YIELD_IMPL(s, __LINE__)
#endif // defined(_MSC_VER)
#define URDL_CORO_END \
}
} // namespace detail
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_DETAIL_COROUTINE_HPP

View File

@ -1,129 +0,0 @@
//
// file_read_stream.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_DETAIL_FILE_READ_STREAM_HPP
#define URDL_DETAIL_FILE_READ_STREAM_HPP
#include <boost/config.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/detail/bind_handler.hpp>
#include <cctype>
#include <fstream>
#include "urdl/option_set.hpp"
#include "urdl/url.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace detail {
class file_read_stream
{
public:
explicit file_read_stream(boost::asio::io_service& io_service,
option_set& options)
: io_service_(io_service),
options_(options)
{
}
boost::system::error_code open(const url& u, boost::system::error_code& ec)
{
file_.clear();
std::string path = u.path();
#if defined(BOOST_WINDOWS)
if (path.length() >= 3 && path[0] == '/'
&& std::isalpha(path[1]) && path[2] == ':')
path = path.substr(1);
#endif // defined(BOOST_WINDOWS)
file_.open(path.c_str(), std::ios_base::in | std::ios_base::binary);
if (!file_)
{
ec = make_error_code(boost::system::errc::no_such_file_or_directory);
return ec;
}
ec = boost::system::error_code();
return ec;
}
template <typename Handler>
void async_open(const url& u, Handler handler)
{
boost::system::error_code ec;
open(u, ec);
io_service_.post(boost::asio::detail::bind_handler(handler, ec));
}
boost::system::error_code close(boost::system::error_code& ec)
{
file_.close();
file_.clear();
ec = boost::system::error_code();
return ec;
}
bool is_open() const
{
// Some older versions of libstdc++ have a non-const is_open().
return const_cast<std::ifstream&>(file_).is_open();
}
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
boost::system::error_code& ec)
{
if (!file_)
{
ec = boost::asio::error::eof;
return 0;
}
typename MutableBufferSequence::const_iterator iter = buffers.begin();
typename MutableBufferSequence::const_iterator end = buffers.end();
for (; iter != end; ++iter)
{
boost::asio::mutable_buffer buffer(*iter);
size_t length = boost::asio::buffer_size(buffer);
if (length > 0)
{
file_.read(boost::asio::buffer_cast<char*>(buffer), length);
length = file_.gcount();
if (length == 0 && !file_)
ec = boost::asio::error::eof;
return length;
}
}
ec = boost::system::error_code();
return 0;
}
template <typename MutableBufferSequence, typename Handler>
void async_read_some(const MutableBufferSequence& buffers, Handler handler)
{
boost::system::error_code ec;
std::size_t bytes_transferred = read_some(buffers, ec);
io_service_.post(boost::asio::detail::bind_handler(
handler, ec, bytes_transferred));
}
private:
boost::asio::io_service& io_service_;
option_set& options_;
std::ifstream file_;
};
} // namespace detail
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_DETAIL_FILE_READ_STREAM_HPP

View File

@ -1,279 +0,0 @@
//
// handshake.hpp
// ~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_DETAIL_HANDSHAKE_HPP
#define URDL_DETAIL_HANDSHAKE_HPP
#include <cstring>
#include <cctype>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/detail/bind_handler.hpp>
#include "urdl/detail/coroutine.hpp"
#if !defined(URDL_DISABLE_SSL)
# include <boost/asio/ssl.hpp>
# include <openssl/x509v3.h>
#endif // !defined(URDL_DISABLE_SSL)
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace detail {
inline boost::system::error_code handshake(
boost::asio::ip::tcp::socket& /*socket*/,
const std::string& /*host*/, boost::system::error_code& ec)
{
ec = boost::system::error_code();
return ec;
}
template <typename Handler>
void async_handshake(boost::asio::ip::tcp::socket& socket,
const std::string& /*host*/, Handler handler)
{
boost::system::error_code ec;
socket.get_io_service().post(boost::asio::detail::bind_handler(handler, ec));
}
#if !defined(URDL_DISABLE_SSL)
inline bool match_pattern(const char* pattern,
std::size_t pattern_length, const char* host)
{
const char* p = pattern;
const char* p_end = p + pattern_length;
const char* h = host;
while (p != p_end && *h)
{
if (*p == '*')
{
++p;
while (*h && *h != '.')
if (match_pattern(p, p_end - p, h++))
return true;
}
else if (std::tolower(*p) == std::tolower(*h))
{
++p;
++h;
}
else
{
return false;
}
}
return p == p_end && !*h;
}
inline bool certificate_matches_host(X509* cert, const std::string& host)
{
// Try converting host name to an address. If it is an address then we need
// to look for an IP address in the certificate rather than a host name.
boost::system::error_code ec;
boost::asio::ip::address address
= boost::asio::ip::address::from_string(host, ec);
bool is_address = !ec;
// Go through the alternate names in the certificate looking for matching DNS
// or IP address entries.
GENERAL_NAMES* gens = static_cast<GENERAL_NAMES*>(
X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0));
for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i)
{
GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i);
if (gen->type == GEN_DNS && !is_address)
{
ASN1_IA5STRING* domain = gen->d.dNSName;
if (domain->type == V_ASN1_IA5STRING
&& domain->data && domain->length)
{
const char* pattern = reinterpret_cast<const char*>(domain->data);
std::size_t pattern_length = domain->length;
if (match_pattern(pattern, pattern_length, host.c_str()))
{
GENERAL_NAMES_free(gens);
return true;
}
}
}
else if (gen->type == GEN_IPADD && is_address)
{
ASN1_OCTET_STRING* ip_address = gen->d.iPAddress;
if (ip_address->type == V_ASN1_OCTET_STRING && ip_address->data)
{
if (address.is_v4() && ip_address->length == 4)
{
boost::asio::ip::address_v4::bytes_type address_bytes
= address.to_v4().to_bytes();
if (std::memcmp(address_bytes.data(), ip_address->data, 4) == 0)
{
GENERAL_NAMES_free(gens);
return true;
}
}
else if (address.is_v6() && ip_address->length == 16)
{
boost::asio::ip::address_v6::bytes_type address_bytes
= address.to_v6().to_bytes();
if (std::memcmp(address_bytes.data(), ip_address->data, 16) == 0)
{
GENERAL_NAMES_free(gens);
return true;
}
}
}
}
}
GENERAL_NAMES_free(gens);
// No match in the alternate names, so try the common names. We should only
// use the "most specific" common name, which is the last one in the list.
X509_NAME* name = X509_get_subject_name(cert);
int i = -1;
ASN1_STRING* common_name = 0;
while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0)
{
X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i);
common_name = X509_NAME_ENTRY_get_data(name_entry);
}
if (common_name && common_name->data && common_name->length)
{
const char* pattern = reinterpret_cast<const char*>(common_name->data);
std::size_t pattern_length = common_name->length;
if (match_pattern(pattern, pattern_length, host.c_str()))
return true;
}
return false;
}
inline boost::system::error_code handshake(
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& socket,
const std::string& host, boost::system::error_code& ec)
{
// Perform SSL handshake.
socket.handshake(boost::asio::ssl::stream_base::client, ec);
if (ec)
return ec;
// Verify the certificate returned by the host.
if (X509* cert = SSL_get_peer_certificate(socket.impl()->ssl))
{
if (SSL_get_verify_result(socket.impl()->ssl) == X509_V_OK)
{
if (certificate_matches_host(cert, host))
ec = boost::system::error_code();
else
ec = make_error_code(boost::system::errc::permission_denied);
}
else
ec = make_error_code(boost::system::errc::permission_denied);
X509_free(cert);
}
else
ec = make_error_code(boost::system::errc::permission_denied);
return ec;
}
template <typename Handler>
class handshake_coro : coroutine
{
public:
handshake_coro(Handler handler,
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& socket,
const std::string& host)
: handler_(handler),
socket_(socket),
host_(host)
{
}
void operator()(boost::system::error_code ec)
{
URDL_CORO_BEGIN;
// Perform SSL handshake.
URDL_CORO_YIELD(socket_.async_handshake(
boost::asio::ssl::stream_base::client, *this));
if (ec)
{
handler_(ec);
return;
}
// Verify the certificate returned by the host.
if (X509* cert = SSL_get_peer_certificate(socket_.impl()->ssl))
{
if (SSL_get_verify_result(socket_.impl()->ssl) == X509_V_OK)
{
if (certificate_matches_host(cert, host_))
ec = boost::system::error_code();
else
ec = make_error_code(boost::system::errc::permission_denied);
}
else
ec = make_error_code(boost::system::errc::permission_denied);
X509_free(cert);
}
else
ec = make_error_code(boost::system::errc::permission_denied);
handler_(ec);
URDL_CORO_END;
}
friend void* asio_handler_allocate(std::size_t size,
handshake_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size, &this_handler->handler_);
}
friend void asio_handler_deallocate(void* pointer, std::size_t size,
handshake_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(pointer, size, &this_handler->handler_);
}
template <typename Function>
friend void asio_handler_invoke(const Function& function,
handshake_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, &this_handler->handler_);
}
private:
Handler handler_;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& socket_;
std::string host_;
};
template <typename Handler>
void async_handshake(
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& socket,
const std::string& host, Handler handler)
{
handshake_coro<Handler>(handler, socket, host)(boost::system::error_code());
}
#endif // !defined(URDL_DISABLE_SSL)
} // namespace detail
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_DETAIL_HANDSHAKE_HPP

View File

@ -1,533 +0,0 @@
//
// http_read_stream.hpp
// ~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_DETAIL_HTTP_READ_STREAM_HPP
#define URDL_DETAIL_HTTP_READ_STREAM_HPP
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/detail/bind_handler.hpp>
#include <algorithm>
#include <ostream>
#include <iterator>
#include "urdl/http.hpp"
#include "urdl/option_set.hpp"
#include "urdl/url.hpp"
#include "urdl/detail/connect.hpp"
#include "urdl/detail/coroutine.hpp"
#include "urdl/detail/handshake.hpp"
#include "urdl/detail/parsers.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace detail {
template <typename Stream>
class http_read_stream
{
public:
explicit http_read_stream(boost::asio::io_service& io_service,
option_set& options)
: resolver_(io_service),
socket_(io_service),
options_(options),
content_length_(0)
{
}
template <typename Arg>
http_read_stream(boost::asio::io_service& io_service,
option_set& options, Arg& arg)
: resolver_(io_service),
socket_(io_service, arg),
options_(options),
content_length_(0)
{
}
boost::system::error_code open(const url& u, boost::system::error_code& ec)
{
// Fail if the socket is already open.
if (socket_.lowest_layer().is_open())
{
ec = boost::asio::error::already_open;
return ec;
}
// Establish a connection to the HTTP server.
connect(socket_.lowest_layer(), resolver_, u, ec);
if (ec)
return ec;
// Perform SSL handshake if required.
handshake(socket_, u.host(), ec);
if (ec)
return ec;
// Get the HTTP options used to build the request.
std::string request_method
= options_.get_option<urdl::http::request_method>().value();
std::string request_content
= options_.get_option<urdl::http::request_content>().value();
std::string request_content_type
= options_.get_option<urdl::http::request_content_type>().value();
std::string user_agent
= options_.get_option<urdl::http::user_agent>().value();
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
std::ostream request_stream(&request_buffer_);
request_stream << request_method << " ";
request_stream << u.to_string(url::path_component | url::query_component);
request_stream << " HTTP/1.0\r\n";
request_stream << "Host: ";
request_stream << u.to_string(url::host_component | url::port_component);
request_stream << "\r\n";
request_stream << "Accept: */*\r\n";
if (request_content.length())
{
request_stream << "Content-Length: ";
request_stream << request_content.length() << "\r\n";
if (request_content_type.length())
{
request_stream << "Content-Type: ";
request_stream << request_content_type << "\r\n";
}
}
if (user_agent.length())
request_stream << "User-Agent: " << user_agent << "\r\n";
request_stream << "Connection: close\r\n\r\n";
request_stream << request_content;
// Send the request.
boost::asio::write(socket_, request_buffer_,
boost::asio::transfer_all(), ec);
if (ec)
return ec;
int status_code = 0;
for (;;)
{
// Read the reply status line.
boost::asio::read_until(socket_, reply_buffer_, "\r\n", ec);
if (ec)
return ec;
// Extract the response code from the status line.
int version_major = 0;
int version_minor = 0;
if (!parse_http_status_line(
std::istreambuf_iterator<char>(&reply_buffer_),
std::istreambuf_iterator<char>(),
version_major, version_minor, status_code))
{
ec = http::errc::malformed_status_line;
return ec;
}
// A "continue" header means we need to keep waiting.
if (status_code != http::errc::continue_request)
break;
}
// Read list of headers and save them. If there's anything left in the reply
// buffer afterwards, it's the start of the content returned by the HTTP
// server.
std::size_t bytes_transferred = boost::asio::read_until(
socket_, reply_buffer_, "\r\n\r\n", ec);
headers_.resize(bytes_transferred);
reply_buffer_.sgetn(&headers_[0], bytes_transferred);
if (ec)
return ec;
// Parse the headers to get Content-Type and Content-Length.
if (!parse_http_headers(headers_.begin(), headers_.end(),
content_type_, content_length_, location_))
{
ec = http::errc::malformed_response_headers;
return ec;
}
// Check the response code to see if we got the page correctly.
if (status_code != http::errc::ok)
ec = make_error_code(static_cast<http::errc::errc_t>(status_code));
return ec;
}
template <typename Handler>
class open_coro : coroutine
{
public:
open_coro(Handler handler, boost::asio::ip::tcp::resolver& resolver,
Stream& socket, const option_set& options,
boost::asio::streambuf& request_buffer,
boost::asio::streambuf& reply_buffer, const url& u,
std::string& headers, std::string& content_type,
std::size_t& content_length, std::string& location)
: handler_(handler),
resolver_(resolver),
socket_(socket),
options_(options),
request_buffer_(request_buffer),
reply_buffer_(reply_buffer),
url_(u),
headers_(headers),
status_code_(0),
content_type_(content_type),
content_length_(content_length),
location_(location)
{
}
void operator()(boost::system::error_code ec,
std::size_t bytes_transferred = 0)
{
URDL_CORO_BEGIN;
// Fail if the socket is already open.
if (socket_.lowest_layer().is_open())
{
ec = boost::asio::error::already_open;
URDL_CORO_YIELD(socket_.get_io_service().post(
boost::asio::detail::bind_handler(*this, ec)));
handler_(ec);
return;
}
// Establish a connection to the HTTP server.
URDL_CORO_YIELD(async_connect(socket_.lowest_layer(),
resolver_, url_, *this));
if (ec)
{
handler_(ec);
return;
}
// Perform SSL handshake if required.
URDL_CORO_YIELD(async_handshake(socket_, url_.host(), *this));
if (ec)
{
handler_(ec);
return;
}
{
// Get the HTTP options used to build the request.
std::string request_method
= options_.get_option<urdl::http::request_method>().value();
std::string request_content
= options_.get_option<urdl::http::request_content>().value();
std::string request_content_type
= options_.get_option<urdl::http::request_content_type>().value();
std::string user_agent
= options_.get_option<urdl::http::user_agent>().value();
// Form the request. We specify the "Connection: close" header so that
// the server will close the socket after transmitting the response.
// This will allow us to treat all data up until the EOF as the
// content.
std::ostream request_stream(&request_buffer_);
request_stream << request_method << " ";
request_stream << url_.to_string(
url::path_component | url::query_component);
request_stream << " HTTP/1.0\r\n";
request_stream << "Host: ";
request_stream << url_.to_string(
url::host_component | url::port_component);
request_stream << "\r\n";
request_stream << "Accept: */*\r\n";
if (request_content.length())
{
request_stream << "Content-Length: ";
request_stream << request_content.length() << "\r\n";
if (request_content_type.length())
{
request_stream << "Content-Type: ";
request_stream << request_content_type << "\r\n";
}
}
if (user_agent.length())
request_stream << "User-Agent: " << user_agent << "\r\n";
request_stream << "Connection: close\r\n\r\n";
request_stream << request_content;
}
// Send the request.
URDL_CORO_YIELD(boost::asio::async_write(socket_,
request_buffer_, boost::asio::transfer_all(), *this));
if (ec)
{
handler_(ec);
return;
}
for (;;)
{
// Read the reply status line.
URDL_CORO_YIELD(boost::asio::async_read_until(socket_,
reply_buffer_, "\r\n", *this));
if (ec)
{
handler_(ec);
return;
}
// Check the response code to see if we got the page correctly.
{
int version_major = 0;
int version_minor = 0;
if (!parse_http_status_line(
std::istreambuf_iterator<char>(&reply_buffer_),
std::istreambuf_iterator<char>(),
version_major, version_minor, status_code_))
{
ec = http::errc::malformed_status_line;
handler_(ec);
return;
}
}
// A "continue" header means we need to keep waiting.
if (status_code_ != http::errc::continue_request)
break;
}
// Read list of headers and save them. If there's anything left in the
// reply buffer afterwards, it's the start of the content returned by the
// HTTP server.
URDL_CORO_YIELD(boost::asio::async_read_until(socket_,
reply_buffer_, "\r\n\r\n", *this));
headers_.resize(bytes_transferred);
reply_buffer_.sgetn(&headers_[0], bytes_transferred);
if (ec)
{
handler_(ec);
return;
}
// Parse the headers to get Content-Type and Content-Length.
if (!parse_http_headers(headers_.begin(), headers_.end(),
content_type_, content_length_, location_))
{
ec = http::errc::malformed_response_headers;
handler_(ec);
return;
}
// Check the response code to see if we got the page correctly.
if (status_code_ != http::errc::ok)
ec = make_error_code(static_cast<http::errc::errc_t>(status_code_));
handler_(ec);
URDL_CORO_END;
}
friend void* asio_handler_allocate(std::size_t size,
open_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size, &this_handler->handler_);
}
friend void asio_handler_deallocate(void* pointer, std::size_t size,
open_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(pointer, size, &this_handler->handler_);
}
template <typename Function>
friend void asio_handler_invoke(const Function& function,
open_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, &this_handler->handler_);
}
private:
Handler handler_;
boost::asio::ip::tcp::resolver& resolver_;
Stream& socket_;
const option_set& options_;
boost::asio::streambuf& request_buffer_;
boost::asio::streambuf& reply_buffer_;
url url_;
std::string& headers_;
int status_code_;
std::string& content_type_;
std::size_t& content_length_;
std::string& location_;
};
template <typename Handler>
void async_open(const url& u, Handler handler)
{
open_coro<Handler>(handler, resolver_, socket_, options_, request_buffer_,
reply_buffer_, u, headers_, content_type_, content_length_, location_)(
boost::system::error_code(), 0);
}
boost::system::error_code close(boost::system::error_code& ec)
{
resolver_.cancel();
if (!socket_.lowest_layer().close(ec))
{
request_buffer_.consume(request_buffer_.size());
reply_buffer_.consume(reply_buffer_.size());
headers_.clear();
content_type_.clear();
content_length_ = 0;
location_.clear();
}
return ec;
}
bool is_open() const
{
return socket_.lowest_layer().is_open();
}
std::string content_type() const
{
return content_type_;
}
std::size_t content_length() const
{
return content_length_;
}
std::string location() const
{
return location_;
}
std::string headers() const
{
return headers_;
}
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
boost::system::error_code& ec)
{
// If we have any data in the reply_buffer_, return that first.
if (reply_buffer_.size() > 0)
{
std::size_t bytes_transferred = 0;
typename MutableBufferSequence::const_iterator iter = buffers.begin();
typename MutableBufferSequence::const_iterator end = buffers.end();
for (; iter != end && reply_buffer_.size() > 0; ++iter)
{
boost::asio::mutable_buffer buffer(*iter);
size_t length = boost::asio::buffer_size(buffer);
if (length > 0)
{
bytes_transferred += reply_buffer_.sgetn(
boost::asio::buffer_cast<char*>(buffer), length);
}
}
ec = boost::system::error_code();
return bytes_transferred;
}
// Otherwise we forward the call to the underlying socket.
std::size_t bytes_transferred = socket_.read_some(buffers, ec);
if (ec == boost::asio::error::shut_down)
ec = boost::asio::error::eof;
return bytes_transferred;
}
template <typename Handler>
class read_handler
{
public:
read_handler(Handler handler)
: handler_(handler)
{
}
void operator()(boost::system::error_code ec, std::size_t bytes_transferred)
{
if (ec == boost::asio::error::shut_down)
ec = boost::asio::error::eof;
handler_(ec, bytes_transferred);
}
friend void* asio_handler_allocate(std::size_t size,
read_handler<Handler>* this_handler)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size, &this_handler->handler_);
}
friend void asio_handler_deallocate(void* pointer, std::size_t size,
read_handler<Handler>* this_handler)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(pointer, size, &this_handler->handler_);
}
template <typename Function>
friend void asio_handler_invoke(const Function& function,
read_handler<Handler>* this_handler)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, &this_handler->handler_);
}
private:
Handler handler_;
};
template <typename MutableBufferSequence, typename Handler>
void async_read_some(const MutableBufferSequence& buffers, Handler handler)
{
// If we have any data in the reply_buffer_, return that first.
if (reply_buffer_.size() > 0)
{
boost::system::error_code ec;
std::size_t bytes_transferred = read_some(buffers, ec);
socket_.get_io_service().post(boost::asio::detail::bind_handler(
handler, ec, bytes_transferred));
return;
}
// Otherwise we forward the call to the underlying socket.
socket_.async_read_some(buffers, read_handler<Handler>(handler));
}
private:
boost::asio::ip::tcp::resolver resolver_;
Stream socket_;
option_set& options_;
boost::asio::streambuf request_buffer_;
boost::asio::streambuf reply_buffer_;
std::string headers_;
std::string content_type_;
std::size_t content_length_;
std::string location_;
};
} // namespace detail
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_DETAIL_HTTP_READ_STREAM_HPP

View File

@ -1,297 +0,0 @@
//
// parsers.hpp
// ~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_DETAIL_PARSERS_HPP
#define URDL_DETAIL_PARSERS_HPP
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <string>
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace detail {
inline bool is_char(int c)
{
return c >= 0 && c <= 127;
}
inline bool is_ctl(int c)
{
return (c >= 0 && c <= 31) || c == 127;
}
inline bool is_tspecial(int c)
{
switch (c)
{
case '(': case ')': case '<': case '>': case '@':
case ',': case ';': case ':': case '\\': case '"':
case '/': case '[': case ']': case '?': case '=':
case '{': case '}': case ' ': case '\t':
return true;
default:
return false;
}
}
inline bool is_digit(int c)
{
return c >= '0' && c <= '9';
}
inline bool tolower_compare(char a, char b)
{
return std::tolower(a) == std::tolower(b);
}
inline bool headers_equal(const std::string& a, const std::string& b)
{
if (a.length() != b.length())
return false;
return std::equal(a.begin(), a.end(), b.begin(), tolower_compare);
}
inline void check_header(const std::string& name, const std::string& value,
std::string& content_type, std::size_t& content_length,
std::string& location)
{
if (headers_equal(name, "Content-Type"))
content_type = value;
else if (headers_equal(name, "Content-Length"))
content_length = std::atoi(value.c_str());
else if (headers_equal(name, "Location"))
location = value;
}
template <typename Iterator>
bool parse_http_status_line(Iterator begin, Iterator end,
int& version_major, int& version_minor, int& status)
{
enum
{
http_version_h,
http_version_t_1,
http_version_t_2,
http_version_p,
http_version_slash,
http_version_major_start,
http_version_major,
http_version_minor_start,
http_version_minor,
status_code_start,
status_code,
reason_phrase,
linefeed,
fail
} state = http_version_h;
Iterator iter = begin;
std::string reason;
while (iter != end && state != fail)
{
char c = *iter++;
switch (state)
{
case http_version_h:
state = (c == 'H') ? http_version_t_1 : fail;
break;
case http_version_t_1:
state = (c == 'T') ? http_version_t_2 : fail;
break;
case http_version_t_2:
state = (c == 'T') ? http_version_p : fail;
break;
case http_version_p:
state = (c == 'P') ? http_version_slash : fail;
break;
case http_version_slash:
state = (c == '/') ? http_version_major_start : fail;
break;
case http_version_major_start:
if (is_digit(c))
{
version_major = version_major * 10 + c - '0';
state = http_version_major;
}
else
state = fail;
break;
case http_version_major:
if (c == '.')
state = http_version_minor_start;
else if (is_digit(c))
version_major = version_major * 10 + c - '0';
else
state = fail;
break;
case http_version_minor_start:
if (is_digit(c))
{
version_minor = version_minor * 10 + c - '0';
state = http_version_minor;
}
else
state = fail;
break;
case http_version_minor:
if (c == ' ')
state = status_code_start;
else if (is_digit(c))
version_minor = version_minor * 10 + c - '0';
else
state = fail;
break;
case status_code_start:
if (is_digit(c))
{
status = status * 10 + c - '0';
state = status_code;
}
else
state = fail;
break;
case status_code:
if (c == ' ')
state = reason_phrase;
else if (is_digit(c))
status = status * 10 + c - '0';
else
state = fail;
break;
case reason_phrase:
if (c == '\r')
state = linefeed;
else if (is_ctl(c))
state = fail;
else
reason.push_back(c);
break;
case linefeed:
return (c == '\n');
default:
return false;
}
}
return false;
}
template <typename Iterator>
bool parse_http_headers(Iterator begin, Iterator end,
std::string& content_type, std::size_t& content_length,
std::string& location)
{
enum
{
first_header_line_start,
header_line_start,
header_lws,
header_name,
space_before_header_value,
header_value,
linefeed,
final_linefeed,
fail
} state = first_header_line_start;
Iterator iter = begin;
std::string reason;
std::string name;
std::string value;
while (iter != end && state != fail)
{
char c = *iter++;
switch (state)
{
case first_header_line_start:
if (c == '\r')
state = final_linefeed;
else if (!is_char(c) || is_ctl(c) || is_tspecial(c))
state = fail;
else
{
name.push_back(c);
state = header_name;
}
break;
case header_line_start:
if (c == '\r')
{
check_header(name, value, content_type, content_length, location);
name.clear();
value.clear();
state = final_linefeed;
}
else if (c == ' ' || c == '\t')
state = header_lws;
else if (!is_char(c) || is_ctl(c) || is_tspecial(c))
state = fail;
else
{
check_header(name, value, content_type, content_length, location);
name.clear();
value.clear();
name.push_back(c);
state = header_name;
}
break;
case header_lws:
if (c == '\r')
state = linefeed;
else if (c == ' ' || c == '\t')
; // Discard character.
else if (is_ctl(c))
state = fail;
else
{
state = header_value;
value.push_back(c);
}
break;
case header_name:
if (c == ':')
state = space_before_header_value;
else if (!is_char(c) || is_ctl(c) || is_tspecial(c))
state = fail;
else
name.push_back(c);
break;
case space_before_header_value:
state = (c == ' ') ? header_value : fail;
break;
case header_value:
if (c == '\r')
state = linefeed;
else if (is_ctl(c))
state = fail;
else
value.push_back(c);
break;
case linefeed:
state = (c == '\n') ? header_line_start : fail;
break;
case final_linefeed:
return (c == '\n');
default:
return false;
}
}
return false;
}
} // namespace detail
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_DETAIL_PARSERS_HPP

View File

@ -1,85 +0,0 @@
//
// detail/scoped_ptr.hpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_DETAIL_SCOPED_PTR_HPP
#define URDL_DETAIL_SCOPED_PTR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace detail {
template <typename T>
class scoped_ptr
{
public:
// Constructor.
explicit scoped_ptr(T* p = 0)
: p_(p)
{
}
// Destructor.
~scoped_ptr()
{
delete p_;
}
// Access.
T* get() const
{
return p_;
}
// Access.
T* operator->()
{
return p_;
}
// Dereference.
T& operator*()
{
return *p_;
}
// Reset pointer.
void reset(T* p = 0)
{
delete p_;
p_ = p;
}
// Release pointer.
T* release()
{
T* p = p_;
p_ = 0;
return p;
}
private:
// Disallow copying and assignment.
scoped_ptr(const scoped_ptr&);
scoped_ptr& operator=(const scoped_ptr&);
T* p_;
};
} // namespace detail
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_DETAIL_SCOPED_PTR_HPP

View File

@ -1,594 +0,0 @@
//
// http.hpp
// ~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_HTTP_HPP
#define URDL_HTTP_HPP
#include <string>
#include <boost/system/error_code.hpp>
#include "urdl/detail/config.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace http {
/// Gets the error category for HTTP errors.
/**
* @returns The @c boost::system::error_category used for HTTP errors.
*
* @par Requirements
* @e Header: @c <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
URDL_DECL const boost::system::error_category& error_category();
/// Option to specify the HTTP request method.
/**
* @par Remarks
* The default request method is "GET".
*
* @par Example
* To set the request method for an object of class @c urdl::istream:
* @code
* urdl::istream is;
* is.set_option(urdl::http::request_method("HEAD"));
* is.open("http://www.boost.org");
* @endcode
*
* To set the request method for an object of class @c urdl::read_stream:
* @code
* urdl::read_stream stream;
* stream.set_option(urdl::http::request_method("HEAD"));
* stream.open("http://www.boost.org");
* @endcode
*
* @par Requirements
* @e Header: @c <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
class request_method
{
public:
/// Constructs an object of class @c request_method.
/**
* @par Remarks
* Postcondition: <tt>value() == "GET"</tt>.
*/
request_method()
: value_("GET")
{
}
/// Constructs an object of class @c request_method.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
explicit request_method(const std::string& v)
: value_(v)
{
}
/// Gets the value of the option.
/**
* @returns The value of the option.
*/
std::string value() const
{
return value_;
}
/// Sets the value of the option.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
void value(const std::string& v)
{
value_ = v;
}
private:
std::string value_;
};
/// Option to specify content to accompany an HTTP request.
/**
* @par Remarks
* The default is for no content to be sent.
*
* @par Example
* To add content to the HTTP request using an object of class @c urdl::istream:
* @code
* urdl::istream is;
* is.set_option(urdl::http::request_method("POST"));
* is.set_option(urdl::http::request_content("Hello, world!"));
* is.set_option(urdl::http::request_content_type("text/plain"));
* is.open("http://host/path");
* @endcode
*
* To add content to the HTTP request using an object of class
* @c urdl::read_stream:
* @code
* urdl::read_stream stream;
* stream.set_option(urdl::http::request_method("POST"));
* stream.set_option(urdl::http::request_content("Hello, world!"));
* stream.set_option(urdl::http::request_content_type("text/plain"));
* stream.open("http://host/path");
* @endcode
*
* @par Requirements
* @e Header: @c <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
class request_content
{
public:
/// Constructs an object of class @c request_content.
/**
* @par Remarks
* Postcondition: <tt>value() == ""</tt>.
*/
request_content()
: value_("")
{
}
/// Constructs an object of class @c request_content.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
explicit request_content(const std::string& v)
: value_(v)
{
}
/// Gets the value of the option.
/**
* @returns The value of the option.
*/
std::string value() const
{
return value_;
}
/// Sets the value of the option.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
void value(const std::string& v)
{
value_ = v;
}
private:
std::string value_;
};
/// Option to specify the type of the content that accompanies an HTTP request.
/**
* @par Remarks
* The default is for no content type to be specified in the request.
*
* @par Example
* To add content to the HTTP request using an object of class @c urdl::istream:
* @code
* urdl::istream is;
* is.set_option(urdl::http::request_method("POST"));
* is.set_option(urdl::http::request_content("Hello, world!"));
* is.set_option(urdl::http::request_content_type("text/plain"));
* is.open("http://host/path");
* @endcode
*
* To add content to the HTTP request using an object of class
* @c urdl::read_stream:
* @code
* urdl::read_stream stream;
* stream.set_option(urdl::http::request_method("POST"));
* stream.set_option(urdl::http::request_content("Hello, world!"));
* stream.set_option(urdl::http::request_content_type("text/plain"));
* stream.open("http://host/path");
* @endcode
*
* @par Requirements
* @e Header: @c <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
class request_content_type
{
public:
/// Constructs an object of class @c request_content_type.
/**
* @par Remarks
* Postcondition: <tt>value() == ""</tt>.
*/
request_content_type()
: value_("")
{
}
/// Constructs an object of class @c request_content_type.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
explicit request_content_type(const std::string& v)
: value_(v)
{
}
/// Gets the value of the option.
/**
* @returns The value of the option.
*/
std::string value() const
{
return value_;
}
/// Sets the value of the option.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
void value(const std::string& v)
{
value_ = v;
}
private:
std::string value_;
};
/// Option to specify the maximum number of allowed HTTP redirects.
/**
* @par Remarks
* The default value is for there to be no limit on the number of allowed
* redirects. Set the option to 0 to disable HTTP redirects.
*
* @par Example
* To set maximum number of redirects for an object of class @c urdl::istream:
* @code
* urdl::istream is;
* is.set_option(urdl::http::max_redirects(1));
* is.open("http://www.boost.org");
* @endcode
*
* To set maximum number of redirects for an object of class
* @c urdl::read_stream:
* @code
* urdl::read_stream stream;
* stream.set_option(urdl::http::max_redirects(1));
* stream.open("http://www.boost.org");
* @endcode
*
* @par Requirements
* @e Header: @c <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
class max_redirects
{
public:
/// Constructs an object of class @c max_redirects.
/**
* @par Remarks
* Postcondition: <tt>value() == std::numeric_limits<std::size_t>::max()</tt>.
*/
max_redirects()
: value_(~std::size_t(0))
{
}
/// Constructs an object of class @c max_redirects.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
explicit max_redirects(std::size_t v)
: value_(v)
{
}
/// Gets the value of the option.
/**
* @returns The value of the option.
*/
std::size_t value() const
{
return value_;
}
/// Sets the value of the option.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
void value(std::size_t v)
{
value_ = v;
}
private:
std::size_t value_;
};
/// Option to specify the user agent identifier.
/**
* @par Remarks
* The default is to not specify the user agent.
*
* @par Example
* To set the user agent for an object of class @c urdl::istream:
* @code
* urdl::istream is;
* is.set_option(urdl::http::user_agent("Urdl"));
* is.open("http://www.boost.org");
* @endcode
*
* To set the user agent for an object of class @c urdl::read_stream:
* @code
* urdl::read_stream stream;
* stream.set_option(urdl::http::user_agent("Urdl"));
* stream.open("http://www.boost.org");
* @endcode
*
* @par Requirements
* @e Header: @c <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
class user_agent
{
public:
/// Constructs an object of class @c user_agent.
/**
* @par Remarks
* Postcondition: <tt>value() == ""</tt>.
*/
user_agent()
: value_("")
{
}
/// Constructs an object of class @c user_agent.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
explicit user_agent(const std::string& v)
: value_(v)
{
}
/// Gets the value of the option.
/**
* @returns The value of the option.
*/
std::string value() const
{
return value_;
}
/// Sets the value of the option.
/**
* @param v The desired value for the option.
*
* @par Remarks
* Postcondition: <tt>value() == v</tt>
*/
void value(const std::string& v)
{
value_ = v;
}
private:
std::string value_;
};
namespace errc {
/// HTTP error codes.
/**
* The enumerators of type @c errc_t are implicitly convertible to objects of
* type @c boost::system::error_code.
*
* @par Requirements
* @e Header: @c <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
enum errc_t
{
// Client-generated errors.
/// The response's status line was malformed.
malformed_status_line = 1,
/// The response's headers were malformed.
malformed_response_headers = 2,
// Server-generated status codes.
/// The server-generated status code "100 Continue".
continue_request = 100,
/// The server-generated status code "101 Switching Protocols".
switching_protocols = 101,
/// The server-generated status code "200 OK".
ok = 200,
/// The server-generated status code "201 Created".
created = 201,
/// The server-generated status code "202 Accepted".
accepted = 202,
/// The server-generated status code "203 Non-Authoritative Information".
non_authoritative_information = 203,
/// The server-generated status code "204 No Content".
no_content = 204,
/// The server-generated status code "205 Reset Content".
reset_content = 205,
/// The server-generated status code "206 Partial Content".
partial_content = 206,
/// The server-generated status code "300 Multiple Choices".
multiple_choices = 300,
/// The server-generated status code "301 Moved Permanently".
moved_permanently = 301,
/// The server-generated status code "302 Found".
found = 302,
/// The server-generated status code "303 See Other".
see_other = 303,
/// The server-generated status code "304 Not Modified".
not_modified = 304,
/// The server-generated status code "305 Use Proxy".
use_proxy = 305,
/// The server-generated status code "307 Temporary Redirect".
temporary_redirect = 307,
/// The server-generated status code "400 Bad Request".
bad_request = 400,
/// The server-generated status code "401 Unauthorized".
unauthorized = 401,
/// The server-generated status code "402 Payment Required".
payment_required = 402,
/// The server-generated status code "403 Forbidden".
forbidden = 403,
/// The server-generated status code "404 Not Found".
not_found = 404,
/// The server-generated status code "405 Method Not Allowed".
method_not_allowed = 405,
/// The server-generated status code "406 Not Acceptable".
not_acceptable = 406,
/// The server-generated status code "407 Proxy Authentication Required".
proxy_authentication_required = 407,
/// The server-generated status code "408 Request Time-out".
request_timeout = 408,
/// The server-generated status code "409 Conflict".
conflict = 409,
/// The server-generated status code "410 Gone".
gone = 410,
/// The server-generated status code "411 Length Required".
length_required = 411,
/// The server-generated status code "412 Precondition Failed".
precondition_failed = 412,
/// The server-generated status code "413 Request Entity Too Large".
request_entity_too_large = 413,
/// The server-generated status code "414 Request URI Too Large".
request_uri_too_large = 414,
/// The server-generated status code "415 Unsupported Media Type".
unsupported_media_type = 415,
/// The server-generated status code "416 Requested Range Not Satisfiable".
requested_range_not_satisfiable = 416,
/// The server-generated status code "417 Expectation Failed".
expectation_failed = 417,
/// The server-generated status code "500 Internal Server Error".
internal_server_error = 500,
/// The server-generated status code "501 Not Implemented".
not_implemented = 501,
/// The server-generated status code "502 Bad Gateway".
bad_gateway = 502,
/// The server-generated status code "503 Service Unavailable".
service_unavailable = 503,
/// The server-generated status code "504 Gateway Timeout".
gateway_timeout = 504,
/// The server-generated status code "505 HTTP Version Not Supported".
version_not_supported = 505
};
/// Converts a value of type @c errc_t to a corresponding object of type
/// @c boost::system::error_code.
/**
* @par Requirements
* @e Header: @c <urdl/http.hpp> @n
* @e Namespace: @c urdl::http
*/
inline boost::system::error_code make_error_code(errc_t e)
{
return boost::system::error_code(
static_cast<int>(e), http::error_category());
}
} // namespace errc
} // namespace http
} // namespace urdl
namespace boost {
namespace system {
template <>
struct is_error_code_enum<urdl::http::errc::errc_t>
{
static const bool value = true;
};
} // namespace system
} // namespace boost
#include "urdl/detail/abi_suffix.hpp"
#if defined(URDL_HEADER_ONLY)
# include "urdl/impl/http.ipp"
#endif
#endif // URDL_HTTP_HPP

View File

@ -1,158 +0,0 @@
//
// http.ipp
// ~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_HTTP_IPP
#define URDL_HTTP_IPP
#include <boost/system/error_code.hpp>
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
namespace http {
namespace detail {
class error_category_impl
: public boost::system::error_category
{
virtual const char* name() const URDL_ERROR_CATEGORY_NOEXCEPT
{
return "HTTP";
}
virtual std::string message(int e) const
{
switch (e)
{
case http::errc::malformed_status_line:
return "Malformed status line";
case http::errc::malformed_response_headers:
return "Malformed response headers";
case http::errc::continue_request:
return "Continue";
case http::errc::switching_protocols:
return "Switching protocols";
case http::errc::ok:
return "OK";
case http::errc::created:
return "Created";
case http::errc::accepted:
return "Accepted";
case http::errc::non_authoritative_information:
return "Non-authoritative information";
case http::errc::no_content:
return "No content";
case http::errc::reset_content:
return "Reset content";
case http::errc::partial_content:
return "Partial content";
case http::errc::multiple_choices:
return "Multiple choices";
case http::errc::moved_permanently:
return "Moved permanently";
case http::errc::found:
return "Found";
case http::errc::see_other:
return "See other";
case http::errc::not_modified:
return "Not modified";
case http::errc::use_proxy:
return "Use proxy";
case http::errc::temporary_redirect:
return "Temporary redirect";
case http::errc::bad_request:
return "Bad request";
case http::errc::unauthorized:
return "Unauthorized";
case http::errc::payment_required:
return "Payment required";
case http::errc::forbidden:
return "Forbidden";
case http::errc::not_found:
return "Not found";
case http::errc::method_not_allowed:
return "Method not allowed";
case http::errc::not_acceptable:
return "Not acceptable";
case http::errc::proxy_authentication_required:
return "Proxy authentication required";
case http::errc::request_timeout:
return "Request time-out";
case http::errc::conflict:
return "Conflict";
case http::errc::gone:
return "Gone";
case http::errc::length_required:
return "Length required";
case http::errc::precondition_failed:
return "Precondition failed";
case http::errc::request_entity_too_large:
return "Request entity too large";
case http::errc::request_uri_too_large:
return "Request URI too large";
case http::errc::unsupported_media_type:
return "Unsupported media type";
case http::errc::requested_range_not_satisfiable:
return "Requested range not satisfiable";
case http::errc::expectation_failed:
return "Expectation failed";
case http::errc::internal_server_error:
return "Internal server error";
case http::errc::not_implemented:
return "Not implemented";
case http::errc::bad_gateway:
return "Bad gateway";
case http::errc::service_unavailable:
return "Service unavailable";
case http::errc::gateway_timeout:
return "Gateway time-out";
case http::errc::version_not_supported:
return "HTTP version not supported";
default:
return "Unknown HTTP error";
}
}
virtual boost::system::error_condition default_error_condition(
int e) const URDL_ERROR_CATEGORY_NOEXCEPT
{
switch (e)
{
case http::errc::unauthorized:
case http::errc::forbidden:
return boost::system::errc::permission_denied;
case http::errc::not_found:
return boost::system::errc::no_such_file_or_directory;
default:
return boost::system::error_condition(e, *this);
}
}
};
} // namespace detail
const boost::system::error_category& error_category()
{
static detail::error_category_impl instance;
return instance;
}
namespace detail {
static const boost::system::error_category& category_instance
= error_category();
} // namespace detail
} // namespace http
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_HTTP_IPP

View File

@ -1,251 +0,0 @@
//
// istreambuf.ipp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_IMPL_ISTREAMBUF_IPP
#define URDL_IMPL_ISTREAMBUF_IPP
#include <boost/array.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include "urdl/read_stream.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
struct istreambuf::body
{
enum { putback_max = 8 };
enum { buffer_size = 512 };
body()
: read_stream_(io_service_),
timer_(io_service_),
open_timeout_(300 * 1000),
read_timeout_(300 * 1000)
{
}
boost::array<char, buffer_size> get_buffer_;
boost::asio::io_service io_service_;
boost::system::error_code error_;
read_stream read_stream_;
boost::asio::deadline_timer timer_;
std::size_t open_timeout_;
std::size_t read_timeout_;
};
namespace detail
{
struct istreambuf_open_handler
{
boost::system::error_code& error_;
boost::asio::deadline_timer& timer_;
void operator()(boost::system::error_code ec)
{
error_ = ec;
timer_.cancel();
}
};
struct istreambuf_read_handler
{
boost::system::error_code& error_;
std::size_t& bytes_transferred_;
boost::asio::deadline_timer& timer_;
void operator()(boost::system::error_code ec, std::size_t bytes_transferred)
{
error_ = ec;
bytes_transferred_ = bytes_transferred;
timer_.cancel();
}
};
struct istreambuf_timeout_handler
{
read_stream& read_stream_;
void operator()(boost::system::error_code ec)
{
if (ec != boost::asio::error::operation_aborted)
read_stream_.close(ec);
}
};
} // namespace detail
istreambuf::istreambuf()
: body_(new body)
{
init_buffers();
}
istreambuf::~istreambuf()
{
try
{
delete body_;
}
catch (std::exception&)
{
// Swallow the exception.
}
}
void istreambuf::set_options(const option_set& options)
{
body_->read_stream_.set_options(options);
}
option_set istreambuf::get_options() const
{
return body_->read_stream_.get_options();
}
istreambuf* istreambuf::open(const url& u)
{
if (is_open())
return 0;
init_buffers();
body_->read_stream_.close(body_->error_);
detail::istreambuf_open_handler oh = { body_->error_, body_->timer_ };
body_->read_stream_.async_open(u, oh);
detail::istreambuf_timeout_handler th = { body_->read_stream_ };
body_->timer_.expires_from_now(
boost::posix_time::milliseconds(body_->open_timeout_));
body_->timer_.async_wait(th);
body_->io_service_.reset();
body_->io_service_.run();
if (!body_->read_stream_.is_open())
body_->error_ = make_error_code(boost::system::errc::timed_out);
return !body_->error_ ? this : 0;
}
bool istreambuf::is_open() const
{
return body_->read_stream_.is_open();
}
istreambuf* istreambuf::close()
{
if (!is_open())
return 0;
body_->read_stream_.close(body_->error_);
if (!body_->error_)
init_buffers();
return !body_->error_ ? this : 0;
}
const boost::system::error_code& istreambuf::puberror() const
{
return error();
}
std::size_t istreambuf::open_timeout() const
{
return body_->open_timeout_;
}
void istreambuf::open_timeout(std::size_t milliseconds)
{
body_->open_timeout_ = milliseconds;
}
std::size_t istreambuf::read_timeout() const
{
return body_->read_timeout_;
}
void istreambuf::read_timeout(std::size_t milliseconds)
{
body_->read_timeout_ = milliseconds;
}
std::string istreambuf::content_type() const
{
return body_->read_stream_.content_type();
}
std::size_t istreambuf::content_length() const
{
return body_->read_stream_.content_length();
}
std::string istreambuf::headers() const
{
return body_->read_stream_.headers();
}
std::streambuf::int_type istreambuf::underflow()
{
if (gptr() == egptr())
{
std::size_t bytes_transferred = 0;
detail::istreambuf_read_handler rh
= { body_->error_, bytes_transferred, body_->timer_ };
body_->read_stream_.async_read_some(boost::asio::buffer(
boost::asio::buffer(body_->get_buffer_) + body::putback_max), rh);
detail::istreambuf_timeout_handler th = { body_->read_stream_ };
body_->timer_.expires_from_now(
boost::posix_time::milliseconds(body_->read_timeout_));
body_->timer_.async_wait(th);
body_->io_service_.reset();
body_->io_service_.run();
if (!body_->read_stream_.is_open())
body_->error_ = make_error_code(boost::system::errc::timed_out);
if (body_->error_)
{
if (body_->error_ == boost::asio::error::eof)
{
body_->error_ = boost::system::error_code();
return traits_type::eof();
}
boost::throw_exception(boost::system::system_error(body_->error_));
}
setg(body_->get_buffer_.begin(),
body_->get_buffer_.begin() + body::putback_max,
body_->get_buffer_.begin() + body::putback_max + bytes_transferred);
return traits_type::to_int_type(*gptr());
}
else
{
return traits_type::eof();
}
}
const boost::system::error_code& istreambuf::error() const
{
return body_->error_;
}
void istreambuf::init_buffers()
{
setg(body_->get_buffer_.begin(),
body_->get_buffer_.begin() + body::putback_max,
body_->get_buffer_.begin() + body::putback_max);
}
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_IMPL_ISTREAMBUF_IPP

View File

@ -1,106 +0,0 @@
//
// option_set.ipp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_OPTION_SET_IPP
#define URDL_OPTION_SET_IPP
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
option_set::option_set()
{
}
option_set::option_set(const option_set& other)
{
detail::scoped_ptr<option_wrapper_base>* prev_link = &head_;
option_wrapper_base* node = other.head_.get();
while (node)
{
prev_link->reset(node->clone());
prev_link = &prev_link->get()->next;
node = node->next.get();
}
}
option_set::~option_set()
{
}
option_set& option_set::operator=(const option_set& other)
{
option_set tmp(other);
head_.reset(tmp.head_.release());
return *this;
}
void option_set::set_options(const option_set& other)
{
option_wrapper_base* node = other.head_.get();
while (node)
{
set_option_wrapper_base(node->clone());
node = node->next.get();
}
}
void option_set::set_option_wrapper_base(option_set::option_wrapper_base* o)
{
detail::scoped_ptr<option_wrapper_base>* prev_link = &head_;
option_wrapper_base* node = head_.get();
while (node)
{
if (o->type_info() == node->type_info())
{
o->next.reset(node->next.release());
prev_link->reset(o);
return;
}
prev_link = &node->next;
node = node->next.get();
}
prev_link->reset(o);
}
option_set::option_wrapper_base* option_set::get_option_wrapper_base(
const std::type_info& ti) const
{
option_wrapper_base* node = head_.get();
while (node)
{
if (ti == node->type_info())
return node;
node = node->next.get();
}
return 0;
}
void option_set::clear_option_wrapper_base(const std::type_info& ti)
{
detail::scoped_ptr<option_wrapper_base>* prev_link = &head_;
option_wrapper_base* node = head_.get();
while (node)
{
if (ti == node->type_info())
{
prev_link->reset(node->next.release());
return;
}
prev_link = &node->next;
node = node->next.get();
}
}
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_OPTION_SET_IPP

View File

@ -1,348 +0,0 @@
//
// url.ipp
// ~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_URL_IPP
#define URDL_URL_IPP
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
unsigned short url::port() const
{
if (!port_.empty())
return std::atoi(port_.c_str());
if (protocol_ == "http")
return 80;
if (protocol_ == "https")
return 443;
if (protocol_ == "ftp")
return 21;
return 0;
}
std::string url::path() const
{
std::string tmp_path;
unescape_path(path_, tmp_path);
return tmp_path;
}
std::string url::to_string(int components) const
{
std::string s;
if ((components & protocol_component) != 0 && !protocol_.empty())
{
s = protocol_;
s += "://";
}
if ((components & user_info_component) != 0 && !user_info_.empty())
{
s += user_info_;
s += "@";
}
if ((components & host_component) != 0)
{
if (ipv6_host_)
s += "[";
s += host_;
if (ipv6_host_)
s += "]";
}
if ((components & port_component) != 0 && !port_.empty())
{
s += ":";
s += port_;
}
if ((components & path_component) != 0 && !path_.empty())
{
s += path_;
}
if ((components & query_component) != 0 && !query_.empty())
{
s += "?";
s += query_;
}
if ((components & fragment_component) != 0 && !fragment_.empty())
{
s += "#";
s += fragment_;
}
return s;
}
url url::from_string(const char* s, boost::system::error_code& ec)
{
url new_url;
// Protocol.
std::size_t length = std::strcspn(s, ":");
new_url.protocol_.assign(s, s + length);
for (std::size_t i = 0; i < new_url.protocol_.length(); ++i)
new_url.protocol_[i] = std::tolower(new_url.protocol_[i]);
s += length;
// "://".
if (*s++ != ':')
{
ec = make_error_code(boost::system::errc::invalid_argument);
return url();
}
if (*s++ != '/')
{
ec = make_error_code(boost::system::errc::invalid_argument);
return url();
}
if (*s++ != '/')
{
ec = make_error_code(boost::system::errc::invalid_argument);
return url();
}
// UserInfo.
length = std::strcspn(s, "@:[/?#");
if (s[length] == '@')
{
new_url.user_info_.assign(s, s + length);
s += length + 1;
}
else if (s[length] == ':')
{
std::size_t length2 = std::strcspn(s + length, "@/?#");
if (s[length + length2] == '@')
{
new_url.user_info_.assign(s, s + length + length2);
s += length + length2 + 1;
}
}
// Host.
if (*s == '[')
{
length = std::strcspn(++s, "]");
if (s[length] != ']')
{
ec = make_error_code(boost::system::errc::invalid_argument);
return url();
}
new_url.host_.assign(s, s + length);
new_url.ipv6_host_ = true;
s += length + 1;
if (std::strcspn(s, ":/?#") != 0)
{
ec = make_error_code(boost::system::errc::invalid_argument);
return url();
}
}
else
{
length = std::strcspn(s, ":/?#");
new_url.host_.assign(s, s + length);
s += length;
}
// Port.
if (*s == ':')
{
length = std::strcspn(++s, "/?#");
if (length == 0)
{
ec = make_error_code(boost::system::errc::invalid_argument);
return url();
}
new_url.port_.assign(s, s + length);
for (std::size_t i = 0; i < new_url.port_.length(); ++i)
{
if (!std::isdigit(new_url.port_[i]))
{
ec = make_error_code(boost::system::errc::invalid_argument);
return url();
}
}
s += length;
}
// Path.
if (*s == '/')
{
length = std::strcspn(s, "?#");
new_url.path_.assign(s, s + length);
std::string tmp_path;
if (!unescape_path(new_url.path_, tmp_path))
{
ec = make_error_code(boost::system::errc::invalid_argument);
return url();
}
s += length;
}
else
new_url.path_ = "/";
// Query.
if (*s == '?')
{
length = std::strcspn(++s, "#");
new_url.query_.assign(s, s + length);
s += length;
}
// Fragment.
if (*s == '#')
new_url.fragment_.assign(++s);
ec = boost::system::error_code();
return new_url;
}
url url::from_string(const char* s)
{
boost::system::error_code ec;
url new_url(from_string(s, ec));
if (ec)
{
boost::system::system_error ex(ec);
boost::throw_exception(ex);
}
return new_url;
}
url url::from_string(const std::string& s, boost::system::error_code& ec)
{
return from_string(s.c_str(), ec);
}
url url::from_string(const std::string& s)
{
return from_string(s.c_str());
}
bool url::unescape_path(const std::string& in, std::string& out)
{
out.clear();
out.reserve(in.size());
for (std::size_t i = 0; i < in.size(); ++i)
{
switch (in[i])
{
case '%':
if (i + 3 <= in.size())
{
unsigned int value = 0;
for (std::size_t j = i + 1; j < i + 3; ++j)
{
switch (in[j])
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
value += in[j] - '0';
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
value += in[j] - 'a' + 10;
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
value += in[j] - 'A' + 10;
break;
default:
return false;
}
if (j == i + 1)
value <<= 4;
}
out += static_cast<char>(value);
i += 2;
}
else
return false;
break;
case '-': case '_': case '.': case '!': case '~': case '*':
case '\'': case '(': case ')': case ':': case '@': case '&':
case '=': case '+': case '$': case ',': case '/': case ';':
out += in[i];
break;
default:
if (!std::isalnum(in[i]))
return false;
out += in[i];
break;
}
}
return true;
}
bool operator==(const url& a, const url& b)
{
return a.protocol_ == b.protocol_
&& a.user_info_ == b.user_info_
&& a.host_ == b.host_
&& a.port_ == b.port_
&& a.path_ == b.path_
&& a.query_ == b.query_
&& a.fragment_ == b.fragment_;
}
bool operator!=(const url& a, const url& b)
{
return !(a == b);
}
bool operator<(const url& a, const url& b)
{
if (a.protocol_ < b.protocol_)
return true;
if (b.protocol_ < a.protocol_)
return false;
if (a.user_info_ < b.user_info_)
return true;
if (b.user_info_ < a.user_info_)
return false;
if (a.host_ < b.host_)
return true;
if (b.host_ < a.host_)
return false;
if (a.port_ < b.port_)
return true;
if (b.port_ < a.port_)
return false;
if (a.path_ < b.path_)
return true;
if (b.path_ < a.path_)
return false;
if (a.query_ < b.query_)
return true;
if (b.query_ < a.query_)
return false;
return a.fragment_ < b.fragment_;
}
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_URL_IPP

View File

@ -1,379 +0,0 @@
//
// istream.hpp
// ~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_ISTREAM_HPP
#define URDL_ISTREAM_HPP
#include <istream>
#include <boost/utility/base_from_member.hpp>
#include <boost/system/error_code.hpp>
#include "urdl/istreambuf.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
/// The class @c istream supports reading content from a specified URL.
/**
* @par Remarks
* The class stores an object of class @c istreambuf.
*
* Currently supported URL protocols are @c http, @c https and @c file.
*
* @par Example
* To read the entire content of a resource located by a URL into a string:
* @code
* urdl::istream is("http://www.boost.org/LICENSE_1_0.txt");
* if (is)
* {
* std::string content;
* if (std::getline(is, content, std::char_traits<char>::eof()))
* {
* ...
* }
* }
* @endcode
*
* @par Requirements
* @e Header: @c <urdl/istream.hpp> @n
* @e Namespace: @c urdl
*/
class istream
: private boost::base_from_member<istreambuf>,
public std::basic_istream<char>
{
public:
/// Constructs an object of class @c istream.
/**
* @par Remarks
* Initializes the base class with @c std::basic_istream<char>(sb),
* where sb is an object of class @c istreambuf stored within the class.
*/
istream()
: std::basic_istream<char>(
&this->boost::base_from_member<istreambuf>::member)
{
}
/// Constructs an object of class @c istream.
/**
* @param u The URL to open.
*
* @par Remarks
* Initializes the base class with @c std::basic_istream<char>(sb),
* where @c sb is an object of class @c istreambuf stored within the class. It
* also opens @c sb by performing @c sb.open(u) and, if that fails (returns a
* null pointer), calls @c setstate(failbit).
*/
explicit istream(const url& u)
: std::basic_istream<char>(
&this->boost::base_from_member<istreambuf>::member)
{
if (rdbuf()->open(u) == 0)
setstate(std::ios_base::failbit);
}
/// Constructs an object of class @c istream.
/**
* @param u The URL to open.
*
* @param options The options to be set on the stream.
*
* @par Remarks
* Initializes the base class with @c std::basic_istream<char>(sb), where
* @c sb is an object of class @c istreambuf stored within the class. It also
* performs @c rdbuf()->set_options(options), then opens @c sb by performing
* @c sb.open(u) and, if that fails (returns a null pointer), calls
* @c setstate(failbit).
*
* @par Example
* @code
* urdl::option_set options;
* options.set_option(urdl::http::max_redirects(1));
* urdl::istream is("http://www.boost.org", options);
* @endcode
*/
explicit istream(const url& u, const option_set& options)
: std::basic_istream<char>(
&this->boost::base_from_member<istreambuf>::member)
{
rdbuf()->set_options(options);
if (rdbuf()->open(u) == 0)
setstate(std::ios_base::failbit);
}
/// Sets an option to control the behaviour of the stream.
/**
* @param option The option to be set on the stream.
*
* @par Remarks
* Performs @c rdbuf()->set_option(option). Options are uniquely identified by
* type.
*
* @par Example
* @code
* urdl::istream is;
* is.set_option(urdl::http::max_redirects(1));
* @endcode
*/
template <typename Option>
void set_option(const Option& option)
{
rdbuf()->set_option(option);
}
/// Sets options to control the behaviour of the stream.
/**
* @param options The options to be set on the stream. The options in the set
* are added on top of any options already set on the stream.
*
* @par Remarks
* Performs @c rdbuf()->set_options(options).
*
* @par Example
* @code
* urdl::istream is;
* urdl::option_set options;
* options.set_option(urdl::http::max_redirects(1));
* options.set_option(urdl::ssl::verify_peer(false));
* stream.set_options(options);
* @endcode
*/
void set_options(const option_set& options)
{
rdbuf()->set_options(options);
}
/// Gets the current value of an option that controls the behaviour of the
/// stream.
/**
* @returns The current value of the option.
*
* @par Remarks
* Returns @c rdbuf()->get_option<Option>(). Options are uniquely identified
* by type.
*
* @par Example
* @code
* urdl::istream is;
* urdl::http::max_redirects option
* = is.get_option<urdl::http::max_redirects>();
* std::size_t value = option.value();
* @endcode
*/
template <typename Option>
Option get_option() const
{
return rdbuf()->get_option<Option>();
}
/// Gets the values of all options set on the stream.
/**
* @returns An option set containing all options from the stream.
*
* @par Remarks
* Returns @c rdbuf()->get_options().
*
* @par Example
* To get the options that have been set on the stream:
* @code
* urdl::istream is;
* ...
* urdl::option_set options(is.get_options());
* urdl::http::max_redirects option
* = options.get_option<urdl::http::max_redirects>();
* std::size_t value = option.value();
* @endcode
*/
option_set get_options() const
{
return rdbuf()->get_options();
}
/// Determines whether the stream is open.
/**
* @returns @c true if the stream is open, @c false otherwise.
*
* @par Remarks
* Returns @c rdbuf()->is_open().
*/
bool is_open() const
{
return rdbuf()->is_open();
}
/// Opens the specified URL.
/**
* @param u The URL to open.
*
* @par Remarks
* Calls @c rdbuf()->open(u). If that function does not return a null
* pointer, calls @c clear(). Otherwise calls @c setstate(failbit) (which may
* throw @c ios_base::failure).
*/
void open(const url& u)
{
if (rdbuf()->open(u) == 0)
setstate(std::ios_base::failbit);
else
clear();
}
/// Closes the stream.
/**
* @par Remarks
* Calls @c rdbuf()->close() and, if that function returns a null
* pointer, calls @c setstate(failbit) (which may throw @c ios_base::failure).
*/
void close()
{
if (rdbuf()->close() == 0)
setstate(std::ios_base::failbit);
}
/// Gets the underlying stream buffer.
/**
* @returns A pointer to the stream buffer contained within the class.
*/
istreambuf* rdbuf() const
{
return const_cast<istreambuf*>(
&this->boost::base_from_member<istreambuf>::member);
}
/// Gets the last error associated with the stream.
/**
* @returns An @c error_code corresponding to the last error from the stream.
*
* @par Remarks
* Returns a reference to an @c error_code object representing the last
* failure reported by an @c istreambuf function. The set of possible
* @c error_code values and categories depends on the protocol of the URL
* used to open the stream.
*
* @par Example
* To take action given a specific error:
* @code
* urdl::istream is("http://somesite/page");
* if (!is)
* {
* if (is.error() == urdl::http::errc::forbidden)
* {
* std::cout << "Computer says no" << std::endl;
* }
* }
* @endcode
*/
const boost::system::error_code& error() const
{
return rdbuf()->puberror();
}
/// Gets the open timeout of the stream.
/**
* @returns The timeout, in milliseconds, used when opening a URL.
*
* @par Remarks
* Returns @c rdbuf()->open_timeout().
*/
std::size_t open_timeout() const
{
return rdbuf()->open_timeout();
}
/// Sets the open timeout of the stream.
/**
* @param milliseconds The timeout, in milliseconds, to be used when opening
* a URL.
*
* @par Remarks
* Performs @c rdbuf()->open_timeout(milliseconds).
*/
void open_timeout(std::size_t milliseconds)
{
rdbuf()->open_timeout(milliseconds);
}
/// Gets the read timeout of the stream.
/**
* @returns The timeout, in milliseconds, used for individual read operations
* on the underlying transport.
*
* @par Remarks
* Returns @c rdbuf()->read_timeout().
*/
std::size_t read_timeout() const
{
return rdbuf()->read_timeout();
}
/// Sets the read timeout of the stream.
/**
* @param milliseconds The timeout, in milliseconds, to be used for individual
* read operations on the underlying transport.
*
* @par Remarks
* Performs @c rdbuf()->read_timeout(milliseconds).
*/
void read_timeout(std::size_t milliseconds)
{
rdbuf()->read_timeout(milliseconds);
}
/// Gets the MIME type of the content obtained from the URL.
/**
* @returns A string specifying the MIME type. Examples of possible return
* values include @c text/plain, @c text/html and @c image/png.
*
* @par Remarks
* Returns @c rdbuf()->content_type().
*
* Not all URL protocols support a content type. For these protocols, this
* function returns an empty string.
*/
std::string content_type() const
{
return rdbuf()->content_type();
}
/// Gets the length of the content obtained from the URL.
/**
* @returns The length, in bytes, of the content. If the content associated
* with the URL does not specify a length,
* @c std::numeric_limits<std::size_t>::max().
*
* @par Remarks
* Returns @c rdbuf()->content_length().
*/
std::size_t content_length() const
{
return rdbuf()->content_length();
}
/// Gets the protocol-specific headers obtained from the URL.
/**
* @returns A string containing the headers returned with the content from the
* URL. The format and interpretation of these headers is specific to the
* protocol associated with the URL.
*
* @par Remarks
* Returns @c rdbuf()->headers().
*/
std::string headers() const
{
return rdbuf()->headers();
}
};
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_ISTREAM_HPP

View File

@ -1,216 +0,0 @@
//
// istreambuf.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_ISTREAMBUF_HPP
#define URDL_ISTREAMBUF_HPP
#include <streambuf>
#include <boost/system/error_code.hpp>
#include "urdl/detail/config.hpp"
#include "urdl/option_set.hpp"
#include "urdl/url.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
/// The class @c istreambuf associates the input sequence with the content from
/// a specified URL.
/**
* @par Requirements
* @e Header: @c <urdl/istreambuf.hpp> @n
* @e Namespace: @c urdl
*/
class istreambuf
: public std::streambuf
{
public:
/// Constructs an object of class @c istreambuf.
URDL_DECL istreambuf();
/// Destroys an object of class @c istreambuf.
URDL_DECL ~istreambuf();
/// Sets an option to control the behaviour of the stream buffer.
/**
* @param option The option to be set on the stream buffer.
*
* @par Remarks
* Options are uniquely identified by type.
*/
template <typename Option>
void set_option(const Option& option)
{
option_set options;
options.set_option(option);
set_options(options);
}
/// Sets options to control the behaviour of the stream buffer.
/**
* @param options The options to be set on the stream buffer.
*/
URDL_DECL void set_options(const option_set& options);
/// Gets the current value of an option that controls the behaviour of the
/// stream buffer.
/**
* @returns The current value of the option.
*
* @par Remarks
* Options are uniquely identified by type.
*/
template <typename Option>
Option get_option() const
{
option_set options(get_options());
return options.get_option<Option>();
}
/// Gets the values of all options set on the stream.
/**
* @returns An option set containing all options from the stream buffer.
*/
URDL_DECL option_set get_options() const;
/// Determines whether the stream buffer is open.
/**
* @returns @c true if the stream buffer is open, @c false otherwise.
*
* @par Remarks
* Returns @c true if a previous call to @c open succeeded (returned a
* non-null value) and there has been no intervening call to @c close.
*/
URDL_DECL bool is_open() const;
/// Opens the specified URL.
/**
* @param u The URL to open.
*
* @returns @c this on success, a null pointer otherwise.
*
* @par Remarks
* If <tt>is_open() != false</tt>, returns a null pointer. Otherwise,
* initializes the @c istreambuf as required.
*/
URDL_DECL istreambuf* open(const url& u);
/// Closes the stream buffer.
/**
* @returns @c this on success, a null pointer otherwise.
*
* @par Remarks
* If <tt>is_open() == false</tt>, returns a null pointer. Otherwise, closes
* the underlying transport's resources as required. If any of those
* operations fail, @c close fails by returning a null pointer.
*/
URDL_DECL istreambuf* close();
/// Gets the last error associated with the stream buffer.
/**
* @returns An @c error_code corresponding to the last error from the stream
* buffer.
*
* @par Remarks
* Returns @c error().
*/
URDL_DECL const boost::system::error_code& puberror() const;
/// Gets the open timeout of the stream buffer.
/**
* @returns The timeout, in milliseconds, used when opening a URL.
*/
URDL_DECL std::size_t open_timeout() const;
/// Sets the open timeout of the stream buffer.
/**
* @param milliseconds The timeout, in milliseconds, to be used when opening
* a URL.
*/
URDL_DECL void open_timeout(std::size_t milliseconds);
/// Gets the read timeout of the stream buffer.
/**
* @returns The timeout, in milliseconds, used for individual read operations
* on the underlying transport, when downloading the URL's content.
*/
URDL_DECL std::size_t read_timeout() const;
/// Sets the read timeout of the stream buffer.
/**
* @param milliseconds The timeout, in milliseconds, to be used for individual
* read operations on the underlying transport, when downloading the URL's
* content.
*/
URDL_DECL void read_timeout(std::size_t milliseconds);
/// Gets the MIME type of the content obtained from the URL.
/**
* @returns A string specifying the MIME type. Examples of possible return
* values include @c text/plain, @c text/html and @c image/png.
*
* @par Remarks
* Not all URL protocols support a content type. For these protocols, this
* function returns an empty string.
*/
URDL_DECL std::string content_type() const;
/// Gets the length of the content obtained from the URL.
/**
* @returns The length, in bytes, of the content. If the content associated
* with the URL does not specify a length,
* @c std::numeric_limits<std::size_t>::max().
*/
URDL_DECL std::size_t content_length() const;
/// Gets the protocol-specific headers obtained from the URL.
/**
* @returns A string containing the headers returned with the content from the
* URL. The format and interpretation of these headers is specific to the
* protocol associated with the URL.
*/
URDL_DECL std::string headers() const;
protected:
/// Overrides @c std::streambuf behaviour.
/**
* par Remarks
* Behaves according to the specification of @c std::streambuf::underflow().
*/
URDL_DECL int_type underflow();
/// Gets the last error associated with the stream.
/**
* @returns An @c error_code corresponding to the last error from the stream.
*
* @par Remarks
* Returns a reference to an @c error_code object representing the last
* failure reported by an @c istreambuf function. The set of possible
* @c error_code values and categories depends on the protocol of the URL
* used to open the stream buffer.
*/
URDL_DECL virtual const boost::system::error_code& error() const;
private:
URDL_DECL void init_buffers();
struct body;
body* body_;
};
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#if defined(URDL_HEADER_ONLY)
# include "urdl/impl/istreambuf.ipp"
#endif
#endif // URDL_ISTREAMBUF_HPP

View File

@ -1,151 +0,0 @@
//
// option_set.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// path LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_OPTION_SET_HPP
#define URDL_OPTION_SET_HPP
#include <typeinfo>
#include "urdl/detail/config.hpp"
#include "urdl/detail/scoped_ptr.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
/// The class @c option_set maintains a collection of options.
/**
* @par Remarks
* Options are uniquely identified by type, so the @c option_set class is a
* collection of objects of differing types, indexed by type.
*
* The option types stored in the set must meet the type requirements for
* CopyConstructible.
*
* @par Requirements
* @e Header: @c <urdl/option_set.hpp> @n
* @e Namespace: @c urdl
*/
class option_set
{
public:
/// Constructs an object of class @c option_set.
/**
* @par Remarks
* Creates an empty set. Any option queried using the @c get_option member
* function will return the default value of the option.
*/
URDL_DECL option_set();
/// Constructs an object of class @c option_set.
/**
* @par Remarks
* Creates an identical copy of another set. Any option queried using the
* @c get_option member function will return the same value for both sets.
*/
URDL_DECL option_set(const option_set& other);
/// Destroys an object of class @c option_set.
URDL_DECL ~option_set();
/// Assignment operator.
/**
* @par Remarks
* Creates an identical copy of another set. Any option queried using the
* @c get_option member function will return the same value for both sets.
*/
URDL_DECL option_set& operator=(const option_set& other);
/// Sets the value of an option in the set.
/**
* @param o The option to be set.
*
* @par Remarks
* If the type @c Option is already present in the set, first removes that
* element. Adds the option to the set.
*/
template <typename Option>
void set_option(const Option& o)
{
set_option_wrapper_base(new option_wrapper<Option>(o));
}
/// Sets multiple options in a set from another set.
/**
* @param other An option set containing all options to be set in the target.
*
* @par Remarks
* Performs a deep copy of all option values from the object @c other into
* the target set.
*/
URDL_DECL void set_options(const option_set& other);
/// Gets an option from the set.
/**
* @returns If the option is present in the set, an object containing the
* value of the option. Otherwise, returns a default-constructed option.
*/
template <typename Option>
Option get_option() const
{
if (option_wrapper_base* o
= get_option_wrapper_base(typeid(option_wrapper<Option>)))
return static_cast<option_wrapper<Option>*>(o)->value;
return Option();
}
/// Removes an option from the set.
/**
* @par Remarks
* If the option is queried using the @c get_option member function, it will
* return the default value of the option.
*/
template <typename Option>
void clear_option()
{
clear_option_wrapper_base(typeid(option_wrapper<Option>));
}
private:
struct option_wrapper_base
{
virtual ~option_wrapper_base() {}
virtual const std::type_info& type_info() const = 0;
virtual option_wrapper_base* clone() const = 0;
detail::scoped_ptr<option_wrapper_base> next;
};
template <typename Option>
struct option_wrapper : option_wrapper_base
{
option_wrapper(const Option& o) : value(o) {}
const std::type_info& type_info() const
{ return typeid(option_wrapper<Option>); }
option_wrapper_base* clone() const
{ return new option_wrapper<Option>(value); }
Option value;
};
URDL_DECL void set_option_wrapper_base(option_wrapper_base* o);
URDL_DECL option_wrapper_base* get_option_wrapper_base(
const std::type_info& ti) const;
URDL_DECL void clear_option_wrapper_base(const std::type_info& ti);
detail::scoped_ptr<option_wrapper_base> head_;
};
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#if defined(URDL_HEADER_ONLY)
# include "urdl/impl/option_set.ipp"
#endif
#endif // URDL_OPTION_SET_HPP

View File

@ -1,807 +0,0 @@
//
// read_stream.hpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_READ_STREAM_HPP
#define URDL_READ_STREAM_HPP
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/detail/bind_handler.hpp>
#include <boost/throw_exception.hpp>
#include "urdl/http.hpp"
#include "urdl/option_set.hpp"
#include "urdl/url.hpp"
#include "urdl/detail/coroutine.hpp"
#include "urdl/detail/file_read_stream.hpp"
#include "urdl/detail/http_read_stream.hpp"
#if !defined(URDL_DISABLE_SSL)
# include <boost/asio/ssl.hpp>
#endif // !defined(URDL_DISABLE_SSL)
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
/// The class @c read_stream supports reading content from a specified URL
/// using synchronous or asynchronous operations.
/**
* @par Remarks
* Currently supported URL protocols are @c http, @c https and @c file.
*
* The class @c read_stream meets the type requirements for @c SyncReadStream
* and @c AsyncReadStream, as defined in the Boost.Asio documentation. This
* allows objects of class @c read_stream to be used with the functions
* @c boost::asio::read, @c boost::asio::async_read, @c boost::asio::read_until
* and @c boost::asio::async_read_until.
*
* @par Example
* To synchronously open the URL, read the content and write it to standard
* output:
* @code
* try
* {
* boost::asio::io_service io_service;
* urdl::read_stream read_stream(io_service);
* read_stream.open("http://www.boost.org/LICENSE_1_0.txt");
* for (;;)
* {
* char data[1024];
* boost::system::error_code ec;
* std::size_t length = stream.read_some(boost::asio::buffer(data), ec);
* if (ec == boost::asio::error::eof)
* break;
* if (ec)
* throw boost::system::system_error(ec);
* os.write(data, length);
* }
* }
* catch (std::exception& e)
* {
* std::cerr << "Exception: " << e.what() << std::endl;
* }
* @endcode
*
* To asynchronously open the URL, read the content and write it to standard
* output:
* @code
* boost::asio::io_service io_service;
* urdl::read_stream read_stream(io_service)
* char data[1024];
* ...
* read_stream.async_open("http://www.boost.org/LICENSE_1_0.txt", open_handler);
* ...
* void open_handler(const boost::system::error_code& ec)
* {
* if (!ec)
* {
* read_stream.async_read_some(boost::asio::buffer(data), read_handler);
* }
* }
* ...
* void read_handler(const boost::system::error_code& ec, std::size_t length)
* {
* if (!ec)
* {
* std::cout.write(data, length);
* read_stream.async_read_some(boost::asio::buffer(data), read_handler);
* }
* }
* @endcode
*
* @par Requirements
* @e Header: @c <urdl/read_stream.hpp> @n
* @e Namespace: @c urdl
*/
class read_stream
{
public:
/// Constructs an object of class @c read_stream.
/**
* @param io_service The @c io_service object that the stream will use to
* dispatch handlers for any asynchronous operations performed on the stream.
*/
explicit read_stream(boost::asio::io_service& io_service)
: io_service_(io_service),
file_(io_service, options_),
http_(io_service, options_),
#if !defined(URDL_DISABLE_SSL)
ssl_context_(io_service, boost::asio::ssl::context::sslv23),
https_(io_service, options_, ssl_context_),
#endif // !defined(URDL_DISABLE_SSL)
protocol_(unknown)
{
#if !defined(URDL_DISABLE_SSL)
ssl_context_.set_verify_mode(boost::asio::ssl::context::verify_peer);
SSL_CTX_set_default_verify_paths(ssl_context_.impl());
#endif // !defined(URDL_DISABLE_SSL)
}
/// Gets the @c io_service associated with the stream.
/**
* @returns A reference to the @c io_service object that the stream will use
* to dispatch handlers. Ownership is not transferred to the caller.
*/
boost::asio::io_service& get_io_service()
{
return io_service_;
}
/// Sets an option to control the behaviour of the stream.
/**
* @param option The option to be set on the stream.
*
* @par Remarks
* Options are uniquely identified by type.
*
* @par Example
* @code
* urdl::read_stream stream(io_service);
* stream.set_option(urdl::http::max_redirects(1));
* @endcode
*/
template <typename Option>
void set_option(const Option& option)
{
options_.set_option(option);
}
/// Sets options to control the behaviour of the stream.
/**
* @param options The options to be set on the stream. The options in the set
* are added on top of any options already set on the stream.
*
* @par Example
* @code
* urdl::read_stream stream(io_service);
* urdl::option_set options;
* options.set_option(urdl::http::max_redirects(1));
* options.set_option(urdl::ssl::verify_peer(false));
* stream.set_options(options);
* @endcode
*/
void set_options(const option_set& options)
{
options_.set_options(options);
}
/// Gets the current value of an option that controls the behaviour of the
/// stream.
/**
* @returns The current value of the option.
*
* @par Remarks
* Options are uniquely identified by type.
*
* @par Example
* @code
* urdl::read_stream stream(io_service);
* urdl::http::max_redirects option
* = stream.get_option<urdl::http::max_redirects>();
* std::size_t value = option.value();
* @endcode
*/
template <typename Option>
Option get_option() const
{
return options_.get_option<Option>();
}
/// Gets the values of all options set on the stream.
/**
* @returns An option set containing all options from the stream.
*
* @par Example
* @code
* urdl::read_stream stream(io_service);
* ...
* urdl::option_set options(stream.get_options());
* urdl::http::max_redirects option
* = options.get_option<urdl::http::max_redirects>();
* std::size_t value = option.value();
* @endcode
*/
option_set get_options() const
{
return options_;
}
/// Determines whether the stream is open.
/**
* @returns @c true if the stream is open, @c false otherwise.
*/
bool is_open() const
{
switch (protocol_)
{
case file:
return file_.is_open();
case http:
return http_.is_open();
#if !defined(URDL_DISABLE_SSL)
case https:
return https_.is_open();
#endif // !defined(URDL_DISABLE_SSL)
default:
return false;
}
}
/// Opens the specified URL.
/**
* @param u The URL to open.
*
* @throws boost::system::system_error Thrown on failure.
*
* @par Example
* @code
* urdl::read_stream read_stream(io_service);
*
* try
* {
* read_stream.open("http://www.boost.org");
* }
* catch (boost::system::error_code& e)
* {
* std::cerr << e.what() << std::endl;
* }
* @endcode
*/
void open(const url& u)
{
boost::system::error_code ec;
if (open(u, ec))
{
boost::system::system_error ex(ec);
boost::throw_exception(ex);
}
}
/// Opens the specified URL.
/**
* @param u The URL to open.
*
* @param ec Set to indicate what error occurred, if any.
*
* @returns @c ec.
*
* @par Example
* @code
* urdl::read_stream read_stream(io_service);
*
* boost::system::error_code ec;
* read_stream.open("http://www.boost.org", ec);
* if (ec)
* {
* std::cerr << ec.message() << std::endl;
* }
* @endcode
*/
boost::system::error_code open(const url& u, boost::system::error_code& ec)
{
url tmp_url = u;
std::size_t redirects = 0;
for (;;)
{
if (tmp_url.protocol() == "file")
{
protocol_ = file;
return file_.open(tmp_url, ec);
}
else if (tmp_url.protocol() == "http")
{
protocol_ = http;
http_.open(tmp_url, ec);
if (ec == http::errc::moved_permanently || ec == http::errc::found)
{
std::size_t max_redirects = options_.get_option<
urdl::http::max_redirects>().value();
if (redirects < max_redirects)
{
++redirects;
tmp_url = http_.location();
http_.close(ec);
continue;
}
}
return ec;
}
#if !defined(URDL_DISABLE_SSL)
else if (tmp_url.protocol() == "https")
{
protocol_ = https;
https_.open(tmp_url, ec);
if (ec == http::errc::moved_permanently || ec == http::errc::found)
{
std::size_t max_redirects = options_.get_option<
urdl::http::max_redirects>().value();
if (redirects < max_redirects)
{
++redirects;
tmp_url = https_.location();
https_.close(ec);
continue;
}
}
return ec;
}
#endif // !defined(URDL_DISABLE_SSL)
else
{
ec = boost::asio::error::operation_not_supported;
return ec;
}
}
}
/// Asynchronously opens the specified URL.
/**
* @param u The URL to open.
*
* @param handler The handler to be called when the open operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code
* void handler(
* const boost::system::error_code& ec // Result of operation.
* );
* @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* @c boost::asio::io_service::post().
*
* @par Example
* @code
* void open_handler(const boost::system::error_code& ec)
* {
* if (!ec)
* {
* // Open succeeded.
* }
* }
* ...
* urdl::read_stream read_stream(io_service);
* read_stream.async_open("http://www.boost.org/", open_handler);
* @endcode
*/
template <typename Handler>
URDL_INITFN_RESULT_TYPE(Handler,
void (boost::system::error_code))
async_open(const url& u, Handler handler)
{
#if (BOOST_VERSION >= 105400)
typedef typename boost::asio::handler_type<Handler,
void (boost::system::error_code)>::type real_handler_type;
real_handler_type real_handler(handler);
boost::asio::async_result<real_handler_type> result(real_handler);
#else // (BOOST_VERSION >= 105400)
typedef Handler real_handler_type;
Handler real_handler(handler);
#endif // (BOOST_VERSION >= 105400)
open_coro<real_handler_type>(this, u, real_handler)(
boost::system::error_code());
#if (BOOST_VERSION >= 105400)
return result.get();
#endif // (BOOST_VERSION >= 105400)
}
/// Closes the stream.
/**
* @throws asio::system_error Thrown on failure.
*
* @par Remarks
* Any asynchronous open or read operations will be cancelled, and will
* complete with the @c boost::asio::error::operation_aborted error.
*/
void close()
{
boost::system::error_code ec;
if (close(ec))
{
boost::system::system_error ex(ec);
boost::throw_exception(ex);
}
}
/// Closes the stream.
/**
* @param ec Set to indicate what error occurred, if any.
*
* @returns @c ec.
*
* @par Remarks
* Any asynchronous open or read operations will be cancelled, and will
* complete with the @c boost::asio::error::operation_aborted error.
*/
boost::system::error_code close(boost::system::error_code& ec)
{
switch (protocol_)
{
case file:
return file_.close(ec);
case http:
return http_.close(ec);
#if !defined(URDL_DISABLE_SSL)
case https:
return https_.close(ec);
#endif // !defined(URDL_DISABLE_SSL)
default:
ec = boost::system::error_code();
break;
}
return ec;
}
/// Gets the MIME type of the content obtained from the URL.
/**
* @returns A string specifying the MIME type. Examples of possible return
* values include @c text/plain, @c text/html and @c image/png.
*
* @par Remarks
* Not all URL protocols support a content type. For these protocols, this
* function returns an empty string.
*/
std::string content_type() const
{
switch (protocol_)
{
case file:
return std::string();
case http:
return http_.content_type();
#if !defined(URDL_DISABLE_SSL)
case https:
return https_.content_type();
#endif // !defined(URDL_DISABLE_SSL)
default:
return std::string();
}
}
/// Gets the length of the content obtained from the URL.
/**
* @returns The length, in bytes, of the content. If the content associated
* with the URL does not specify a length,
* @c std::numeric_limits<std::size_t>::max().
*/
std::size_t content_length() const
{
switch (protocol_)
{
case file:
return ~std::size_t(0);
case http:
return http_.content_length();
#if !defined(URDL_DISABLE_SSL)
case https:
return https_.content_length();
#endif // !defined(URDL_DISABLE_SSL)
default:
return ~std::size_t(0);
}
}
/// Gets the protocol-specific headers obtained from the URL.
/**
* @returns A string containing the headers returned with the content from the
* URL. The format and interpretation of these headers is specific to the
* protocol associated with the URL.
*/
std::string headers() const
{
switch (protocol_)
{
case file:
return std::string();
case http:
return http_.headers();
#if !defined(URDL_DISABLE_SSL)
case https:
return https_.headers();
#endif // !defined(URDL_DISABLE_SSL)
default:
return std::string();
}
}
/// Reads some data from the stream.
/**
* @param buffers One or more buffers into which the data will be read. The
* type must meet the requirements for @c MutableBufferSequence, as defined in
* the Boost.Asio documentation.
*
* @returns The number of bytes read.
*
* @throws boost::system::system_error Thrown on failure. An error code of
* @c boost::asio::error::eof indicates that the end of the URL content has
* been reached.
*
* @par Remarks
* The function call will block until one or more bytes of data has been read
* successfully, or until an error occurs.
*
* The @c read_some operation may not read all of the requested number of
* bytes. Consider using the @c boost::asio::read function if you need to
* ensure that the requested amount of data is read before the blocking
* operation completes.
*
* @par Example
* To read into a single data buffer use the @c boost::asio::buffer function
* as follows:
* @code
* read_stream.read_some(boost::asio::buffer(data, size));
* @endcode
* See the documentation for the @c boost::asio::buffer function for
* information on reading into multiple buffers in one go, and how to use it
* with arrays, @c boost::array or @c std::vector.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers)
{
boost::system::error_code ec;
std::size_t bytes_transferred = read_some(buffers, ec);
if (ec)
{
boost::system::system_error ex(ec);
boost::throw_exception(ex);
}
return bytes_transferred;
}
/// Reads some data from the stream.
/**
* @param buffers One or more buffers into which the data will be read. The
* type must meet the requirements for @c MutableBufferSequence, as defined in
* the Boost.Asio documentation.
*
* @param ec Set to indicate what error occurred, if any. An error code of
* @c boost::asio::error::eof indicates that the end of the URL content has
* been reached.
*
* @returns The number of bytes read.
*
* @par Remarks
* This function is used to read data from the stream. The function call will
* block until one or more bytes of data has been read successfully, or until
* an error occurs.
*
* The @c read_some operation may not read all of the requested number of
* bytes. Consider using the @c boost::asio::read function if you need to
* ensure that the requested amount of data is read before the blocking
* operation completes.
*
* @par Example
* To read into a single data buffer use the @c boost::asio::buffer function
* as follows:
* @code
* read_stream.read_some(boost::asio::buffer(data, size));
* @endcode
* See the documentation for the @c boost::asio::buffer function for
* information on reading into multiple buffers in one go, and how to use it
* with arrays, @c boost::array or @c std::vector.
*/
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers,
boost::system::error_code& ec)
{
switch (protocol_)
{
case file:
return file_.read_some(buffers, ec);
case http:
return http_.read_some(buffers, ec);
#if !defined(URDL_DISABLE_SSL)
case https:
return https_.read_some(buffers, ec);
#endif // !defined(URDL_DISABLE_SSL)
default:
ec = boost::asio::error::operation_not_supported;
return 0;
}
}
/// Asynchronously reads some data from the stream.
/**
* @param buffers One or more buffers into which the data will be read. The
* type must meet the requirements for @c MutableBufferSequence, as defined in
* the Boost.Asio documentation. Although the buffers object may be copied as
* necessary, ownership of the underlying memory blocks is retained by the
* caller, which must guarantee that they remain valid until the handler is
* called.
*
* @param handler The handler to be called when the read operation completes.
* Copies will be made of the handler as required. The function signature of
* the handler must be:
* @code
* void handler(
* const boost::system::error_code& ec, // Result of operation.
* std::size_t bytes_transferred // Number of bytes read.
* );
* @endcode
* Regardless of whether the asynchronous operation completes immediately or
* not, the handler will not be invoked from within this function. Invocation
* of the handler will be performed in a manner equivalent to using
* @c boost::asio::io_service::post().
*
* @par Remarks
* The asynchronous operation will continue until one or more bytes of data
* has been read successfully, or until an error occurs.
*
* The @c async_read_some operation may not read all of the requested number
* of bytes. Consider using the @c boost::asio::async_read function if you
* need to ensure that the requested amount of data is read before the
* asynchronous operation completes.
*
* @par Example
* To read into a single data buffer use the @c boost::asio::buffer function
* as follows:
* @code
* read_stream.async_read_some(boost::asio::buffer(data, size), handler);
* @endcode
* See the documentation for the @c boost::asio::buffer function for
* information on reading into multiple buffers in one go, and how to use it
* with arrays, @c boost::array or @c std::vector.
*/
template <typename MutableBufferSequence, typename Handler>
URDL_INITFN_RESULT_TYPE(Handler,
void (boost::system::error_code, std::size_t))
async_read_some(const MutableBufferSequence& buffers, Handler handler)
{
#if (BOOST_VERSION >= 105400)
typedef typename boost::asio::handler_type<Handler,
void (boost::system::error_code, std::size_t)>::type real_handler_type;
real_handler_type real_handler(handler);
boost::asio::async_result<real_handler_type> result(real_handler);
#else // (BOOST_VERSION >= 105400)
Handler real_handler(handler);
#endif // (BOOST_VERSION >= 105400)
switch (protocol_)
{
case file:
file_.async_read_some(buffers, real_handler);
break;
case http:
http_.async_read_some(buffers, real_handler);
break;
#if !defined(URDL_DISABLE_SSL)
case https:
https_.async_read_some(buffers, real_handler);
break;
#endif // !defined(URDL_DISABLE_SSL)
default:
boost::system::error_code ec
= boost::asio::error::operation_not_supported;
io_service_.post(boost::asio::detail::bind_handler(real_handler, ec, 0));
break;
}
#if (BOOST_VERSION >= 105400)
return result.get();
#endif // (BOOST_VERSION >= 105400)
}
private:
template <typename Handler>
class open_coro : detail::coroutine
{
public:
open_coro(read_stream* this_ptr, const url& u, Handler handler)
: this_(this_ptr),
url_(u),
handler_(handler)
{
}
void operator()(boost::system::error_code ec)
{
URDL_CORO_BEGIN;
for (;;)
{
if (url_.protocol() == "file")
{
this_->protocol_ = file;
URDL_CORO_YIELD(this_->file_.async_open(url_, *this));
handler_(ec);
return;
}
else if (url_.protocol() == "http")
{
this_->protocol_ = http;
URDL_CORO_YIELD(this_->http_.async_open(url_, *this));
if (ec == http::errc::moved_permanently || ec == http::errc::found)
{
url_ = this_->http_.location();
this_->http_.close(ec);
continue;
}
handler_(ec);
return;
}
#if !defined(URDL_DISABLE_SSL)
else if (url_.protocol() == "https")
{
this_->protocol_ = https;
URDL_CORO_YIELD(this_->https_.async_open(url_, *this));
if (ec == http::errc::moved_permanently || ec == http::errc::found)
{
url_ = this_->https_.location();
this_->https_.close(ec);
continue;
}
handler_(ec);
return;
}
#endif // !defined(URDL_DISABLE_SSL)
else
{
ec = boost::asio::error::operation_not_supported;
this_->io_service_.post(
boost::asio::detail::bind_handler(handler_, ec));
return;
}
}
URDL_CORO_END;
}
friend void* asio_handler_allocate(std::size_t size,
open_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size, &this_handler->handler_);
}
friend void asio_handler_deallocate(void* pointer, std::size_t size,
open_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(pointer, size, &this_handler->handler_);
}
template <typename Function>
friend void asio_handler_invoke(const Function& function,
open_coro<Handler>* this_handler)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, &this_handler->handler_);
}
private:
read_stream* this_;
url url_;
Handler handler_;
};
template <typename Handler> friend class open_coro;
boost::asio::io_service& io_service_;
option_set options_;
detail::file_read_stream file_;
detail::http_read_stream<boost::asio::ip::tcp::socket> http_;
#if !defined(URDL_DISABLE_SSL)
boost::asio::ssl::context ssl_context_;
detail::http_read_stream<
boost::asio::ssl::stream<
boost::asio::ip::tcp::socket> > https_;
#endif // !defined(URDL_DISABLE_SSL)
enum { unknown, file, http, https } protocol_;
};
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#endif // URDL_READ_STREAM_HPP

View File

@ -1,269 +0,0 @@
//
// url.hpp
// ~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// path LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef URDL_URL_HPP
#define URDL_URL_HPP
#include <string>
#include <boost/system/error_code.hpp>
#include "urdl/detail/config.hpp"
#include "urdl/detail/abi_prefix.hpp"
namespace urdl {
/// The class @c url enables parsing and accessing the components of URLs.
/**
* @par Example
* To extract the components of a URL:
* @code
* urdl::url url("http://user:pass@host:1234/dir/page?param=0#anchor");
* std::cout << "Protocol: " << url.protocol() << std::endl;
* std::cout << "User Info: " << url.user_info() << std::endl;
* std::cout << "Host: " << url.host() << std::endl;
* std::cout << "Port: " << url.port() << std::endl;
* std::cout << "Path: " << url.path() << std::endl;
* std::cout << "Query: " << url.query() << std::endl;
* std::cout << "Fragment: " << url.fragment() << std::endl;
* @endcode
* The above code will print:
* @code
* Protocol: http
* User Info: user:pass
* Host: host
* Port: 1234
* Path: /dir/page
* Query: param=0
* Fragment: anchor
* @endcode
*
* @par Requirements
* @e Header: @c <urdl/url.hpp> @n
* @e Namespace: @c urdl
*/
class url
{
public:
/// Constructs an object of class @c url.
/**
* @par Remarks
* Postconditions: @c protocol(), @c user_info(), @c host(), @c path(),
* @c query(), @c fragment() all return an empty string, and @c port() returns
* 0.
*/
url()
: ipv6_host_(false)
{
}
/// Constructs an object of class @c url.
/**
* @param s URL string to be parsed into its components.
*
* @throws boost::system::system_error Thrown when the URL string is invalid.
*/
url(const char* s)
: ipv6_host_(false)
{
*this = from_string(s);
}
/// Constructs an object of class @c url.
/**
* @param s URL string to be parsed into its components.
*
* @throws boost::system::system_error Thrown when the URL string is invalid.
*/
url(const std::string& s)
: ipv6_host_(false)
{
*this = from_string(s);
}
/// Gets the protocol component of the URL.
/**
* @returns A string specifying the protocol of the URL. Examples include
* @c http, @c https or @c file.
*/
std::string protocol() const
{
return protocol_;
}
/// Gets the user info component of the URL.
/**
* @returns A string containing the user info of the URL. Typically in the
* format <tt>user:password</tt>, but depends on the protocol.
*/
std::string user_info() const
{
return user_info_;
}
/// Gets the host component of the URL.
/**
* @returns A string containing the host name of the URL.
*/
std::string host() const
{
return host_;
}
/// Gets the port component of the URL.
/**
* @returns The port number of the URL.
*
* @par Remarks
* If the URL string did not specify a port, and the protocol is one of @c
* http, @c https or @c ftp, an appropriate default port number is returned.
*/
URDL_DECL unsigned short port() const;
/// Gets the path component of the URL.
/**
* @returns A string containing the path of the URL.
*
* @par Remarks
* The path string is unescaped. To obtain the path in escaped form, use
* @c to_string(url::path_component).
*/
URDL_DECL std::string path() const;
/// Gets the query component of the URL.
/**
* @returns A string containing the query string of the URL.
*
* @par Remarks
* The query string is not unescaped, but is returned in whatever form it
* takes in the original URL string.
*/
std::string query() const
{
return query_;
}
/// Gets the fragment component of the URL.
/**
* @returns A string containing the fragment of the URL.
*/
std::string fragment() const
{
return fragment_;
}
/// Components of the URL, used with @c from_string.
enum components_type
{
protocol_component = 1,
user_info_component = 2,
host_component = 4,
port_component = 8,
path_component = 16,
query_component = 32,
fragment_component = 64,
all_components = protocol_component | user_info_component | host_component
| port_component | path_component | query_component | fragment_component
};
/// Converts an object of class @c url to a string representation.
/**
* @param components A bitmask specifying which components of the URL should
* be included in the string. See the @c url::components_type enumeration for
* possible values.
*
* @returns A string representation of the URL.
*
* @par Examples
* To convert the entire URL to a string:
* @code
* std::string s = url.to_string();
* @endcode
* To convert only the host and port number into a string:
* @code
* std::string s = url.to_string(
* urdl::url::host_component
* | urdl::url::port_component);
* @endcode
*/
URDL_DECL std::string to_string(int components = all_components) const;
/// Converts a string representation of a URL into an object of class @c url.
/**
* @param s URL string to be parsed into its components.
*
* @returns A @c url object corresponding to the specified string.
*
* @throws boost::system::system_error Thrown when the URL string is invalid.
*/
URDL_DECL static url from_string(const char* s);
/// Converts a string representation of a URL into an object of class @c url.
/**
* @param s URL string to be parsed into its components.
*
* @param ec Error code set to indicate the reason for failure, if any.
*
* @returns A @c url object corresponding to the specified string.
*/
URDL_DECL static url from_string(const char* s,
boost::system::error_code& ec);
/// Converts a string representation of a URL into an object of class @c url.
/**
* @param s URL string to be parsed into its components.
*
* @returns A @c url object corresponding to the specified string.
*
* @throws boost::system::system_error Thrown when the URL string is invalid.
*/
URDL_DECL static url from_string(const std::string& s);
/// Converts a string representation of a URL into an object of class @c url.
/**
* @param s URL string to be parsed into its components.
*
* @param ec Error code set to indicate the reason for failure, if any.
*
* @returns A @c url object corresponding to the specified string.
*/
URDL_DECL static url from_string(const std::string& s,
boost::system::error_code& ec);
/// Compares two @c url objects for equality.
friend URDL_DECL bool operator==(const url& a, const url& b);
/// Compares two @c url objects for inequality.
friend URDL_DECL bool operator!=(const url& a, const url& b);
/// Compares two @c url objects for ordering.
friend URDL_DECL bool operator<(const url& a, const url& b);
private:
URDL_DECL static bool unescape_path(const std::string& in, std::string& out);
std::string protocol_;
std::string user_info_;
std::string host_;
std::string port_;
std::string path_;
std::string query_;
std::string fragment_;
bool ipv6_host_;
};
} // namespace urdl
#include "urdl/detail/abi_suffix.hpp"
#if defined(URDL_HEADER_ONLY)
# include "urdl/impl/url.ipp"
#endif
#endif // URDL_URL_HPP

View File

@ -1,17 +0,0 @@
#if defined(URDL_HEADER_ONLY)
# error Do not compile Urdl library source with URDL_HEADER_ONLY defined
#endif
#define URDL_SOURCE
#include "urdl/istreambuf.hpp"
#include "urdl/impl/istreambuf.ipp"
#include "urdl/http.hpp"
#include "urdl/impl/http.ipp"
#include "urdl/option_set.hpp"
#include "urdl/impl/option_set.ipp"
#include "urdl/url.hpp"
#include "urdl/impl/url.ipp"

View File

@ -1,27 +0,0 @@
#
# Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
project
:
requirements
<library>../build//urdl
<library>/boost/thread//boost_thread
:
default-build
debug
<threading>multi
<link>shared
<runtime-link>shared
;
test-suite "urdl" :
[ run istream.cpp ]
[ run istreambuf.cpp ]
[ run option_set.cpp ]
[ run read_stream.cpp ]
[ run url.cpp ]
;

View File

@ -1,118 +0,0 @@
//
// http_server.hpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef HTTP_SERVER_HPP
#define HTTP_SERVER_HPP
#include <boost/asio/buffer.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/write.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/thread.hpp>
// Helper class to test HTTP client functionality.
class http_server
{
public:
typedef boost::asio::ip::tcp tcp;
http_server()
: acceptor_(io_service_, tcp::endpoint(
boost::asio::ip::address_v4::loopback(), 0)),
socket_(io_service_),
response_delay_(0),
content_delay_(0),
success_(false)
{
}
unsigned short port() const
{
return acceptor_.local_endpoint().port();
}
void start(const std::string& expected_request,
std::size_t response_delay, const std::string& response,
std::size_t content_delay, const std::string& content)
{
success_ = false;
expected_request_ = expected_request;
response_delay_ = response_delay;
response_ = response;
content_delay_ = content_delay;
content_ = content;
thread_.reset(new boost::thread(boost::bind(&http_server::worker, this)));
}
bool stop()
{
thread_->join();
thread_.reset();
return success_;
}
private:
void worker()
{
try
{
acceptor_.accept(socket_);
// Wait for request.
boost::asio::streambuf buffer;
std::size_t size = boost::asio::read_until(socket_, buffer, "\r\n\r\n");
std::string request(size, 0);
buffer.sgetn(&request[0], size);
success_ = (request == expected_request_);
// Introduce a delay before sending the response.
boost::asio::deadline_timer timer(io_service_);
timer.expires_from_now(boost::posix_time::milliseconds(response_delay_));
timer.wait();
// Send response headers.
boost::system::error_code ec;
boost::asio::write(socket_, boost::asio::buffer(response_));
// Introduce a delay before sending the content.
timer.expires_from_now(boost::posix_time::milliseconds(content_delay_));
timer.wait();
// Now we can write the content.
boost::asio::write(socket_, boost::asio::buffer(content_));
// We're done. Shut down the connection.
socket_.shutdown(tcp::socket::shutdown_both, ec);
socket_.close(ec);
}
catch (std::exception&)
{
}
}
boost::asio::io_service io_service_;
tcp::acceptor acceptor_;
tcp::socket socket_;
std::string expected_request_;
std::size_t response_delay_;
std::string response_;
std::size_t content_delay_;
std::string content_;
boost::scoped_ptr<boost::thread> thread_;
bool success_;
};
#endif // HTTP_SERVER_HPP

View File

@ -1,215 +0,0 @@
//
// istream.cpp
// ~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Disable autolinking for unit tests.
#if !defined(BOOST_ALL_NO_LIB)
#define BOOST_ALL_NO_LIB 1
#endif // !defined(BOOST_ALL_NO_LIB)
// Test that header file is self-contained.
#include "urdl/istream.hpp"
#include "unit_test.hpp"
#include "urdl/http.hpp"
#include "urdl/option_set.hpp"
#include "http_server.hpp"
#include <string>
#include <sstream>
// Ensure all functions compile correctly.
void istream_compile_test()
{
// Constructors
urdl::istream istream1;
urdl::istream istream2("file://foobar");
urdl::istream istream3(urdl::url("file://foobar"));
urdl::istream istream4("file://foobar", urdl::option_set());
urdl::istream istream5(urdl::url("file://foobar"), urdl::option_set());
// set_option()
istream1.set_option(0);
istream1.set_option<char>(0);
// set_options()
istream1.set_options(urdl::option_set());
// get_option()
const urdl::istream& const_istream1 = istream1;
want<int>(const_istream1.get_option<int>());
want<char>(const_istream1.get_option<char>());
// get_options()
want<urdl::option_set>(const_istream1.get_options());
// is_open()
want<bool>(const_istream1.is_open());
// open()
istream1.open("file://foobar");
istream1.open(urdl::url("file://foobar"));
// close()
istream1.close();
// rdbuf()
want<urdl::istreambuf*>(const_istream1.rdbuf());
// error()
want<boost::system::error_code>(const_istream1.error());
// read_timeout()
want<std::size_t>(const_istream1.read_timeout());
istream1.read_timeout(std::size_t(123));
// content_type()
want<std::string>(const_istream1.content_type());
// content_length()
want<std::size_t>(const_istream1.content_length());
// headers()
want<std::string>(const_istream1.headers());
}
// Test HTTP.
void istream_http_test()
{
http_server server;
std::string port = boost::lexical_cast<std::string>(server.port());
std::string request =
"GET / HTTP/1.0\r\n"
"Host: localhost:" + port + "\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
std::string response =
"HTTP/1.0 200 OK\r\n"
"Content-Length: 13\r\n"
"Content-Type: text/plain\r\n\r\n";
std::string content = "Hello, World!";
server.start(request, 0, response, 0, content);
urdl::istream istream1("http://localhost:" + port + "/");
std::string returned_content;
std::getline(istream1, returned_content);
bool request_matched = server.stop();
BOOST_CHECK(request_matched);
BOOST_CHECK(istream1.content_type() == "text/plain");
BOOST_CHECK(istream1.content_length() == 13);
BOOST_CHECK(returned_content == content);
}
// Test HTTP with an error status returned by the server.
void istream_http_not_found_test()
{
http_server server;
std::string port = boost::lexical_cast<std::string>(server.port());
std::string request =
"GET / HTTP/1.0\r\n"
"Host: localhost:" + port + "\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
std::string response =
"HTTP/1.0 404 Not Found\r\n"
"Content-Length: 9\r\n"
"Content-Type: text/plain\r\n\r\n";
std::string content = "Not Found";
server.start(request, 0, response, 0, content);
urdl::istream istream1("http://localhost:" + port + "/");
std::string returned_content;
std::getline(istream1, returned_content);
bool request_matched = server.stop();
BOOST_CHECK(request_matched);
BOOST_CHECK(istream1.error() == urdl::http::errc::not_found);
}
// Test HTTP with an open timeout.
void istream_http_open_timeout_test()
{
http_server server;
std::string port = boost::lexical_cast<std::string>(server.port());
std::string request =
"GET / HTTP/1.0\r\n"
"Host: localhost:" + port + "\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
std::string response =
"HTTP/1.0 200 OK\r\n"
"Content-Length: 13\r\n"
"Content-Type: text/plain\r\n\r\n";
std::string content = "Hello, World!";
server.start(request, 1500, response, 0, content);
urdl::istream istream1;
istream1.open_timeout(1000);
istream1.open("http://localhost:" + port + "/");
server.stop();
BOOST_CHECK(istream1.error() == boost::system::errc::timed_out);
}
// Test HTTP with a read timeout.
void istream_http_read_timeout_test()
{
http_server server;
std::string port = boost::lexical_cast<std::string>(server.port());
std::string request =
"GET / HTTP/1.0\r\n"
"Host: localhost:" + port + "\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
std::string response =
"HTTP/1.0 200 OK\r\n"
"Content-Length: 13\r\n"
"Content-Type: text/plain\r\n\r\n";
std::string content = "Hello, World!";
server.start(request, 0, response, 1500, content);
urdl::istream istream1;
istream1.open("http://localhost:" + port + "/");
istream1.read_timeout(1000);
std::string returned_content;
std::getline(istream1, returned_content);
bool request_matched = server.stop();
BOOST_CHECK(request_matched);
BOOST_CHECK(istream1.error() == boost::system::errc::timed_out);
}
test_suite* init_unit_test_suite(int, char*[])
{
test_suite* test = BOOST_TEST_SUITE("istream");
test->add(BOOST_TEST_CASE(&istream_compile_test));
test->add(BOOST_TEST_CASE(&istream_http_test));
test->add(BOOST_TEST_CASE(&istream_http_not_found_test));
test->add(BOOST_TEST_CASE(&istream_http_open_timeout_test));
test->add(BOOST_TEST_CASE(&istream_http_read_timeout_test));
return test;
}

View File

@ -1,88 +0,0 @@
//
// istreambuf.cpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Disable autolinking for unit tests.
#if !defined(BOOST_ALL_NO_LIB)
#define BOOST_ALL_NO_LIB 1
#endif // !defined(BOOST_ALL_NO_LIB)
// Test that header file is self-contained.
#include "urdl/istreambuf.hpp"
#include "unit_test.hpp"
#include "urdl/option_set.hpp"
// Ensure all functions compile correctly.
void istreambuf_compile_test()
{
// Constructors
urdl::istreambuf istreambuf1;
// set_option()
istreambuf1.set_option(0);
istreambuf1.set_option<char>(0);
// set_options()
istreambuf1.set_options(urdl::option_set());
// get_option()
const urdl::istreambuf& const_istreambuf1 = istreambuf1;
want<int>(const_istreambuf1.get_option<int>());
want<char>(const_istreambuf1.get_option<char>());
// get_options()
want<urdl::option_set>(const_istreambuf1.get_options());
// is_open()
want<bool>(const_istreambuf1.is_open());
// open()
want<urdl::istreambuf*>(istreambuf1.open("file://foobar"));
want<urdl::istreambuf*>(istreambuf1.open(urdl::url("file://foobar")));
// close()
want<urdl::istreambuf*>(istreambuf1.close());
// puberror()
want<boost::system::error_code>(const_istreambuf1.puberror());
// read_timeout()
want<std::size_t>(const_istreambuf1.read_timeout());
istreambuf1.read_timeout(std::size_t(123));
// content_type()
want<std::string>(const_istreambuf1.content_type());
// content_length()
want<std::size_t>(const_istreambuf1.content_length());
// headers()
want<std::string>(const_istreambuf1.headers());
}
test_suite* init_unit_test_suite(int, char*[])
{
test_suite* test = BOOST_TEST_SUITE("istreambuf");
test->add(BOOST_TEST_CASE(&istreambuf_compile_test));
return test;
}

View File

@ -1,99 +0,0 @@
//
// option_set.cpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Disable autolinking for unit tests.
#if !defined(BOOST_ALL_NO_LIB)
#define BOOST_ALL_NO_LIB 1
#endif // !defined(BOOST_ALL_NO_LIB)
// Test that header file is self-contained.
#include "urdl/option_set.hpp"
#include "unit_test.hpp"
// Ensure all functions compile correctly.
void option_set_compile_test()
{
// Constructors
urdl::option_set set1;
urdl::option_set set2(set1);
// operator=
set1 = set2;
// set_option()
set1.set_option(0);
set1.set_option<char>(0);
// set_options()
const urdl::option_set& const_set1 = set1;
set2.set_options(const_set1);
// get_option()
want<int>(const_set1.get_option<int>());
want<char>(const_set1.get_option<char>());
// clear_option()
set1.clear_option<int>();
set1.clear_option<char>();
}
// Test option_set runtime behaviour.
void option_set_runtime_test()
{
urdl::option_set set1;
set1.set_option(std::string("foobar"));
set1.set_option(int(123));
urdl::option_set set2(set1);
BOOST_CHECK(set2.get_option<std::string>() == set1.get_option<std::string>());
BOOST_CHECK(set2.get_option<int>() == set1.get_option<int>());
urdl::option_set set3;
set3 = set2;
BOOST_CHECK(set3.get_option<std::string>() == set1.get_option<std::string>());
BOOST_CHECK(set3.get_option<int>() == set1.get_option<int>());
urdl::option_set set4;
set4.set_option(char('A'));
set4.set_options(set3);
BOOST_CHECK(set4.get_option<char>() == 'A');
BOOST_CHECK(set4.get_option<std::string>() == set1.get_option<std::string>());
BOOST_CHECK(set4.get_option<int>() == set1.get_option<int>());
set4.clear_option<std::string>();
BOOST_CHECK(set4.get_option<char>() == 'A');
BOOST_CHECK(set4.get_option<std::string>() == "");
BOOST_CHECK(set4.get_option<int>() == set1.get_option<int>());
set4.set_option(int(321));
BOOST_CHECK(set4.get_option<char>() == 'A');
BOOST_CHECK(set4.get_option<std::string>() == "");
BOOST_CHECK(set4.get_option<int>() == 321);
set4.set_options(set1);
BOOST_CHECK(set4.get_option<char>() == 'A');
BOOST_CHECK(set4.get_option<std::string>() == set1.get_option<std::string>());
BOOST_CHECK(set4.get_option<int>() == set1.get_option<int>());
}
test_suite* init_unit_test_suite(int, char*[])
{
test_suite* test = BOOST_TEST_SUITE("option_set");
test->add(BOOST_TEST_CASE(&option_set_compile_test));
test->add(BOOST_TEST_CASE(&option_set_runtime_test));
return test;
}

View File

@ -1,280 +0,0 @@
//
// read_stream.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Disable autolinking for unit tests.
#if !defined(BOOST_ALL_NO_LIB)
#define BOOST_ALL_NO_LIB 1
#endif // !defined(BOOST_ALL_NO_LIB)
// Test that header file is self-contained.
#include "urdl/read_stream.hpp"
#include "unit_test.hpp"
#include "urdl/option_set.hpp"
#include "http_server.hpp"
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/read.hpp>
void open_handler(const boost::system::error_code&) {}
void read_handler(const boost::system::error_code&, std::size_t) {}
// Ensure all functions compile correctly.
void read_stream_compile_test()
{
try
{
boost::asio::io_service io_service;
boost::system::error_code ec;
char buffer[1024];
// Constructors
urdl::read_stream stream1(io_service);
// get_io_service()
want<boost::asio::io_service>(stream1.get_io_service());
// set_option()
stream1.set_option(0);
stream1.set_option<char>(0);
// set_options()
stream1.set_options(urdl::option_set());
// get_option()
const urdl::read_stream& const_stream1 = stream1;
want<int>(const_stream1.get_option<int>());
want<char>(const_stream1.get_option<char>());
// get_options()
want<urdl::option_set>(const_stream1.get_options());
// is_open()
want<bool>(const_stream1.is_open());
// open()
stream1.open("file://xyz");
stream1.open(urdl::url("file://xyz"));
want<boost::system::error_code>(stream1.open("file://xyz", ec));
want<boost::system::error_code>(stream1.open(urdl::url("file://xyz"), ec));
// async_open()
stream1.async_open("file://xyz", open_handler);
stream1.async_open(urdl::url("file://xyz"), open_handler);
// close()
stream1.close();
want<boost::system::error_code>(stream1.close(ec));
// content_type()
want<std::string>(const_stream1.content_type());
// content_length()
want<std::size_t>(const_stream1.content_length());
// headers()
want<std::string>(const_stream1.headers());
// read_some()
want<std::size_t>(stream1.read_some(boost::asio::buffer(buffer)));
want<std::size_t>(stream1.read_some(boost::asio::buffer(buffer), ec));
// async_read_some()
stream1.async_read_some(boost::asio::buffer(buffer), read_handler);
}
catch (std::exception&)
{
}
}
// Test synchronous HTTP.
void read_stream_synchronous_http_test()
{
http_server server;
std::string port = boost::lexical_cast<std::string>(server.port());
std::string request =
"GET / HTTP/1.0\r\n"
"Host: localhost:" + port + "\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
std::string response =
"HTTP/1.0 200 OK\r\n"
"Content-Length: 13\r\n"
"Content-Type: text/plain\r\n\r\n";
std::string content = "Hello, World!";
server.start(request, 0, response, 0, content);
boost::asio::io_service io_service;
urdl::read_stream stream1(io_service);
stream1.open("http://localhost:" + port + "/");
std::string returned_content(stream1.content_length(), 0);
boost::asio::read(stream1, boost::asio::buffer(
&returned_content[0], returned_content.size()));
bool request_matched = server.stop();
BOOST_CHECK(request_matched);
BOOST_CHECK(stream1.content_type() == "text/plain");
BOOST_CHECK(stream1.content_length() == 13);
BOOST_CHECK(returned_content == content);
}
// Test synchronous HTTP with an error status returned by the server.
void read_stream_synchronous_http_not_found_test()
{
http_server server;
std::string port = boost::lexical_cast<std::string>(server.port());
std::string request =
"GET / HTTP/1.0\r\n"
"Host: localhost:" + port + "\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
std::string response =
"HTTP/1.0 404 Not Found\r\n"
"Content-Length: 9\r\n"
"Content-Type: text/plain\r\n\r\n";
std::string content = "Not Found";
server.start(request, 0, response, 0, content);
boost::asio::io_service io_service;
urdl::read_stream stream1(io_service);
boost::system::error_code ec;
stream1.open("http://localhost:" + port + "/", ec);
bool request_matched = server.stop();
BOOST_CHECK(request_matched);
BOOST_CHECK(ec == urdl::http::errc::not_found);
}
struct handler
{
boost::system::error_code& ec_;
std::size_t& size_;
void operator()(const boost::system::error_code& ec, std::size_t size = 0)
{
ec_ = ec;
size_ = size;
}
};
// Test asynchronous HTTP.
void read_stream_asynchronous_http_test()
{
http_server server;
std::string port = boost::lexical_cast<std::string>(server.port());
std::string request =
"GET / HTTP/1.0\r\n"
"Host: localhost:" + port + "\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
std::string response =
"HTTP/1.0 200 OK\r\n"
"Content-Length: 13\r\n"
"Content-Type: text/plain\r\n\r\n";
std::string content = "Hello, World!";
server.start(request, 0, response, 0, content);
boost::asio::io_service io_service;
urdl::read_stream stream1(io_service);
boost::system::error_code ec;
std::size_t bytes_transferred = 0;
handler h = { ec, bytes_transferred };
stream1.async_open("http://localhost:" + port + "/", h);
io_service.run();
BOOST_CHECK(!ec);
std::string returned_content(stream1.content_length(), 0);
boost::asio::async_read(stream1, boost::asio::buffer(
&returned_content[0], returned_content.size()), h);
io_service.reset();
io_service.run();
BOOST_CHECK(!ec);
BOOST_CHECK(bytes_transferred == returned_content.size());
bool request_matched = server.stop();
BOOST_CHECK(request_matched);
BOOST_CHECK(stream1.content_type() == "text/plain");
BOOST_CHECK(stream1.content_length() == 13);
BOOST_CHECK(returned_content == content);
}
// Test asynchronous HTTP with an error status returned by the server.
void read_stream_asynchronous_http_not_found_test()
{
http_server server;
std::string port = boost::lexical_cast<std::string>(server.port());
std::string request =
"GET / HTTP/1.0\r\n"
"Host: localhost:" + port + "\r\n"
"Accept: */*\r\n"
"Connection: close\r\n\r\n";
std::string response =
"HTTP/1.0 404 Not Found\r\n"
"Content-Length: 9\r\n"
"Content-Type: text/plain\r\n\r\n";
std::string content = "Not Found";
server.start(request, 0, response, 0, content);
boost::asio::io_service io_service;
urdl::read_stream stream1(io_service);
boost::system::error_code ec;
std::size_t bytes_transferred = 0;
handler h = { ec, bytes_transferred };
stream1.async_open("http://localhost:" + port + "/", h);
io_service.run();
bool request_matched = server.stop();
BOOST_CHECK(request_matched);
BOOST_CHECK(ec == urdl::http::errc::not_found);
}
test_suite* init_unit_test_suite(int, char*[])
{
test_suite* test = BOOST_TEST_SUITE("read_stream");
test->add(BOOST_TEST_CASE(&read_stream_compile_test));
test->add(BOOST_TEST_CASE(&read_stream_synchronous_http_test));
test->add(BOOST_TEST_CASE(&read_stream_synchronous_http_not_found_test));
test->add(BOOST_TEST_CASE(&read_stream_asynchronous_http_test));
test->add(BOOST_TEST_CASE(&read_stream_asynchronous_http_not_found_test));
return test;
}

View File

@ -1,39 +0,0 @@
//
// unit_test.hpp
// ~~~~~~~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef UNIT_TEST_HPP
#define UNIT_TEST_HPP
#include <boost/config.hpp>
#if defined(BOOST_MSVC)
# pragma warning (push)
# pragma warning (disable:4244)
# pragma warning (disable:4535)
# pragma warning (disable:4702)
# pragma warning (disable:4996)
#endif // defined(BOOST_MSVC)
#include <boost/test/unit_test.hpp>
#include <boost/test/included/unit_test_framework.hpp>
using boost::unit_test::test_suite;
#if defined(BOOST_MSVC)
# pragma warning (pop)
# pragma warning (disable:4996) // Leave this disabled for the unit tests.
#endif // defined(BOOST_MSVC)
// Helper function to check the return type of a function.
template <typename T>
void want(const T&)
{
}
#endif // UNIT_TEST_HPP

View File

@ -1,311 +0,0 @@
//
// url.cpp
// ~~~~~~~
//
// Copyright (c) 2009-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Disable autolinking for unit tests.
#if !defined(BOOST_ALL_NO_LIB)
#define BOOST_ALL_NO_LIB 1
#endif // !defined(BOOST_ALL_NO_LIB)
// Test that header file is self-contained.
#include "urdl/url.hpp"
#include "unit_test.hpp"
// Ensure all functions compile correctly.
void url_compile_test()
{
// Constructors
urdl::url url1;
urdl::url url2("http://foo/bar");
urdl::url url3(std::string("http://foo/bar"));
urdl::url url4(url1);
urdl::url url5 = url1;
// operator=
url5 = url2;
// protocol()
const urdl::url& const_url1 = url1;
want<std::string>(const_url1.protocol());
// user_info()
want<std::string>(const_url1.user_info());
// host()
want<std::string>(const_url1.host());
// port()
want<unsigned short>(const_url1.port());
// path()
want<std::string>(const_url1.path());
// query()
want<std::string>(const_url1.query());
// fragment()
want<std::string>(const_url1.fragment());
// to_string()
want<std::string>(const_url1.to_string());
want<std::string>(const_url1.to_string(urdl::url::protocol_component));
want<std::string>(const_url1.to_string(urdl::url::user_info_component));
want<std::string>(const_url1.to_string(urdl::url::host_component));
want<std::string>(const_url1.to_string(urdl::url::port_component));
want<std::string>(const_url1.to_string(urdl::url::path_component));
want<std::string>(const_url1.to_string(urdl::url::query_component));
want<std::string>(const_url1.to_string(urdl::url::fragment_component));
want<std::string>(const_url1.to_string(urdl::url::all_components));
// from_string()
boost::system::error_code ec;
want<urdl::url>(urdl::url::from_string("http://foo/bar"));
want<urdl::url>(urdl::url::from_string("http://foo/bar", ec));
want<urdl::url>(urdl::url::from_string(std::string("http://foo/bar")));
want<urdl::url>(urdl::url::from_string(std::string("http://foo/bar"), ec));
// operator==
const urdl::url& const_url2 = url2;
want<bool>(const_url1 == const_url2);
// operator!=
want<bool>(const_url1 != const_url2);
// operator<
want<bool>(const_url1 < const_url2);
}
// Test URL parsing.
void url_from_string_test()
{
boost::system::error_code ec;
urdl::url url = urdl::url::from_string("http://host", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("HTTP://host", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://host:123", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://user@host", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "user");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://user@host:123", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "user");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://user:pass@host", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "user:pass");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://user:pass@host:123", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "user:pass");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://host/path", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/path");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://host:123/path", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/path");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://host?query", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "query");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://host:123?query", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "query");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://host/path?query", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/path");
BOOST_CHECK(url.query() == "query");
BOOST_CHECK(url.fragment() == "");
url = urdl::url::from_string("http://host#fragment", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "fragment");
url = urdl::url::from_string("http://host:123#fragment", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "fragment");
url = urdl::url::from_string("http://host/path#fragment", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/path");
BOOST_CHECK(url.query() == "");
BOOST_CHECK(url.fragment() == "fragment");
url = urdl::url::from_string("http://host?query#fragment", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/");
BOOST_CHECK(url.query() == "query");
BOOST_CHECK(url.fragment() == "fragment");
url = urdl::url::from_string("http://host/path?query#fragment", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 80);
BOOST_CHECK(url.path() == "/path");
BOOST_CHECK(url.query() == "query");
BOOST_CHECK(url.fragment() == "fragment");
url = urdl::url::from_string("http://host:123/path?query#fragment", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/path");
BOOST_CHECK(url.query() == "query");
BOOST_CHECK(url.fragment() == "fragment");
url = urdl::url::from_string("http://user@host:123/path?query#fragment", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "user");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/path");
BOOST_CHECK(url.query() == "query");
BOOST_CHECK(url.fragment() == "fragment");
url = urdl::url::from_string("http://u:p@host:123/path?query#fragment", ec);
BOOST_CHECK(!ec);
BOOST_CHECK(url.protocol() == "http");
BOOST_CHECK(url.user_info() == "u:p");
BOOST_CHECK(url.host() == "host");
BOOST_CHECK(url.port() == 123);
BOOST_CHECK(url.path() == "/path");
BOOST_CHECK(url.query() == "query");
BOOST_CHECK(url.fragment() == "fragment");
}
test_suite* init_unit_test_suite(int, char*[])
{
test_suite* test = BOOST_TEST_SUITE("url");
test->add(BOOST_TEST_CASE(&url_compile_test));
test->add(BOOST_TEST_CASE(&url_from_string_test));
return test;
}