]> git.mxchange.org Git - simgear.git/blob - simgear/io/HTTPClient.cxx
Fix Linux compilation of netChat - explicit include of malloc.h required.
[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 #include <simgear/version.h>
15
16 using std::string;
17 using std::stringstream;
18 using std::vector;
19
20 #include <iostream>
21
22 using std::cout;
23 using std::cerr;
24 using std::endl;
25
26 namespace simgear
27 {
28
29 namespace HTTP
30 {
31
32
33 class Connection : public NetChat
34 {
35 public:
36     Connection(Client* pr) :
37         client(pr),
38         state(STATE_IDLE)
39     {
40         setTerminator("\r\n");
41     }
42     
43     void connectToHost(const string& host)
44     {
45         open();
46         
47         int colonPos = host.find(':');
48         if (colonPos > 0) {
49             string h = host.substr(0, colonPos);
50             int port = strutils::to_int(host.substr(colonPos + 1));
51             connect(h.c_str(), port);
52         } else {
53             connect(host.c_str(), 80 /* default port */);
54         }
55     }
56     
57     void queueRequest(const Request_ptr& r)
58     {
59         if (!activeRequest) {
60             startRequest(r);
61         } else {
62             queuedRequests.push_back(r);
63         }
64     }
65     
66     void startRequest(const Request_ptr& r)
67     {
68         activeRequest = r;
69         state = STATE_IDLE;
70         bodyTransferSize = 0;
71         
72         stringstream headerData;
73         string path = r->path();
74         if (!client->proxyHost().empty()) {
75             path = "http://" + r->host() + path;
76         }
77
78         int requestTime;
79         headerData << r->method() << " " << path << " HTTP/1.1 " << client->userAgent() << "\r\n";
80         headerData << "Host: " << r->host() << "\r\n";
81         headerData << "X-Time: " << requestTime << "\r\n";
82
83         if (!client->proxyAuth().empty()) {
84             headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
85         }
86
87         BOOST_FOREACH(string h, r->requestHeaders()) {
88             headerData << h << ": " << r->header(h) << "\r\n";
89         }
90
91         headerData << "\r\n"; // final CRLF to terminate the headers
92
93     // TODO - add request body support for PUT, etc operations
94
95         push(headerData.str().c_str());
96         cout << "sent request" << endl;
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                 cout << "getting body:" << bodyTransferSize << endl;
147                 setByteCount(bodyTransferSize);
148             } else {
149                 responseComplete();
150                 state = STATE_IDLE; // no response body, we're done
151             }
152             return;
153         }
154         
155         int colonPos = buffer.find(':');
156         if (colonPos < 0) {
157             SG_LOG(SG_IO, SG_WARN, "malformed HTTP response header:" << h);
158             return;
159         }
160         
161         string key = strutils::simplify(buffer.substr(0, colonPos));
162         string lkey = boost::to_lower_copy(key);
163         string value = strutils::strip(buffer.substr(colonPos + 1));
164         
165         if (lkey == "content-length" && (bodyTransferSize <= 0)) {
166             bodyTransferSize = strutils::to_int(value);
167         } else if (lkey == "transfer-length") {
168             bodyTransferSize = strutils::to_int(value);
169         }
170         
171         activeRequest->responseHeader(lkey, value);
172     }
173     
174     void headersComplete()
175     {
176         activeRequest->responseHeadersComplete();
177     }
178     
179     void responseComplete()
180     {
181         activeRequest->responseComplete();
182         client->requestFinished(this);
183         activeRequest = NULL;
184     }
185     
186     enum ConnectionState {
187         STATE_IDLE = 0,
188         STATE_GETTING_HEADERS,
189         STATE_GETTING_BODY
190     };
191     
192     Client* client;
193     Request_ptr activeRequest;
194     ConnectionState state;
195     std::string buffer;
196     int bodyTransferSize;
197     
198     std::list<Request_ptr> queuedRequests;
199 };
200
201 Client::Client()
202 {
203     setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
204 }
205
206 void Client::makeRequest(const Request_ptr& r)
207 {
208     string host = r->host();
209     if (!_proxy.empty()) {
210         host = _proxy;
211     }
212     
213     if (_connections.find(host) == _connections.end()) {
214         Connection* con = new Connection(this);
215         con->connectToHost(host);
216         _connections[host] = con;
217     }
218     
219     _connections[host]->queueRequest(r);
220 }
221
222 void Client::requestFinished(Connection* con)
223 {
224     
225 }
226
227 void Client::setUserAgent(const string& ua)
228 {
229     _userAgent = ua;
230 }
231
232 void Client::setProxy(const string& proxy, const string& auth)
233 {
234     _proxy = proxy;
235     _proxyAuth = auth;
236 }
237
238 } // of namespace HTTP
239
240 } // of namespace simgear