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 has_error_handler(false),
39 has_download_handler(false),
40 has_download_finished_handler(false),
41 user_agent(http_user_agent_firefox
),
46 void http_client::use_post()
51 void http_client::add_post_data(std::string
const & field
, std::string
const & value
)
53 form_data
[field
] = value
;
56 void http_client::start_download(std::string
const & new_url
)
60 https_string
= "https",
61 protocol_separator
= "://";
63 std::size_t host_offset
;
67 std::size_t separator_offset
= url
.find(protocol_separator
);
68 if(separator_offset
== std::string::npos
)
75 host_offset
= separator_offset
+ protocol_separator
.length();
76 std::string protocol
= url
.substr(0, separator_offset
);
77 if(protocol
== http_string
)
79 else if(protocol
== https_string
)
82 throw ail::exception("Invalid protocol specified for HTTP client");
85 std::size_t path_offset
= url
.find('/', host_offset
);
86 if(path_offset
== std::string::npos
)
88 host
= url
.substr(host_offset
);
93 host
= url
.substr(host_offset
, path_offset
- host_offset
);
94 path
= url
.substr(path_offset
);
97 boost::asio::ip::tcp::resolver::query
query(host
, "http");
98 resolver
.async_resolve(query
, boost::bind(&http_client::dns_event
, this, boost::asio::placeholders::error
, boost::asio::placeholders::iterator
));
102 bool http_client::start_download(std::string
const & new_url
, std::string
const & file_name
)
104 if(!file_output
.open_create(file_name
))
106 start_download(new_url
);
110 void http_client::dns_event(boost::system::error_code
const & error
, boost::asio::ip::tcp::resolver::iterator endpoint_iterator
)
112 //std::cout << "DNS event" << std::endl;
115 boost::asio::ip::tcp::endpoint endpoint
= *endpoint_iterator
;
117 socket
.async_connect(endpoint
, boost::bind(&http_client::connect_event
, this, boost::asio::placeholders::error
, endpoint_iterator
));
121 error_occured(http_error_dns
);
124 void http_client::connect_event(boost::system::error_code
const & error
, boost::asio::ip::tcp::resolver::iterator endpoint_iterator
)
128 std::ostream
request_stream(&request
);
129 request_stream
<< method
<< " " << path
<< " HTTP/1.1\r\n";
132 case http_user_agent_firefox
:
133 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";
136 request_stream
<< "Host: " << host
<< "\r\n";
137 //request_stream << "Accept: */*\r\n";
138 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";
139 request_stream
<< "Accept-Language: en-us,en;q=0.5\r\n";
140 request_stream
<< "Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1\r\n";
141 request_stream
<< "Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0\r\n";
142 //request_stream << "Connection: close\r\n";
143 //request_stream << "Connection: Keep-Alive, TE\r\n";
144 request_stream
<< "Connection: close, TE\r\n";
145 request_stream
<< "TE: deflate, gzip, chunked, identity, trailers\r\n";
146 if(!referrer
.empty())
147 request_stream
<< "Referer: " << referrer
<< "\r\n";
150 request_stream
<< "Cookie: ";
152 for(cookie_map::iterator i
= cookies
.begin(), end
= cookies
.end(); i
!= end
; i
++)
157 request_stream
<< "; ";
158 request_stream
<< i
->first
<< "=" << i
->second
;
160 request_stream
<< "\r\n";
163 std::string post_data
;
165 if(!form_data
.empty())
167 std::string
const boundary_tag
= "------------PDq3XENV0Hx17uo1Qpe5qN";
168 for(form_map::iterator i
= form_data
.begin(), end
= form_data
.end(); i
!= end
; i
++)
169 post_data
+= boundary_tag
+ "\r\nContent-Disposition: form-data; name=\"" + i
->first
+ "\"\r\n\r\n" + i
->second
+ "\r\n";
170 post_data
+= boundary_tag
+ "--\r\n";
172 std::cout
<< ail::consolify(post_data
) << std::endl
;
174 request_stream
<< "Content-Length: " << post_data
.size() << "\r\n";
175 request_stream
<< "Content-Type: multipart/form-data; boundary=" << boundary_tag
<< "\r\n";
176 request_stream
<< "\r\n";
177 request_stream
<< post_data
;
180 request_stream
<< "\r\n";
182 boost::asio::async_write(socket
, request
, boost::bind(&http_client::write_event
, this, boost::asio::placeholders::error
));
185 else if(endpoint_iterator
!= boost::asio::ip::tcp::resolver::iterator())
188 boost::asio::ip::tcp::endpoint endpoint
= *endpoint_iterator
;
190 socket
.async_connect(endpoint
, boost::bind(&http_client::connect_event
, this, boost::asio::placeholders::error
, endpoint_iterator
));
195 std::cout
<< "Connect: " << error
.message() << std::endl
;
196 error_occured(http_error_connect
);
200 void http_client::write_event(boost::system::error_code
const & error
)
205 error_occured(http_error_write
);
208 void http_client::read_header_data()
210 boost::asio::async_read(socket
, response
, boost::asio::transfer_at_least(1), boost::bind(&http_client::read_header_event
, this, boost::asio::placeholders::error
));
214 void http_client::read_partial_data()
216 boost::asio::async_read(socket
, response
, boost::asio::transfer_at_least(1), boost::bind(&http_client::partial_read_event
, this, boost::asio::placeholders::error
));
220 void http_client::read_full_data()
222 boost::asio::async_read(socket
, response
, boost::asio::transfer_all(), boost::bind(&http_client::full_read_event
, this, boost::asio::placeholders::error
));
226 void http_client::read_header_event(boost::system::error_code
const & error
)
233 bool download_finished
= error
== boost::asio::error::eof
;
234 if(error
&& !download_finished
)
236 error_occured(http_error_read
);
240 extract_data(response
, buffer
);
242 std::string
const end_of_header_string
= "\r\n\r\n";
244 std::size_t end_of_header
= buffer
.find(end_of_header_string
);
245 if(end_of_header
== std::string::npos
)
247 if(download_finished
)
248 error_occured(http_error_end_of_header
);
253 std::string header
= buffer
.substr(0, end_of_header
);
254 buffer
.erase(0, end_of_header
+ end_of_header_string
.size());
256 string_vector tokens
= tokenise(header
, "\r\n");
259 error_occured(http_error_header_lacks_lines
);
263 //std::cout << "Header: " << header << std::endl;
265 std::string
const & status_line
= tokens
[0];
267 //std::cout << status_line << std::endl;
269 std::string http_code_string
;
270 if(!extract_string(status_line
, " ", " ", http_code_string
))
272 error_occured(http_error_invalid_status_line
);
276 //std::cout << "Code: " << http_code_string << std::endl;
279 if(!string_to_number
<long>(http_code_string
, http_code
))
281 error_occured(http_error_invalid_code_string
);
285 if(http_code
!= ok_code
&& http_code
!= moved_code
&& http_code
!= found_code
)
287 error_occured(http_error_status_code_error
);
288 std::cout
<< buffer
<< std::endl
;
292 encoding
= http_encoding_none
;
296 for(std::size_t i
= 1, end
= tokens
.size(); i
< end
; i
++)
298 string_vector line_tokens
= tokenise(tokens
[i
], ": ");
299 if(line_tokens
.size() != 2)
301 error_occured(http_error_header_token_error
);
304 std::string
const & field
= line_tokens
[0];
305 std::string
const & value
= line_tokens
[1];
306 if(http_code
== ok_code
&& field
== "Content-Encoding")
309 encoding
= http_encoding_gzip
;
310 else if(value
== "deflate")
311 encoding
= http_encoding_deflate
;
313 else if(http_code
== ok_code
&& field
== "Transfer-Encoding")
315 if(value
== "chunked")
318 else if((http_code
== moved_code
|| http_code
== found_code
) && field
== "Location")
324 if(!value
.empty() && value
[0] == '/')
325 new_url
= (use_ssl
? "http" : "https") + std::string("://") + host
+ value
;
328 start_download(new_url
);
333 if(download_finished
)
337 if(encoding
== http_encoding_none
&& has_download_handler
)
339 process_partial_data();
347 void http_client::full_read_event(boost::system::error_code
const & error
)
349 if(error
!= boost::asio::error::eof
)
351 error_occured(http_error_read
);
355 extract_data(response
, buffer
);
359 void http_client::process_partial_data()
361 if(file_output
.open())
362 file_output
.write(buffer
);
364 if(has_download_handler
)
365 download_handler(*this, buffer
);
370 void http_client::partial_read_event(boost::system::error_code
const & error
)
372 bool download_finished
= error
== boost::asio::error::eof
;
373 if(error
&& !download_finished
)
375 error_occured(http_error_read
);
379 extract_data(response
, buffer
);
381 process_partial_data();
383 if(download_finished
)
385 if(file_output
.open())
388 if(has_download_finished_handler
)
389 download_finished_handler(*this, buffer
);
395 void http_client::process_data()
401 * input_pointer
= &buffer
;
405 if(!process_chunked_data(buffer
, dechunked_data
, 0))
407 error_occured(http_error_chunk
);
410 input_pointer
= &dechunked_data
;
413 std::string
& input
= *input_pointer
;
417 case http_encoding_none
:
421 case http_encoding_gzip
:
422 case http_encoding_deflate
:
425 decompress_gzip(input
, content
);
429 error_occured(http_error_compression
);
435 if(has_download_handler
)
436 download_handler(*this, content
);
438 if(has_download_finished_handler
)
439 download_finished_handler(*this, content
);
444 void http_client::error_occured(http_error_type the_error_code
)
446 //std::cout << "http_client Error: " << the_error_code << std::endl;
447 if(has_error_handler
)
448 error_handler(*this, the_error_code
);
451 void http_client::set_error_handler(error_handler_type new_error_handler
)
453 error_handler
= new_error_handler
;
454 has_error_handler
= true;
457 void http_client::set_download_handler(download_handler_type new_download_handler
)
459 download_handler
= new_download_handler
;
460 has_download_handler
= true;
463 void http_client::set_download_finished_handler(download_finished_handler_type new_download_finished_handler
)
465 download_finished_handler
= new_download_finished_handler
;
466 has_download_finished_handler
= true;
469 void http_client::set_referrer(std::string
const & new_referrer
)
471 referrer
= new_referrer
;
474 std::string
http_client::get_referrer() const
479 std::string
http_client::get_url() const