1 #include "HTTPClient.hxx"
7 #include <boost/foreach.hpp>
8 #include <boost/algorithm/string/case_conv.hpp>
10 #include <simgear/io/sg_netChat.hxx>
11 #include <simgear/misc/strutils.hxx>
12 #include <simgear/compiler.h>
13 #include <simgear/debug/logstream.hxx>
15 #if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
18 # if !defined(SIMGEAR_VERSION)
19 # define SIMGEAR_VERSION "development " __DATE__
24 using std::stringstream;
34 class Connection : public NetChat
37 Connection(Client* pr) :
41 setTerminator("\r\n");
44 void connectToHost(const string& host)
48 int colonPos = host.find(':');
50 string h = host.substr(0, colonPos);
51 int port = strutils::to_int(host.substr(colonPos + 1));
52 connect(h.c_str(), port);
54 connect(host.c_str(), 80 /* default port */);
58 void queueRequest(const Request_ptr& r)
63 queuedRequests.push_back(r);
67 void startRequest(const Request_ptr& r)
73 stringstream headerData;
74 string path = r->path();
75 if (!client->proxyHost().empty()) {
76 path = "http://" + r->host() + path;
80 headerData << r->method() << " " << path << " HTTP/1.1 " << client->userAgent() << "\r\n";
81 headerData << "Host: " << r->host() << "\r\n";
82 headerData << "X-Time: " << requestTime << "\r\n";
84 if (!client->proxyAuth().empty()) {
85 headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
88 BOOST_FOREACH(string h, r->requestHeaders()) {
89 headerData << h << ": " << r->header(h) << "\r\n";
92 headerData << "\r\n"; // final CRLF to terminate the headers
94 // TODO - add request body support for PUT, etc operations
96 push(headerData.str().c_str());
99 virtual void collectIncomingData(const char* s, int n)
101 if (state == STATE_GETTING_BODY) {
102 activeRequest->gotBodyData(s, n);
104 buffer += string(s, n);
108 virtual void foundTerminator(void)
112 activeRequest->responseStart(buffer);
113 state = STATE_GETTING_HEADERS;
117 case STATE_GETTING_HEADERS:
122 case STATE_GETTING_BODY:
125 setTerminator("\r\n");
127 if (!queuedRequests.empty()) {
128 Request_ptr next = queuedRequests.front();
129 queuedRequests.pop_front();
140 string h = strutils::simplify(buffer);
141 if (h.empty()) { // blank line terminates headers
144 if (bodyTransferSize > 0) {
145 state = STATE_GETTING_BODY;
146 setByteCount(bodyTransferSize);
149 state = STATE_IDLE; // no response body, we're done
154 int colonPos = buffer.find(':');
156 SG_LOG(SG_IO, SG_WARN, "malformed HTTP response header:" << h);
160 string key = strutils::simplify(buffer.substr(0, colonPos));
161 string lkey = boost::to_lower_copy(key);
162 string value = strutils::strip(buffer.substr(colonPos + 1));
164 if (lkey == "content-length" && (bodyTransferSize <= 0)) {
165 bodyTransferSize = strutils::to_int(value);
166 } else if (lkey == "transfer-length") {
167 bodyTransferSize = strutils::to_int(value);
170 activeRequest->responseHeader(lkey, value);
173 void headersComplete()
175 activeRequest->responseHeadersComplete();
178 void responseComplete()
180 activeRequest->responseComplete();
181 client->requestFinished(this);
182 activeRequest = NULL;
185 enum ConnectionState {
187 STATE_GETTING_HEADERS,
192 Request_ptr activeRequest;
193 ConnectionState state;
195 int bodyTransferSize;
197 std::list<Request_ptr> queuedRequests;
202 setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
205 void Client::makeRequest(const Request_ptr& r)
207 string host = r->host();
208 if (!_proxy.empty()) {
212 if (_connections.find(host) == _connections.end()) {
213 Connection* con = new Connection(this);
214 con->connectToHost(host);
215 _connections[host] = con;
218 _connections[host]->queueRequest(r);
221 void Client::requestFinished(Connection* con)
226 void Client::setUserAgent(const string& ua)
231 void Client::setProxy(const string& proxy, const string& auth)
237 } // of namespace HTTP
239 } // of namespace simgear