5 #include <ail/http.hpp>
6 #include <ail/string.hpp>
7 #include <ail/zlib.hpp>
8 #include <ail/file.hpp>
13 bool process_chunked_data(std::string
const & data
, std::string
& content
, std::size_t offset
)
15 std::string
const target
= "\r\n";
16 while(offset
< data
.size())
18 std::size_t newline_offset
= data
.find(target
, offset
);
19 if(newline_offset
== std::string::npos
)
21 std::string number_string
= data
.substr(offset
, newline_offset
- offset
);
23 if(!ail::string_to_number
<long>(number_string
, chunk_size
, std::ios_base::hex
))
25 std::size_t chunk_offset
= newline_offset
+ target
.length();
26 std::size_t end_of_chunk
= chunk_offset
+ chunk_size
;
27 std::string chunk
= data
.substr(chunk_offset
, end_of_chunk
- chunk_offset
);
29 offset
= end_of_chunk
+ target
.length();
34 http_client::http_client(boost::asio::io_service
& io_service
):
35 io_service(io_service
),
38 use_compression(true),
39 has_error_handler(false),
40 has_download_handler(false),
41 has_download_finished_handler(false),
42 user_agent(http_user_agent_firefox
),
47 void http_client::use_post()
52 void http_client::set_compression(bool new_use_compression
)
54 use_compression
= new_use_compression
;
57 void http_client::add_post_data(std::string
const & field
, std::string
const & value
)
59 form_data
[field
] = value
;
62 void http_client::start_download(std::string
const & new_url
)
66 https_string
= "https",
67 protocol_separator
= "://";
69 std::size_t host_offset
;
73 std::size_t separator_offset
= url
.find(protocol_separator
);
74 if(separator_offset
== std::string::npos
)
81 host_offset
= separator_offset
+ protocol_separator
.length();
82 std::string protocol
= url
.substr(0, separator_offset
);
83 if(protocol
== http_string
)
85 else if(protocol
== https_string
)
88 throw ail::exception("Invalid protocol specified for HTTP client");
91 std::size_t path_offset
= url
.find('/', host_offset
);
92 if(path_offset
== std::string::npos
)
94 host
= url
.substr(host_offset
);
99 host
= url
.substr(host_offset
, path_offset
- host_offset
);
100 path
= url
.substr(path_offset
);
103 boost::asio::ip::tcp::resolver::query
query(host
, "http");
104 resolver
.async_resolve(query
, boost::bind(&http_client::dns_event
, this, boost::asio::placeholders::error
, boost::asio::placeholders::iterator
));
108 bool http_client::start_download(std::string
const & new_url
, std::string
const & file_name
)
110 if(!file_output
.open_create(file_name
))
112 start_download(new_url
);
116 void http_client::dns_event(boost::system::error_code
const & error
, boost::asio::ip::tcp::resolver::iterator endpoint_iterator
)
118 //std::cout << "DNS event" << std::endl;
121 boost::asio::ip::tcp::endpoint endpoint
= *endpoint_iterator
;
123 socket
.async_connect(endpoint
, boost::bind(&http_client::connect_event
, this, boost::asio::placeholders::error
, endpoint_iterator
));
127 error_occured(http_error_dns
);
130 void http_client::connect_event(boost::system::error_code
const & error
, boost::asio::ip::tcp::resolver::iterator endpoint_iterator
)
134 std::ostream
request_stream(&request
);
135 request_stream
<< method
<< " " << path
<< " HTTP/1.1\r\n";
138 case http_user_agent_firefox
:
139 request_stream
<< "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 (.NET CLR 3.5.30729)\r\n";
142 request_stream
<< "Host: " << host
<< "\r\n";
143 //request_stream << "Accept: */*\r\n";
144 request_stream
<< "Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1\r\n";
145 request_stream
<< "Accept-Language: en-us,en;q=0.5\r\n";
146 request_stream
<< "Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1\r\n";
147 request_stream
<< "Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0\r\n";
148 //request_stream << "Connection: close\r\n";
149 //request_stream << "Connection: Keep-Alive, TE\r\n";
150 request_stream
<< "Connection: close, TE\r\n";
151 request_stream
<< "TE: ";
153 request_stream
<< "deflate, gzip, ";
154 request_stream
<< "chunked, identity, trailers\r\n";
155 if(!referrer
.empty())
156 request_stream
<< "Referer: " << referrer
<< "\r\n";
159 request_stream
<< "Cookie: ";
161 for(cookie_map::iterator i
= cookies
.begin(), end
= cookies
.end(); i
!= end
; i
++)
166 request_stream
<< "; ";
167 request_stream
<< i
->first
<< "=" << i
->second
;
169 request_stream
<< "\r\n";
172 std::string post_data
;
174 if(!form_data
.empty())
176 std::string
const boundary_tag
= "------------PDq3XENV0Hx17uo1Qpe5qN";
177 for(form_map::iterator i
= form_data
.begin(), end
= form_data
.end(); i
!= end
; i
++)
178 post_data
+= boundary_tag
+ "\r\nContent-Disposition: form-data; name=\"" + i
->first
+ "\"\r\n\r\n" + i
->second
+ "\r\n";
179 post_data
+= boundary_tag
+ "--\r\n";
181 //std::cout << ail::consolify(post_data) << std::endl;
183 request_stream
<< "Content-Length: " << post_data
.size() << "\r\n";
184 request_stream
<< "Content-Type: multipart/form-data; boundary=" << boundary_tag
<< "\r\n";
185 request_stream
<< "\r\n";
186 request_stream
<< post_data
;
189 request_stream
<< "\r\n";
191 boost::asio::async_write(socket
, request
, boost::bind(&http_client::write_event
, this, boost::asio::placeholders::error
));
194 else if(endpoint_iterator
!= boost::asio::ip::tcp::resolver::iterator())
197 boost::asio::ip::tcp::endpoint endpoint
= *endpoint_iterator
;
199 socket
.async_connect(endpoint
, boost::bind(&http_client::connect_event
, this, boost::asio::placeholders::error
, endpoint_iterator
));
204 std::cout
<< "Connect: " << error
.message() << std::endl
;
205 error_occured(http_error_connect
);
209 void http_client::write_event(boost::system::error_code
const & error
)
214 error_occured(http_error_write
);
217 void http_client::read_header_data()
219 boost::asio::async_read(socket
, response
, boost::asio::transfer_at_least(1), boost::bind(&http_client::read_header_event
, this, boost::asio::placeholders::error
));
223 void http_client::read_partial_data()
225 boost::asio::async_read(socket
, response
, boost::asio::transfer_at_least(1), boost::bind(&http_client::partial_read_event
, this, boost::asio::placeholders::error
));
229 void http_client::read_full_data()
231 boost::asio::async_read(socket
, response
, boost::asio::transfer_all(), boost::bind(&http_client::full_read_event
, this, boost::asio::placeholders::error
));
235 void http_client::read_header_event(boost::system::error_code
const & error
)
242 bool download_finished
= error
== boost::asio::error::eof
;
243 if(error
&& !download_finished
)
245 error_occured(http_error_read
);
249 extract_data(response
, buffer
);
251 std::string
const end_of_header_string
= "\r\n\r\n";
253 std::size_t end_of_header
= buffer
.find(end_of_header_string
);
254 if(end_of_header
== std::string::npos
)
256 if(download_finished
)
257 error_occured(http_error_end_of_header
);
262 std::string header
= buffer
.substr(0, end_of_header
);
263 buffer
.erase(0, end_of_header
+ end_of_header_string
.size());
265 string_vector tokens
= tokenise(header
, "\r\n");
268 error_occured(http_error_header_lacks_lines
);
272 //std::cout << "Header: " << header << std::endl;
274 std::string
const & status_line
= tokens
[0];
276 //std::cout << status_line << std::endl;
278 std::string http_code_string
;
279 if(!extract_string(status_line
, " ", " ", http_code_string
))
281 error_occured(http_error_invalid_status_line
);
285 //std::cout << "Code: " << http_code_string << std::endl;
288 if(!string_to_number
<long>(http_code_string
, http_code
))
290 error_occured(http_error_invalid_code_string
);
294 if(http_code
!= ok_code
&& http_code
!= moved_code
&& http_code
!= found_code
)
296 error_occured(http_error_status_code_error
);
297 std::cout
<< buffer
<< std::endl
;
301 encoding
= http_encoding_none
;
305 for(std::size_t i
= 1, end
= tokens
.size(); i
< end
; i
++)
307 string_vector line_tokens
= tokenise(tokens
[i
], ": ");
308 if(line_tokens
.size() != 2)
310 error_occured(http_error_header_token_error
);
313 std::string
const & field
= line_tokens
[0];
314 std::string
const & value
= line_tokens
[1];
315 if(http_code
== ok_code
&& field
== "Content-Encoding")
318 encoding
= http_encoding_gzip
;
319 else if(value
== "deflate")
320 encoding
= http_encoding_deflate
;
322 else if(http_code
== ok_code
&& field
== "Transfer-Encoding")
324 if(value
== "chunked")
327 else if((http_code
== moved_code
|| http_code
== found_code
) && field
== "Location")
333 if(!value
.empty() && value
[0] == '/')
334 new_url
= (use_ssl
? "http" : "https") + std::string("://") + host
+ value
;
337 start_download(new_url
);
342 if(download_finished
)
346 if(encoding
== http_encoding_none
&& has_download_handler
)
348 process_partial_data();
356 void http_client::full_read_event(boost::system::error_code
const & error
)
358 if(error
!= boost::asio::error::eof
)
360 error_occured(http_error_read
);
364 extract_data(response
, buffer
);
368 void http_client::process_partial_data()
370 if(file_output
.open())
371 file_output
.write(buffer
);
373 if(has_download_handler
)
374 download_handler(*this, buffer
);
379 void http_client::partial_read_event(boost::system::error_code
const & error
)
381 bool download_finished
= error
== boost::asio::error::eof
;
382 if(error
&& !download_finished
)
384 error_occured(http_error_read
);
388 extract_data(response
, buffer
);
390 process_partial_data();
392 if(download_finished
)
394 if(file_output
.open())
397 if(has_download_finished_handler
)
398 download_finished_handler(*this, buffer
);
404 void http_client::process_data()
410 * input_pointer
= &buffer
;
414 if(!process_chunked_data(buffer
, dechunked_data
, 0))
416 error_occured(http_error_chunk
);
419 input_pointer
= &dechunked_data
;
422 std::string
& input
= *input_pointer
;
426 case http_encoding_none
:
430 case http_encoding_gzip
:
431 case http_encoding_deflate
:
434 decompress_gzip(input
, content
);
438 error_occured(http_error_compression
);
444 if(has_download_handler
)
445 download_handler(*this, content
);
447 if(has_download_finished_handler
)
448 download_finished_handler(*this, content
);
453 void http_client::error_occured(http_error_type the_error_code
)
455 //std::cout << "http_client Error: " << the_error_code << std::endl;
456 if(has_error_handler
)
457 error_handler(*this, the_error_code
);
460 void http_client::set_error_handler(error_handler_type new_error_handler
)
462 error_handler
= new_error_handler
;
463 has_error_handler
= true;
466 void http_client::set_download_handler(download_handler_type new_download_handler
)
468 download_handler
= new_download_handler
;
469 has_download_handler
= true;
472 void http_client::set_download_finished_handler(download_finished_handler_type new_download_finished_handler
)
474 download_finished_handler
= new_download_finished_handler
;
475 has_download_finished_handler
= true;
478 void http_client::set_referrer(std::string
const & new_referrer
)
480 referrer
= new_referrer
;
483 std::string
http_client::get_referrer() const
488 std::string
http_client::get_url() const