]> git.mxchange.org Git - simgear.git/blob - simgear/io/HTTPClient.cxx
4c06fba344018003b48db234851bb34d555fe9c0
[simgear.git] / simgear / io / HTTPClient.cxx
1 #include "HTTPClient.hxx"
2
3 #include <sstream>
4 #include <cassert>
5 #include <list>
6
7 #include <boost/foreach.hpp>
8 #include <boost/algorithm/string/case_conv.hpp>
9
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
15 #if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
16 #include "version.h"
17 #else
18 #  if !defined(SIMGEAR_VERSION)
19 #    define SIMGEAR_VERSION "development " __DATE__
20 #  endif
21 #endif
22
23 using std::string;
24 using std::stringstream;
25 using std::vector;
26
27 namespace simgear
28 {
29
30 namespace HTTP
31 {
32
33
34 class Connection : public NetChat
35 {
36 public:
37     Connection(Client* pr) :
38         client(pr),
39         state(STATE_IDLE)
40     {
41         setTerminator("\r\n");
42     }
43     
44     void connectToHost(const string& host)
45     {
46         open();
47         
48         int colonPos = host.find(':');
49         if (colonPos > 0) {
50             string h = host.substr(0, colonPos);
51             int port = strutils::to_int(host.substr(colonPos + 1));
52             connect(h.c_str(), port);
53         } else {
54             connect(host.c_str(), 80 /* default port */);
55         }
56     }
57     
58     void queueRequest(const Request_ptr& r)
59     {
60         if (!activeRequest) {
61             startRequest(r);
62         } else {
63             queuedRequests.push_back(r);
64         }
65     }
66     
67     void startRequest(const Request_ptr& r)
68     {
69         activeRequest = r;
70         state = STATE_IDLE;
71         bodyTransferSize = 0;
72         
73         stringstream headerData;
74         string path = r->path();
75         if (!client->proxyHost().empty()) {
76             path = "http://" + r->host() + path;
77         }
78
79         int requestTime = 0;
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";
83
84         if (!client->proxyAuth().empty()) {
85             headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
86         }
87
88         BOOST_FOREACH(string h, r->requestHeaders()) {
89             headerData << h << ": " << r->header(h) << "\r\n";
90         }
91
92         headerData << "\r\n"; // final CRLF to terminate the headers
93
94     // TODO - add request body support for PUT, etc operations
95
96         push(headerData.str().c_str());
97     }
98     
99     virtual void collectIncomingData(const char* s, int n)
100     {
101         if (state == STATE_GETTING_BODY) {
102             activeRequest->gotBodyData(s, n);
103         } else {
104             buffer += string(s, n);
105         }
106     }
107     
108     virtual void foundTerminator(void)
109     {
110         switch (state) {
111         case STATE_IDLE:
112             activeRequest->responseStart(buffer);
113             state = STATE_GETTING_HEADERS;
114             buffer.clear();
115             break;
116             
117         case STATE_GETTING_HEADERS:
118             processHeader();
119             buffer.clear();
120             break;
121             
122         case STATE_GETTING_BODY:
123             responseComplete();
124             state = STATE_IDLE;
125             setTerminator("\r\n");
126             
127             if (!queuedRequests.empty()) {
128                 Request_ptr next = queuedRequests.front();
129                 queuedRequests.pop_front();
130                 startRequest(next);
131             }
132             
133             break;
134         }
135     }
136     
137 private:
138     void processHeader()
139     {
140         string h = strutils::simplify(buffer);
141         if (h.empty()) { // blank line terminates headers
142             headersComplete();
143             
144             if (bodyTransferSize > 0) {
145                 state = STATE_GETTING_BODY;
146                 setByteCount(bodyTransferSize);
147             } else {
148                 responseComplete();
149                 state = STATE_IDLE; // no response body, we're done
150             }
151             return;
152         }
153         
154         int colonPos = buffer.find(':');
155         if (colonPos < 0) {
156             SG_LOG(SG_IO, SG_WARN, "malformed HTTP response header:" << h);
157             return;
158         }
159         
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));
163         
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);
168         }
169         
170         activeRequest->responseHeader(lkey, value);
171     }
172     
173     void headersComplete()
174     {
175         activeRequest->responseHeadersComplete();
176     }
177     
178     void responseComplete()
179     {
180         activeRequest->responseComplete();
181         client->requestFinished(this);
182         activeRequest = NULL;
183     }
184     
185     enum ConnectionState {
186         STATE_IDLE = 0,
187         STATE_GETTING_HEADERS,
188         STATE_GETTING_BODY
189     };
190     
191     Client* client;
192     Request_ptr activeRequest;
193     ConnectionState state;
194     std::string buffer;
195     int bodyTransferSize;
196     
197     std::list<Request_ptr> queuedRequests;
198 };
199
200 Client::Client()
201 {
202     setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
203 }
204
205 void Client::makeRequest(const Request_ptr& r)
206 {
207     string host = r->host();
208     if (!_proxy.empty()) {
209         host = _proxy;
210     }
211     
212     if (_connections.find(host) == _connections.end()) {
213         Connection* con = new Connection(this);
214         con->connectToHost(host);
215         _connections[host] = con;
216     }
217     
218     _connections[host]->queueRequest(r);
219 }
220
221 void Client::requestFinished(Connection* con)
222 {
223     
224 }
225
226 void Client::setUserAgent(const string& ua)
227 {
228     _userAgent = ua;
229 }
230
231 void Client::setProxy(const string& proxy, const string& auth)
232 {
233     _proxy = proxy;
234     _proxyAuth = auth;
235 }
236
237 } // of namespace HTTP
238
239 } // of namespace simgear