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>
14 #include <simgear/timing/timestamp.hxx>
16 #if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
19 # if !defined(SIMGEAR_VERSION)
20 # define SIMGEAR_VERSION "development " __DATE__
25 using std::stringstream;
34 extern const int DEFAULT_HTTP_PORT = 80;
36 class Connection : public NetChat
39 Connection(Client* pr) :
43 setTerminator("\r\n");
46 void connectToHost(const string& host, short port)
49 connect(host.c_str(), 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->hostAndPort() + path;
73 headerData << r->method() << " " << path << " HTTP/1.1 " << client->userAgent() << "\r\n";
74 headerData << "Host: " << r->hostAndPort() << "\r\n";
76 if (!client->proxyAuth().empty()) {
77 headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
80 BOOST_FOREACH(string h, r->requestHeaders()) {
81 headerData << h << ": " << r->header(h) << "\r\n";
84 headerData << "\r\n"; // final CRLF to terminate the headers
86 // TODO - add request body support for PUT, etc operations
88 push(headerData.str().c_str());
91 virtual void collectIncomingData(const char* s, int n)
93 if (state == STATE_GETTING_BODY) {
94 activeRequest->gotBodyData(s, n);
96 buffer += string(s, n);
100 virtual void foundTerminator(void)
104 activeRequest->responseStart(buffer);
105 state = STATE_GETTING_HEADERS;
109 case STATE_GETTING_HEADERS:
114 case STATE_GETTING_BODY:
117 setTerminator("\r\n");
119 if (!queuedRequests.empty()) {
120 Request_ptr next = queuedRequests.front();
121 queuedRequests.pop_front();
131 bool hasIdleTimeout() const
133 if (state != STATE_IDLE) {
137 return idleTime.elapsedMSec() > 1000 * 10; // ten seconds
142 string h = strutils::simplify(buffer);
143 if (h.empty()) { // blank line terminates headers
146 if (bodyTransferSize > 0) {
147 state = STATE_GETTING_BODY;
148 setByteCount(bodyTransferSize);
151 state = STATE_IDLE; // no response body, we're done
156 int colonPos = buffer.find(':');
158 SG_LOG(SG_IO, SG_WARN, "malformed HTTP response header:" << h);
162 string key = strutils::simplify(buffer.substr(0, colonPos));
163 string lkey = boost::to_lower_copy(key);
164 string value = strutils::strip(buffer.substr(colonPos + 1));
166 if (lkey == "content-length" && (bodyTransferSize <= 0)) {
167 bodyTransferSize = strutils::to_int(value);
168 } else if (lkey == "transfer-length") {
169 bodyTransferSize = strutils::to_int(value);
172 activeRequest->responseHeader(lkey, value);
175 void headersComplete()
177 activeRequest->responseHeadersComplete();
180 void responseComplete()
182 activeRequest->responseComplete();
183 client->requestFinished(this);
184 activeRequest = NULL;
187 enum ConnectionState {
189 STATE_GETTING_HEADERS,
194 Request_ptr activeRequest;
195 ConnectionState state;
197 int bodyTransferSize;
198 SGTimeStamp idleTime;
200 std::list<Request_ptr> queuedRequests;
205 setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
208 void Client::update()
210 ConnectionDict::iterator it = _connections.begin();
211 for (; it != _connections.end(); ) {
212 if (it->second->hasIdleTimeout()) {
213 // connection has been idle for a while, clean it up
214 ConnectionDict::iterator del = it++;
216 _connections.erase(del);
220 } // of connecion iteration
223 void Client::makeRequest(const Request_ptr& r)
225 string host = r->hostAndPort();
226 if (!_proxy.empty()) {
230 if (_connections.find(host) == _connections.end()) {
231 Connection* con = new Connection(this);
232 con->connectToHost(r->host(), r->port());
233 _connections[host] = con;
236 _connections[host]->queueRequest(r);
239 void Client::requestFinished(Connection* con)
244 void Client::setUserAgent(const string& ua)
249 void Client::setProxy(const string& proxy, const string& auth)
255 } // of namespace HTTP
257 } // of namespace simgear