7 #include <boost/algorithm/string/case_conv.hpp>
9 #include "HTTPClient.hxx"
10 #include "HTTPRequest.hxx"
12 #include <simgear/io/sg_netChat.hxx>
13 #include <simgear/misc/strutils.hxx>
14 #include <simgear/timing/timestamp.hxx>
20 using std::stringstream;
22 using namespace simgear;
24 const char* BODY1 = "The quick brown fox jumps over a lazy dog.";
26 const unsigned int body2Size = 8 * 1024;
27 char body2[body2Size];
29 #define COMPARE(a, b) \
31 cerr << "failed:" << #a << " != " << #b << endl; \
32 cerr << "\tgot:" << a << endl; \
38 cerr << "failed:" << #a << endl; \
42 class TestRequest : public HTTP::Request
49 TestRequest(const std::string& url) :
56 std::map<string, string> headers;
58 virtual void responseHeadersComplete()
62 virtual void responseComplete()
67 virtual void failure()
72 virtual void gotBodyData(const char* s, int n)
74 bodyData += string(s, n);
77 virtual void responseHeader(const string& header, const string& value)
79 headers[header] = value;
83 class TestServerChannel : public NetChat
96 setTerminator("\r\n");
99 virtual void collectIncomingData(const char* s, int n)
101 buffer += string(s, n);
104 virtual void foundTerminator(void)
106 if (state == STATE_IDLE) {
107 state = STATE_HEADERS;
108 string_list line = strutils::split(buffer, NULL, 3);
109 if (line.size() < 3) {
110 cerr << "malformed request:" << buffer << endl;
116 httpVersion = line[2];
117 requestHeaders.clear();
119 } else if (state == STATE_HEADERS) {
120 string s = strutils::simplify(buffer);
123 receivedRequestHeaders();
127 int colonPos = buffer.find(':');
129 cerr << "malformed HTTP response header:" << buffer << endl;
134 string key = strutils::simplify(buffer.substr(0, colonPos));
135 string lkey = boost::to_lower_copy(key);
136 string value = strutils::strip(buffer.substr(colonPos + 1));
137 requestHeaders[lkey] = value;
139 } else if (state == STATE_REQUEST_BODY) {
144 void receivedRequestHeaders()
147 if (path == "/test1") {
148 string contentStr(BODY1);
150 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
151 d << "Content-Length:" << contentStr.size() << "\r\n";
152 d << "\r\n"; // final CRLF to terminate the headers
154 push(d.str().c_str());
155 } else if (path == "/test2") {
157 } else if (path == "/testchunked") {
159 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
160 d << "Transfer-Encoding:chunked\r\n";
162 d << "8\r\n"; // first chunk
164 d << "6\r\n"; // second chunk
166 d << "10\r\n"; // third chunk
167 d << "ABCDSTUVABCDSTUV\r\n";
168 d << "0\r\n"; // start of trailer
169 d << "X-Foobar: wibble\r\n"; // trailer data
171 push(d.str().c_str());
172 } else if (path == "http://www.google.com/test2") {
174 if (requestHeaders["host"] != "www.google.com") {
175 sendErrorResponse(400, true, "bad destination");
178 if (requestHeaders["proxy-authorization"] != string()) {
179 sendErrorResponse(401, false, "bad auth"); // shouldn't supply auth
183 } else if (path == "http://www.google.com/test3") {
185 if (requestHeaders["host"] != "www.google.com") {
186 sendErrorResponse(400, true, "bad destination");
189 if (requestHeaders["proxy-authorization"] != "ABCDEF") {
190 sendErrorResponse(401, false, "bad auth"); // forbidden
194 } else if (path == "/test_1_0") {
195 string contentStr(BODY1);
197 d << "HTTP/1.0 " << 200 << " " << reasonForCode(200) << "\r\n";
198 d << "\r\n"; // final CRLF to terminate the headers
200 push(d.str().c_str());
202 } else if (path == "/test_close") {
203 string contentStr(BODY1);
205 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
206 d << "Connection: close\r\n";
207 d << "\r\n"; // final CRLF to terminate the headers
209 push(d.str().c_str());
212 sendErrorResponse(404, true, "");
219 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
220 d << "Content-Length:" << body2Size << "\r\n";
221 d << "\r\n"; // final CRLF to terminate the headers
222 push(d.str().c_str());
223 bufferSend(body2, body2Size);
226 void sendErrorResponse(int code, bool close, string content)
228 cerr << "sending error " << code << " for " << path << endl;
229 stringstream headerData;
230 headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n";
231 headerData << "Content-Length:" << content.size() << "\r\n";
232 headerData << "\r\n"; // final CRLF to terminate the headers
233 push(headerData.str().c_str());
234 push(content.c_str());
241 string reasonForCode(int code)
244 case 200: return "OK";
245 case 404: return "not found";
246 default: return "unknown code";
255 std::map<string, string> requestHeaders;
258 class TestServer : public NetChannel
264 bind(NULL, 2000); // localhost, any port
268 virtual ~TestServer()
272 virtual bool writable (void) { return false ; }
274 virtual void handleAccept (void)
276 simgear::IPAddress addr ;
277 int handle = accept ( &addr ) ;
278 //cout << "did accept from " << addr.getHost() << ":" << addr.getPort() << endl;
279 TestServerChannel* chan = new TestServerChannel();
280 chan->setHandle(handle);
284 void waitForComplete(TestRequest* tr)
286 SGTimeStamp start(SGTimeStamp::now());
287 while (start.elapsedMSec() < 1000) {
288 NetChannel::poll(10);
294 cerr << "timed out" << endl;
297 void waitForFailed(TestRequest* tr)
299 SGTimeStamp start(SGTimeStamp::now());
300 while (start.elapsedMSec() < 1000) {
301 NetChannel::poll(10);
307 cerr << "timed out waiting for failure" << endl;
310 int main(int argc, char* argv[])
317 TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
318 COMPARE(tr1->scheme(), "http");
319 COMPARE(tr1->hostAndPort(), "localhost.woo.zar:2000");
320 COMPARE(tr1->host(), "localhost.woo.zar");
321 COMPARE(tr1->port(), 2000);
322 COMPARE(tr1->path(), "/test1");
324 TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
325 COMPARE(tr2->scheme(), "http");
326 COMPARE(tr2->hostAndPort(), "192.168.1.1");
327 COMPARE(tr2->host(), "192.168.1.1");
328 COMPARE(tr2->port(), 80);
329 COMPARE(tr2->path(), "/test1/dir/thing/file.png");
333 TestRequest* tr = new TestRequest("http://localhost:2000/test1");
334 HTTP::Request_ptr own(tr);
338 COMPARE(tr->responseCode(), 200);
339 COMPARE(tr->responseReason(), string("OK"));
340 COMPARE(tr->responseLength(), strlen(BODY1));
341 COMPARE(tr->responseBytesReceived(), strlen(BODY1));
342 COMPARE(tr->bodyData, string(BODY1));
345 // larger get request
346 for (unsigned int i=0; i<body2Size; ++i) {
347 body2[i] = (i << 4) | (i >> 2);
351 TestRequest* tr = new TestRequest("http://localhost:2000/test2");
352 HTTP::Request_ptr own(tr);
355 COMPARE(tr->responseCode(), 200);
356 COMPARE(tr->responseBytesReceived(), body2Size);
357 COMPARE(tr->bodyData, string(body2, body2Size));
361 TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
362 HTTP::Request_ptr own(tr);
366 COMPARE(tr->responseCode(), 200);
367 COMPARE(tr->responseReason(), string("OK"));
368 COMPARE(tr->responseBytesReceived(), 30);
369 COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
370 // check trailers made it too
371 COMPARE(tr->headers["x-foobar"], string("wibble"));
376 TestRequest* tr = new TestRequest("http://localhost:2000/not-found");
377 HTTP::Request_ptr own(tr);
380 COMPARE(tr->responseCode(), 404);
381 COMPARE(tr->responseReason(), string("not found"));
382 COMPARE(tr->responseLength(), 0);
385 cout << "done1" << endl;
388 TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0");
389 HTTP::Request_ptr own(tr);
392 COMPARE(tr->responseCode(), 200);
393 COMPARE(tr->responseLength(), strlen(BODY1));
394 COMPARE(tr->bodyData, string(BODY1));
397 cout << "done2" << endl;
398 // test HTTP/1.1 Connection::close
400 TestRequest* tr = new TestRequest("http://localhost:2000/test_close");
401 HTTP::Request_ptr own(tr);
404 COMPARE(tr->responseCode(), 200);
405 COMPARE(tr->responseLength(), strlen(BODY1));
406 COMPARE(tr->bodyData, string(BODY1));
408 cout << "done3" << endl;
409 // test connectToHost failure
412 TestRequest* tr = new TestRequest("http://not.found/something");
413 HTTP::Request_ptr own(tr);
416 COMPARE(tr->responseCode(), -1);
421 cl.setProxy("localhost", 2000);
422 TestRequest* tr = new TestRequest("http://www.google.com/test2");
423 HTTP::Request_ptr own(tr);
426 COMPARE(tr->responseCode(), 200);
427 COMPARE(tr->responseLength(), body2Size);
428 COMPARE(tr->bodyData, string(body2, body2Size));
432 cl.setProxy("localhost", 2000, "ABCDEF");
433 TestRequest* tr = new TestRequest("http://www.google.com/test3");
434 HTTP::Request_ptr own(tr);
437 COMPARE(tr->responseCode(), 200);
438 COMPARE(tr->responseBytesReceived(), body2Size);
439 COMPARE(tr->bodyData, string(body2, body2Size));
442 cout << "all tests passed ok" << endl;