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