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) :
55 std::map<string, string> sendHeaders;
56 std::map<string, string> headers;
58 string_list requestHeaders() const
61 std::map<string, string>::const_iterator it;
62 for (it = sendHeaders.begin(); it != sendHeaders.end(); ++it) {
63 r.push_back(it->first);
68 string header(const string& name) const
70 std::map<string, string>::const_iterator it = sendHeaders.find(name);
71 if (it == sendHeaders.end()) {
78 virtual void responseHeadersComplete()
82 virtual void responseComplete()
87 virtual void failure()
92 virtual void gotBodyData(const char* s, int n)
94 bodyData += string(s, n);
97 virtual void responseHeader(const string& header, const string& value)
99 headers[header] = value;
103 class TestServerChannel : public NetChat
116 setTerminator("\r\n");
119 virtual void collectIncomingData(const char* s, int n)
121 buffer += string(s, n);
124 virtual void foundTerminator(void)
126 if (state == STATE_IDLE) {
127 state = STATE_HEADERS;
128 string_list line = strutils::split(buffer, NULL, 3);
129 if (line.size() < 3) {
130 cerr << "malformed request:" << buffer << endl;
136 httpVersion = line[2];
137 requestHeaders.clear();
139 } else if (state == STATE_HEADERS) {
140 string s = strutils::simplify(buffer);
143 receivedRequestHeaders();
147 int colonPos = buffer.find(':');
149 cerr << "malformed HTTP response header:" << buffer << endl;
154 string key = strutils::simplify(buffer.substr(0, colonPos));
155 string value = strutils::strip(buffer.substr(colonPos + 1));
156 requestHeaders[key] = value;
158 } else if (state == STATE_REQUEST_BODY) {
163 void receivedRequestHeaders()
167 if (path == "/test1") {
168 string contentStr(BODY1);
170 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
171 d << "Content-Length:" << contentStr.size() << "\r\n";
172 d << "\r\n"; // final CRLF to terminate the headers
174 push(d.str().c_str());
175 } else if (path == "/test_headers") {
176 COMPARE(requestHeaders["X-Foo"], string("Bar"));
177 COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
179 string contentStr(BODY1);
181 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
182 d << "Content-Length:" << contentStr.size() << "\r\n";
183 d << "\r\n"; // final CRLF to terminate the headers
185 push(d.str().c_str());
186 } else if (path == "/test2") {
188 } else if (path == "/testchunked") {
190 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
191 d << "Transfer-Encoding:chunked\r\n";
193 d << "8\r\n"; // first chunk
195 d << "6\r\n"; // second chunk
197 d << "10\r\n"; // third chunk
198 d << "ABCDSTUVABCDSTUV\r\n";
199 d << "0\r\n"; // start of trailer
200 d << "X-Foobar: wibble\r\n"; // trailer data
202 push(d.str().c_str());
203 } else if (path == "http://www.google.com/test2") {
205 if (requestHeaders["Host"] != "www.google.com") {
206 sendErrorResponse(400, true, "bad destination");
209 if (requestHeaders["Proxy-Authorization"] != string()) {
210 sendErrorResponse(401, false, "bad auth"); // shouldn't supply auth
214 } else if (path == "http://www.google.com/test3") {
216 if (requestHeaders["Host"] != "www.google.com") {
217 sendErrorResponse(400, true, "bad destination");
220 if (requestHeaders["Proxy-Authorization"] != "ABCDEF") {
221 sendErrorResponse(401, false, "bad auth"); // forbidden
225 } else if (strutils::starts_with(path, "/test_1_0")) {
226 string contentStr(BODY1);
228 d << "HTTP/1.0 " << 200 << " " << reasonForCode(200) << "\r\n";
229 d << "\r\n"; // final CRLF to terminate the headers
231 push(d.str().c_str());
233 } else if (path == "/test_close") {
234 string contentStr(BODY1);
236 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
237 d << "Connection: close\r\n";
238 d << "\r\n"; // final CRLF to terminate the headers
240 push(d.str().c_str());
243 sendErrorResponse(404, true, "");
250 d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
251 d << "Content-Length:" << body2Size << "\r\n";
252 d << "\r\n"; // final CRLF to terminate the headers
253 push(d.str().c_str());
254 bufferSend(body2, body2Size);
257 void sendErrorResponse(int code, bool close, string content)
259 cerr << "sending error " << code << " for " << path << endl;
260 stringstream headerData;
261 headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n";
262 headerData << "Content-Length:" << content.size() << "\r\n";
263 headerData << "\r\n"; // final CRLF to terminate the headers
264 push(headerData.str().c_str());
265 push(content.c_str());
272 string reasonForCode(int code)
275 case 200: return "OK";
276 case 404: return "not found";
277 default: return "unknown code";
286 std::map<string, string> requestHeaders;
289 class TestServer : public NetChannel
295 bind(NULL, 2000); // localhost, any port
299 virtual ~TestServer()
303 virtual bool writable (void) { return false ; }
305 virtual void handleAccept (void)
307 simgear::IPAddress addr ;
308 int handle = accept ( &addr ) ;
309 //cout << "did accept from " << addr.getHost() << ":" << addr.getPort() << endl;
310 TestServerChannel* chan = new TestServerChannel();
311 chan->setHandle(handle);
315 void waitForComplete(HTTP::Client* cl, TestRequest* tr)
317 SGTimeStamp start(SGTimeStamp::now());
318 while (start.elapsedMSec() < 1000) {
323 SGTimeStamp::sleepForMSec(1);
326 cerr << "timed out" << endl;
329 void waitForFailed(HTTP::Client* cl, TestRequest* tr)
331 SGTimeStamp start(SGTimeStamp::now());
332 while (start.elapsedMSec() < 1000) {
337 SGTimeStamp::sleepForMSec(1);
340 cerr << "timed out waiting for failure" << endl;
343 int main(int argc, char* argv[])
350 TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
351 COMPARE(tr1->scheme(), "http");
352 COMPARE(tr1->hostAndPort(), "localhost.woo.zar:2000");
353 COMPARE(tr1->host(), "localhost.woo.zar");
354 COMPARE(tr1->port(), 2000);
355 COMPARE(tr1->path(), "/test1");
357 TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
358 COMPARE(tr2->scheme(), "http");
359 COMPARE(tr2->hostAndPort(), "192.168.1.1");
360 COMPARE(tr2->host(), "192.168.1.1");
361 COMPARE(tr2->port(), 80);
362 COMPARE(tr2->path(), "/test1/dir/thing/file.png");
366 TestRequest* tr = new TestRequest("http://localhost:2000/test1");
367 HTTP::Request_ptr own(tr);
370 waitForComplete(&cl, tr);
371 COMPARE(tr->responseCode(), 200);
372 COMPARE(tr->responseReason(), string("OK"));
373 COMPARE(tr->responseLength(), strlen(BODY1));
374 COMPARE(tr->responseBytesReceived(), strlen(BODY1));
375 COMPARE(tr->bodyData, string(BODY1));
379 TestRequest* tr = new TestRequest("http://localhost:2000/test_headers");
380 HTTP::Request_ptr own(tr);
381 tr->sendHeaders["X-Foo"] = "Bar";
382 tr->sendHeaders["X-AnotherHeader"] = "A longer value";
385 waitForComplete(&cl, tr);
386 COMPARE(tr->responseCode(), 200);
387 COMPARE(tr->responseReason(), string("OK"));
388 COMPARE(tr->responseLength(), strlen(BODY1));
389 COMPARE(tr->responseBytesReceived(), strlen(BODY1));
390 COMPARE(tr->bodyData, string(BODY1));
393 // larger get request
394 for (unsigned int i=0; i<body2Size; ++i) {
395 body2[i] = (i << 4) | (i >> 2);
399 TestRequest* tr = new TestRequest("http://localhost:2000/test2");
400 HTTP::Request_ptr own(tr);
402 waitForComplete(&cl, tr);
403 COMPARE(tr->responseCode(), 200);
404 COMPARE(tr->responseBytesReceived(), body2Size);
405 COMPARE(tr->bodyData, string(body2, body2Size));
409 TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
410 HTTP::Request_ptr own(tr);
413 waitForComplete(&cl, tr);
414 COMPARE(tr->responseCode(), 200);
415 COMPARE(tr->responseReason(), string("OK"));
416 COMPARE(tr->responseBytesReceived(), 30);
417 COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
418 // check trailers made it too
419 COMPARE(tr->headers["x-foobar"], string("wibble"));
424 TestRequest* tr = new TestRequest("http://localhost:2000/not-found");
425 HTTP::Request_ptr own(tr);
427 waitForComplete(&cl, tr);
428 COMPARE(tr->responseCode(), 404);
429 COMPARE(tr->responseReason(), string("not found"));
430 COMPARE(tr->responseLength(), 0);
433 cout << "done1" << endl;
436 TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0");
437 HTTP::Request_ptr own(tr);
439 waitForComplete(&cl, tr);
440 COMPARE(tr->responseCode(), 200);
441 COMPARE(tr->responseLength(), strlen(BODY1));
442 COMPARE(tr->bodyData, string(BODY1));
445 cout << "done2" << endl;
446 // test HTTP/1.1 Connection::close
448 TestRequest* tr = new TestRequest("http://localhost:2000/test_close");
449 HTTP::Request_ptr own(tr);
451 waitForComplete(&cl, tr);
452 COMPARE(tr->responseCode(), 200);
453 COMPARE(tr->responseLength(), strlen(BODY1));
454 COMPARE(tr->bodyData, string(BODY1));
456 cout << "done3" << endl;
457 // test connectToHost failure
460 TestRequest* tr = new TestRequest("http://not.found/something");
461 HTTP::Request_ptr own(tr);
464 COMPARE(tr->responseCode(), -1);
469 cl.setProxy("localhost", 2000);
470 TestRequest* tr = new TestRequest("http://www.google.com/test2");
471 HTTP::Request_ptr own(tr);
473 waitForComplete(&cl, tr);
474 COMPARE(tr->responseCode(), 200);
475 COMPARE(tr->responseLength(), body2Size);
476 COMPARE(tr->bodyData, string(body2, body2Size));
480 cl.setProxy("localhost", 2000, "ABCDEF");
481 TestRequest* tr = new TestRequest("http://www.google.com/test3");
482 HTTP::Request_ptr own(tr);
484 waitForComplete(&cl, tr);
485 COMPARE(tr->responseCode(), 200);
486 COMPARE(tr->responseBytesReceived(), body2Size);
487 COMPARE(tr->bodyData, string(body2, body2Size));
493 TestRequest* tr = new TestRequest("http://localhost:2000/test1");
494 HTTP::Request_ptr own(tr);
498 TestRequest* tr2 = new TestRequest("http://localhost:2000/test1");
499 HTTP::Request_ptr own2(tr2);
502 TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
503 HTTP::Request_ptr own3(tr3);
506 waitForComplete(&cl, tr3);
507 VERIFY(tr->complete);
508 VERIFY(tr2->complete);
509 COMPARE(tr->bodyData, string(BODY1));
510 COMPARE(tr2->bodyData, string(BODY1));
511 COMPARE(tr3->bodyData, string(BODY1));
514 // multiple requests with an HTTP 1.0 server
516 cout << "http 1.0 multiple requests" << endl;
519 TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0/A");
520 HTTP::Request_ptr own(tr);
523 TestRequest* tr2 = new TestRequest("http://localhost:2000/test_1_0/B");
524 HTTP::Request_ptr own2(tr2);
527 TestRequest* tr3 = new TestRequest("http://localhost:2000/test_1_0/C");
528 HTTP::Request_ptr own3(tr3);
531 waitForComplete(&cl, tr3);
532 VERIFY(tr->complete);
533 VERIFY(tr2->complete);
534 COMPARE(tr->bodyData, string(BODY1));
535 COMPARE(tr2->bodyData, string(BODY1));
536 COMPARE(tr3->bodyData, string(BODY1));
539 cout << "all tests passed ok" << endl;