OSDN Git Service

3fbc4bf081b55cd24384de4077fd9a64c98c25e7
[fukui-no-namari/dialektos.git] / src / http_client.cxx
1 /*
2  * Copyright (C) 2009 by Aiwota Programmer
3  * aiwotaprog@tetteke.tk
4  *
5  * This file is part of Dialektos.
6  *
7  * Dialektos is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Dialektos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Dialektos.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "http_client.hxx"
22
23 #include <boost/asio.hpp>
24 #include <boost/bind.hpp>
25 #include <boost/algorithm/string.hpp>
26 #include <boost/date_time/posix_time/posix_time.hpp>
27 #include <boost/foreach.hpp>
28 #include <boost/format.hpp>
29 #include <iostream>
30 #include <istream>
31 #include <ostream>
32 #include <string>
33 #include <map>
34 #include "http_response.hxx"
35 #include "http_status_line.hxx"
36
37
38 namespace dialektos {
39
40 namespace http {
41
42
43 AsyncClient::AsyncClient(
44     boost::asio::io_service& io_service, const std::string& uri,
45     const Header& header) :
46       resolver_(io_service),
47       socket_(io_service),
48       request_(),
49       response_(),
50       content_(),
51       http_version_(),
52       status_code_(),
53       status_message_(),
54       response_header_(),
55       err_() {
56
57   const std::string host = header.get_host();
58   assert(!host.empty());
59
60   std::ostream stream(&request_);
61
62   stream << boost::format("GET %1% HTTP/1.0\r\n") % uri;
63
64   typedef std::map<std::string, std::string>::value_type PairType;
65   BOOST_FOREACH(const PairType& pair, header) {
66     stream << boost::format("%1%: %2%\r\n") % pair.first % pair.second;
67   }
68   stream << "\r\n" << std::flush;
69
70   boost::asio::ip::tcp::resolver::query query(host, "http");
71   resolver_.async_resolve(query,
72       boost::bind(&AsyncClient::handle_resolve, this,
73         boost::asio::placeholders::error,
74         boost::asio::placeholders::iterator));
75 }
76
77 void AsyncClient::handle_resolve(const boost::system::error_code& err,
78     boost::asio::ip::tcp::resolver::iterator endpoint_iterator) {
79   if (err) {
80     err_ = err;
81     return;
82   }
83
84   boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
85   socket_.async_connect(endpoint,
86       boost::bind(&AsyncClient::handle_connect, this,
87           boost::asio::placeholders::error, ++endpoint_iterator));
88 }
89
90 void AsyncClient::handle_connect(const boost::system::error_code& err,
91     boost::asio::ip::tcp::resolver::iterator endpoint_iterator) {
92   if (!err) {
93     boost::asio::async_write(socket_, request_,
94         boost::bind(&AsyncClient::handle_write_request, this,
95           boost::asio::placeholders::error));
96   } else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator()) {
97     socket_.close();
98     boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
99     socket_.async_connect(endpoint,
100         boost::bind(&AsyncClient::handle_connect, this,
101           boost::asio::placeholders::error, ++endpoint_iterator));
102   } else {
103     err_= err;
104   }
105 }
106
107 void AsyncClient::handle_write_request(const boost::system::error_code& err) {
108   if (err) {
109     err_ = err;
110     return;
111   }
112
113   boost::asio::async_read_until(socket_, response_, "\r\n",
114       boost::bind(&AsyncClient::handle_read_status_line, this,
115           boost::asio::placeholders::error));
116 }
117
118 void AsyncClient::handle_read_status_line(
119     const boost::system::error_code& err) {
120   if (err) {
121     err_ = err;
122     return;
123   }
124
125   std::istream stream(&response_);
126
127   stream >> http_version_;
128   stream >> status_code_;
129
130   std::string status_message_;
131   std::getline(stream, status_message_);
132
133   if (!stream || !boost::algorithm::starts_with(http_version_, "HTTP/")) {
134     std::cerr << "response is not HTTP" << std::endl;
135     return;
136   }
137
138   boost::asio::async_read_until(socket_, response_, "\r\n\r\n",
139       boost::bind(&AsyncClient::handle_read_headers, this,
140           boost::asio::placeholders::error));
141 }
142
143 void AsyncClient::handle_read_headers(const boost::system::error_code& err) {
144   if (err) {
145     err_ = err;
146     return;
147   }
148
149   std::istream stream(&response_);
150   std::string line;
151   while (std::getline(stream, line)) {
152     boost::algorithm::trim_right(line);
153     response_header_.set_header_from_line(line);
154   }
155
156   if (response_.size() > 0)
157     content_ << &response_ << std::flush;
158
159   boost::asio::async_read(socket_, response_,
160       boost::asio::transfer_at_least(1),
161       boost::bind(&AsyncClient::handle_read_content, this,
162           boost::asio::placeholders::error));
163 }
164
165 void AsyncClient::handle_read_content(const boost::system::error_code& err)
166 {
167   if (!err) {
168     content_ << &response_ << std::flush;
169
170     boost::asio::async_read(socket_, response_,
171         boost::asio::transfer_at_least(1),
172         boost::bind(&AsyncClient::handle_read_content, this,
173           boost::asio::placeholders::error));
174   } else if (err != boost::asio::error::eof) {
175     err_ = err;
176   }
177 }
178
179 Response AsyncClient::get_response() const {
180   std::stringstream ss;
181   ss << boost::format("%1% %2% %3%")
182     % http_version_ % status_code_ % status_message_;
183   std::string status_line = ss.str();
184
185   StatusLine status(http_version_, status_code_, status_message_, status_line);
186   return Response(status, response_header_, content_.str());
187 }
188
189
190 } // namespace http
191
192 } // namespace dialektos