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>
18 using std::stringstream;
28 class Connection : public NetChat
31 Connection(Client* pr) :
35 setTerminator("\r\n");
38 void connectToHost(const string& host)
42 int colonPos = host.find(':');
44 string h = host.substr(0, colonPos);
45 int port = strutils::to_int(host.substr(colonPos + 1));
46 connect(h.c_str(), port);
48 connect(host.c_str(), 80 /* default port */);
52 void queueRequest(const Request_ptr& r)
57 queuedRequests.push_back(r);
61 void startRequest(const Request_ptr& r)
67 stringstream headerData;
68 string path = r->path();
69 if (!client->proxyHost().empty()) {
70 path = "http://" + r->host() + path;
74 headerData << r->method() << " " << path << " HTTP/1.1 " << client->userAgent() << "\r\n";
75 headerData << "Host: " << r->host() << "\r\n";
76 headerData << "X-Time: " << requestTime << "\r\n";
78 if (!client->proxyAuth().empty()) {
79 headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
82 BOOST_FOREACH(string h, r->requestHeaders()) {
83 headerData << h << ": " << r->header(h) << "\r\n";
86 headerData << "\r\n"; // final CRLF to terminate the headers
88 // TODO - add request body support for PUT, etc operations
90 push(headerData.str().c_str());
93 virtual void collectIncomingData(const char* s, int n)
95 if (state == STATE_GETTING_BODY) {
96 activeRequest->gotBodyData(s, n);
98 buffer += string(s, n);
102 virtual void foundTerminator(void)
106 activeRequest->responseStart(buffer);
107 state = STATE_GETTING_HEADERS;
111 case STATE_GETTING_HEADERS:
116 case STATE_GETTING_BODY:
119 setTerminator("\r\n");
121 if (!queuedRequests.empty()) {
122 Request_ptr next = queuedRequests.front();
123 queuedRequests.pop_front();
134 string h = strutils::simplify(buffer);
135 if (h.empty()) { // blank line terminates headers
138 if (bodyTransferSize > 0) {
139 state = STATE_GETTING_BODY;
140 setByteCount(bodyTransferSize);
143 state = STATE_IDLE; // no response body, we're done
148 int colonPos = buffer.find(':');
150 SG_LOG(SG_IO, SG_WARN, "malformed HTTP response header:" << h);
154 string key = strutils::simplify(buffer.substr(0, colonPos));
155 string lkey = boost::to_lower_copy(key);
156 string value = strutils::strip(buffer.substr(colonPos + 1));
158 if (lkey == "content-length" && (bodyTransferSize <= 0)) {
159 bodyTransferSize = strutils::to_int(value);
160 } else if (lkey == "transfer-length") {
161 bodyTransferSize = strutils::to_int(value);
164 activeRequest->responseHeader(lkey, value);
167 void headersComplete()
169 activeRequest->responseHeadersComplete();
172 void responseComplete()
174 activeRequest->responseComplete();
175 client->requestFinished(this);
176 activeRequest = NULL;
179 enum ConnectionState {
181 STATE_GETTING_HEADERS,
186 Request_ptr activeRequest;
187 ConnectionState state;
189 int bodyTransferSize;
191 std::list<Request_ptr> queuedRequests;
196 setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
199 void Client::makeRequest(const Request_ptr& r)
201 string host = r->host();
202 if (!_proxy.empty()) {
206 if (_connections.find(host) == _connections.end()) {
207 Connection* con = new Connection(this);
208 con->connectToHost(host);
209 _connections[host] = con;
212 _connections[host]->queueRequest(r);
215 void Client::requestFinished(Connection* con)
220 void Client::setUserAgent(const string& ua)
225 void Client::setProxy(const string& proxy, const string& auth)
231 } // of namespace HTTP
233 } // of namespace simgear