77#ifndef __NETWORK_PROTOCOL_HTTP_CLIENT_20070908_1_HPP__
88#define __NETWORK_PROTOCOL_HTTP_CLIENT_20070908_1_HPP__
99
10- #ifndef BOOST_NETLIB_VERSION
11- #define BOOST_NETLIB_VERSION " 0.5"
12- #endif
13-
10+ #include < boost/network/version.hpp>
1411#include < boost/network/traits/ostringstream.hpp>
1512#include < boost/network/protocol/http/message.hpp>
1613#include < boost/network/protocol/http/response.hpp>
1714#include < boost/network/protocol/http/request.hpp>
18- #include < boost/network/protocol/http/traits/resolver_policy .hpp>
15+ #include < boost/network/protocol/http/traits/connection_policy .hpp>
1916#include < boost/asio.hpp>
2017#include < boost/lexical_cast.hpp>
2118#include < boost/algorithm/string/classification.hpp>
2219#include < boost/algorithm/string/split.hpp>
23- #include < boost/tuple/tuple.hpp>
2420#include < boost/foreach.hpp>
2521#include < ostream>
2622#include < istream>
3127namespace boost {namespace network {namespace http {
3228
3329template <class Tag ,unsigned version_major,unsigned version_minor>
34- class basic_client :resolver_policy <Tag>::type {
30+ class basic_client :connection_policy <Tag, version_major, version_minor >::type {
3531
3632private:
37- typedef typename resolver_policy <Tag>::typeresolver_base ;
33+ typedef typename connection_policy <Tag, version_major, version_minor >::typeconnection_base ;
3834 boost::asio::io_service service_;
39- typename resolver_base::resolver_type resolver_;
40- bool follow_redirect_;
35+ typename connection_base::resolver_type resolver_;
4136
4237typedef typename string<Tag>::type string_type;
4338
44- void init_socket (boost::asio::ip::tcp::socket & socket_, string_typeconst & hostname, string_typeconst & port) {
45- using boost::asio::ip::tcp;
46-
47- boost::system::error_code error = boost::asio::error::host_not_found;
48-
49- typename resolver_base::resolver_type::iterator endpoint_iterator, end;
50- boost::tie (endpoint_iterator, end) =resolve (resolver_, hostname, port);
51-
52- while (error && endpoint_iterator != end) {
53- socket_.close ();
54- socket_.connect (
55- tcp::endpoint (
56- endpoint_iterator->endpoint ().address ()
57- , endpoint_iterator->endpoint ().port ()
58- )
59- , error
60- );
61- ++endpoint_iterator;
62- }
63-
64- if (error)
65- throw boost::system::system_error (error);
66- };
67-
68- void create_request (boost::asio::streambuf & request_buffer, string_typeconst & method, basic_request<Tag> request_)const {
69- std::ostreamrequest_stream (&request_buffer);
70-
71- request_stream
72- << method <<" " ;
73-
74- if (request_.path ().empty () || request_.path ()[0 ] !=' /' )
75- request_stream <<' /' ;
76-
77- request_stream
78- << request_.path ()
79- ;
80-
81- if (!request_.query ().empty ())
82- request_stream
83- <<' ?'
84- << request_.query ()
85- ;
86-
87- if (!request_.anchor ().empty ())
88- request_stream
89- <<' #'
90- << request_.anchor ()
91- ;
92-
93- request_stream <<" HTTP/" << version_major <<' .' << version_minor <<" \r\n "
94- <<" Host:" << request_.host () <<" \r\n "
95- <<" Accept: */*\r\n " ;
96-
97- typename headers_range<http::basic_request<Tag> >::type range =headers (request_);
98- BOOST_FOREACH (typename headers_range<http::basic_request<Tag> >::type::value_typeconst & header, range) {
99- request_stream << header.first <<" :" << header.second <<" \r\n " ;
100- };
101-
102- range =headers (request_)[" user-agent" ];
103- if (begin (range) ==end (range)) request_stream <<" User-Agent: cpp-netlib/" << BOOST_NETLIB_VERSION <<" \r\n " ;
104-
105- request_stream
106- <<" Connection: close\r\n\r\n " ;
107-
108- string_type body_ =body (request_);
109- if (!body_.empty ())
110- request_stream << body_;
111- };
112-
113- void send_request (boost::asio::ip::tcp::socket & socket, string_typeconst & method, basic_request<Tag>const & request_)const {
114- boost::asio::streambuf request_buffer;
115- create_request (request_buffer, method, request_);
116- write (socket, request_buffer);
117- };
118-
119- void read_status (basic_response<Tag> & response_, boost::asio::ip::tcp::socket & socket, boost::asio::streambuf & response_buffer)const {
120- boost::asio::read_until (socket, response_buffer," \r\n " );
121- std::istreamresponse_stream (&response_buffer);
122- string_type http_version;
123- unsigned int status_code;
124- string_type status_message;
125- response_stream >> http_version
126- >> status_code;
127- std::getline (response_stream, status_message);
128- trim_left (status_message);
129- trim_right_if (status_message,boost::is_space () ||boost::is_any_of (" \r " ));
130-
131- if (!response_stream || http_version.substr (0 ,5 ) !=" HTTP/" )
132- throw std::runtime_error (" Invalid response" );
133-
134- response_.version () = http_version;
135- response_.status () = status_code;
136- response_.status_message () = status_message;
137- };
138-
139- void read_headers (basic_response<Tag> & response_, boost::asio::ip::tcp::socket & socket, boost::asio::streambuf & response_buffer)const {
140- boost::asio::read_until (socket, response_buffer," \r\n\r\n " );
141- std::istreamresponse_stream (&response_buffer);
142- string_type header_line, name;
143- while (std::getline (response_stream, header_line) && header_line !=" \r " ) {
144- trim_right_if (header_line,boost::is_space () ||boost::is_any_of (" \r " ));
145- typename string_type::size_type colon_offset;
146- if (header_line.size () && header_line[0 ] ==' ' ) {
147- assert (!name.empty ());
148- if (name.empty ())
149- throw std::runtime_error (
150- std::string (" Malformed header:" )
151- + header_line
152- );
153- response_
154- <<header (name,trim_left_copy (header_line));
155- }else if ((colon_offset = header_line.find_first_of (' :' )) != string_type::npos) {
156- name = header_line.substr (0 , colon_offset);
157- response_
158- <<header (name, header_line.substr (colon_offset+2 ));
159- };
160- };
161- };
162-
163- void read_body (basic_response<Tag> & response_, boost::asio::ip::tcp::socket & socket, boost::asio::streambuf & response_buffer)const {
164- typename ostringstream<Tag>::type body_stream;
165-
166- if (response_buffer.size () >0 )
167- body_stream << &response_buffer;
168-
169- boost::system::error_code error;
170- while (boost::asio::read (socket, response_buffer,boost::asio::transfer_at_least (1 ), error))
171- body_stream << &response_buffer;
172-
173- if (error != boost::asio::error::eof)
174- throw boost::system::system_error (error);
175-
176- response_ <<body (body_stream.str ());
177- };
178-
17939 basic_response<Tag>const sync_request_skeleton (basic_request<Tag>const & request_, string_type method,bool get_body) {
18040using boost::asio::ip::tcp;
18141
182- basic_request<Tag>request_copy (request_);
183- basic_response<Tag> response_;
184- do {
185- tcp::socketsocket (service_);
186- init_socket (socket, request_copy.host (), boost::lexical_cast<string_type>(request_copy.port ()));
187- send_request (socket, method, request_copy);
188-
189- response_ = basic_response<Tag>();
190- response_ <<source (request_copy.host ());
191-
192- boost::asio::streambuf response_buffer;
193- read_status (response_, socket, response_buffer);
194- read_headers (response_, socket, response_buffer);
195- if (get_body)
196- read_body (response_, socket, response_buffer);
197-
198- if (follow_redirect_) {
199- uint16_t status = response_.status ();
200- if (status >=300 && status <=307 ) {
201- typename headers_range<http::basic_response<Tag> >::type location_range =headers (response_)[" Location" ];
202- typename range_iterator<typename headers_range<http::basic_request<Tag> >::type>::type location_header =begin (location_range);
203- if (location_header !=end (location_range)) {
204- request_copy.uri (location_header->second );
205- }else throw std::runtime_error (" Location header not defined in redirect response." );
206- }else break ;
207- }else break ;
208- }while (true );
209-
210- return response_;
211- };
42+ typename connection_base::connection_ptr connection_;
43+ connection_ =connection_base::get_connection (resolver_, request_);
44+ return connection_->send_request (method, request_, get_body);
45+ }
21246
21347public:
21448
@@ -229,23 +63,23 @@ namespace boost { namespace network { namespace http {
22963 };
23064
23165basic_client ()
232- :resolver_base (false ), service_(), resolver_(service_), follow_redirect_( false )
66+ :connection_base (false , false ), service_(), resolver_(service_)
23367 {};
23468
23569explicit basic_client (cache_resolved_type (*)())
236- :resolver_base (true ), service_(), resolver_(service_), follow_redirect_( false )
70+ :connection_base (true , false ), service_(), resolver_(service_)
23771 {};
23872
23973explicit basic_client (follow_redirect_type (*)())
240- :resolver_base (false ), service_(), resolver_(service_), follow_redirect_( true )
74+ :connection_base (false , true ), service_(), resolver_(service_)
24175 {};
24276
24377basic_client (cache_resolved_type (*)(), follow_redirect_type (*)())
244- :resolver_base( false ), service_(), resolver_(service_), follow_redirect_( true )
78+ :connection_base( true , true ), service_(), resolver_(service_)
24579 {};
24680
24781void clear_resolved_cache () {
248- resolver_base ::endpoint_cache_.clear ();
82+ connection_base ::endpoint_cache_.clear ();
24983 }
25084
25185 basic_response<Tag>const head (basic_request<Tag>const & request_) {