]> git.mxchange.org Git - simgear.git/blob - simgear/io/test_HTTP.cxx
Linux test_HTTP fixes.
[simgear.git] / simgear / io / test_HTTP.cxx
1 #include <cstdlib>
2
3 #include <iostream>
4 #include <map>
5 #include <sstream>
6 #include <errno.h>
7
8 #include <boost/algorithm/string/case_conv.hpp>
9
10 #include <simgear/simgear_config.h>
11
12 #include "HTTPClient.hxx"
13 #include "HTTPRequest.hxx"
14
15 #include <simgear/io/sg_netChat.hxx>
16 #include <simgear/misc/strutils.hxx>
17 #include <simgear/timing/timestamp.hxx>
18 #include <simgear/debug/logstream.hxx>
19
20 #if defined(ENABLE_CURL)
21 #include <curl/multi.h>
22 #endif
23
24 using std::cout;
25 using std::cerr;
26 using std::endl;
27 using std::string;
28 using std::stringstream;
29
30 using namespace simgear;
31
32 const char* BODY1 = "The quick brown fox jumps over a lazy dog.";
33 const char* BODY3 = "Cras ut neque nulla. Duis ut velit neque, sit amet "
34 "pharetra risus. In est ligula, lacinia vitae congue in, sollicitudin at "
35 "libero. Mauris pharetra pretium elit, nec placerat dui semper et. Maecenas "
36 "magna magna, placerat sed luctus ac, commodo et ligula. Mauris at purus et "
37 "nisl molestie auctor placerat at quam. Donec sapien magna, venenatis sed "
38 "iaculis id, fringilla vel arcu. Duis sed neque nisi. Cras a arcu sit amet "
39 "risus ultrices varius. Integer sagittis euismod dui id varius. Cras vel "
40 "justo gravida metus.";
41
42 const unsigned int body2Size = 8 * 1024;
43 char body2[body2Size];
44
45 #define COMPARE(a, b) \
46     if ((a) != (b))  { \
47         cerr << "failed:" << #a << " != " << #b << endl; \
48         cerr << "\tgot:'" << a << "'" << endl; \
49         exit(1); \
50     }
51
52 #define VERIFY(a) \
53     if (!(a))  { \
54         cerr << "failed:" << #a << endl; \
55         exit(1); \
56     }
57
58 class TestRequest : public HTTP::Request
59 {
60 public:
61     bool complete;
62     bool failed;
63     string bodyData;
64
65     TestRequest(const std::string& url, const std::string method = "GET") :
66         HTTP::Request(url, method),
67         complete(false),
68         failed(false)
69     {
70
71     }
72
73     std::map<string, string> headers;
74 protected:
75
76     virtual void onDone()
77     {
78         complete = true;
79     }
80
81     virtual void onFail()
82     {
83         failed = true;
84     }
85
86     virtual void gotBodyData(const char* s, int n)
87     {
88       //std::cout << "got body data:'" << string(s, n) << "'" <<std::endl;
89         bodyData += string(s, n);
90     }
91
92     virtual void responseHeader(const string& header, const string& value)
93     {
94         Request::responseHeader(header, value);
95         headers[header] =  value;
96     }
97 };
98
99 class TestServerChannel : public NetChat
100 {
101 public:
102     enum State
103     {
104         STATE_IDLE = 0,
105         STATE_HEADERS,
106         STATE_CLOSING,
107         STATE_REQUEST_BODY
108     };
109
110     TestServerChannel()
111     {
112         state = STATE_IDLE;
113         setTerminator("\r\n");
114
115     }
116
117     virtual void collectIncomingData(const char* s, int n)
118     {
119         buffer += string(s, n);
120     }
121
122     virtual void foundTerminator(void)
123     {
124         if (state == STATE_IDLE) {
125             state = STATE_HEADERS;
126             string_list line = strutils::split(buffer, NULL, 3);
127             if (line.size() < 3) {
128                 cerr << "malformed request:" << buffer << endl;
129                 exit(-1);
130             }
131
132             method = line[0];
133             path = line[1];
134
135             string::size_type queryPos = path.find('?');
136             if (queryPos != string::npos) {
137                 parseArgs(path.substr(queryPos + 1));
138                 path = path.substr(0, queryPos);
139             }
140
141             httpVersion = line[2];
142             requestHeaders.clear();
143             buffer.clear();
144         } else if (state == STATE_HEADERS) {
145             string s = strutils::simplify(buffer);
146             if (s.empty()) {
147                 buffer.clear();
148                 receivedRequestHeaders();
149                 return;
150             }
151
152             string::size_type colonPos = buffer.find(':');
153             if (colonPos == string::npos) {
154                 cerr << "test malformed HTTP response header:" << buffer << endl;
155                 buffer.clear();
156                 return;
157             }
158
159             string key = strutils::simplify(buffer.substr(0, colonPos));
160             string value = strutils::strip(buffer.substr(colonPos + 1));
161             requestHeaders[key] = value;
162             buffer.clear();
163         } else if (state == STATE_REQUEST_BODY) {
164             receivedBody();
165             setTerminator("\r\n");
166         } else if (state == STATE_CLOSING) {
167           // ignore!
168         }
169     }
170
171     void parseArgs(const string& argData)
172     {
173         string_list argv = strutils::split(argData, "&");
174         for (unsigned int a=0; a<argv.size(); ++a) {
175             string::size_type eqPos = argv[a].find('=');
176             if (eqPos == string::npos) {
177                 cerr << "malformed HTTP argument:" << argv[a] << endl;
178                 continue;
179             }
180
181             string key = argv[a].substr(0, eqPos);
182             string value = argv[a].substr(eqPos + 1);
183             args[key] = value;
184         }
185     }
186
187     void receivedRequestHeaders()
188     {
189         state = STATE_IDLE;
190         if (path == "/test1") {
191             string contentStr(BODY1);
192             stringstream d;
193             d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
194             d << "Content-Length:" << contentStr.size() << "\r\n";
195             d << "\r\n"; // final CRLF to terminate the headers
196             d << contentStr;
197             push(d.str().c_str());
198         } else if (path == "/testLorem") {
199             string contentStr(BODY3);
200             stringstream d;
201             d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
202             d << "Content-Length:" << contentStr.size() << "\r\n";
203             d << "\r\n"; // final CRLF to terminate the headers
204             d << contentStr;
205             push(d.str().c_str());
206         } else if (path == "/test_zero_length_content") {
207             string contentStr;
208             stringstream d;
209             d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
210             d << "Content-Length:" << contentStr.size() << "\r\n";
211             d << "\r\n"; // final CRLF to terminate the headers
212             d << contentStr;
213             push(d.str().c_str());
214         } else if (path == "/test_headers") {
215             COMPARE(requestHeaders["X-Foo"], string("Bar"));
216             COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
217
218             string contentStr(BODY1);
219             stringstream d;
220             d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
221             d << "Content-Length:" << contentStr.size() << "\r\n";
222             d << "\r\n"; // final CRLF to terminate the headers
223             d << contentStr;
224             push(d.str().c_str());
225         } else if (path == "/test2") {
226             sendBody2();
227         } else if (path == "/testchunked") {
228             stringstream d;
229             d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
230             d << "Transfer-Encoding:chunked\r\n";
231             d << "\r\n";
232             d << "8\r\n"; // first chunk
233             d << "ABCDEFGH\r\n";
234             d << "6\r\n"; // second chunk
235             d << "ABCDEF\r\n";
236             d << "10\r\n"; // third chunk
237             d << "ABCDSTUVABCDSTUV\r\n";
238             d << "0\r\n"; // start of trailer
239             d << "X-Foobar: wibble\r\n"; // trailer data
240             d << "\r\n";
241             push(d.str().c_str());
242         } else if (path == "http://www.google.com/test2") {
243             // proxy test
244             if (requestHeaders["Host"] != "www.google.com") {
245                 sendErrorResponse(400, true, "bad destination");
246             }
247
248             if (requestHeaders["Proxy-Authorization"] != string()) {
249                 sendErrorResponse(401, false, "bad auth, not empty"); // shouldn't supply auth
250             }
251
252             sendBody2();
253         } else if (path == "http://www.google.com/test3") {
254             // proxy test
255             if (requestHeaders["Host"] != "www.google.com") {
256                 sendErrorResponse(400, true, "bad destination");
257             }
258
259             string credentials = requestHeaders["Proxy-Authorization"];
260             if (credentials.substr(0, 5) != "Basic") {
261               // request basic auth
262               stringstream d;
263               d << "HTTP/1.1 " << 407 << " " << reasonForCode(407) << "\r\n";
264               d << "WWW-Authenticate: Basic real=\"simgear\"\r\n";
265               d << "\r\n"; // final CRLF to terminate the headers
266               push(d.str().c_str());
267               return;
268             }
269
270             std::vector<unsigned char> userAndPass;
271             strutils::decodeBase64(credentials.substr(6), userAndPass);
272             std::string decodedUserPass((char*) userAndPass.data(), userAndPass.size());
273
274             if (decodedUserPass != "johndoe:swordfish") {
275                 std::map<string, string>::const_iterator it;
276                 for (it = requestHeaders.begin(); it != requestHeaders.end(); ++it) {
277                   cerr << "header:" << it->first << " = " << it->second << endl;
278                 }
279
280                 sendErrorResponse(401, false, "bad auth, not as set"); // forbidden
281             }
282
283             sendBody2();
284         } else if (strutils::starts_with(path, "/test_1_0")) {
285             string contentStr(BODY1);
286             if (strutils::ends_with(path, "/B")) {
287                 contentStr = BODY3;
288             }
289             stringstream d;
290             d << "HTTP/1.0 " << 200 << " " << reasonForCode(200) << "\r\n";
291             d << "\r\n"; // final CRLF to terminate the headers
292             d << contentStr;
293             push(d.str().c_str());
294             closeAfterSending();
295         } else if (path == "/test_close") {
296             string contentStr(BODY1);
297             stringstream d;
298             d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
299             d << "Connection: close\r\n";
300             d << "\r\n"; // final CRLF to terminate the headers
301             d << contentStr;
302             push(d.str().c_str());
303             closeAfterSending();
304         } else if (path == "/test_abrupt_close") {
305             // simulate server doing socket close before sending any
306             // response - this used to cause a TerraSync failure since we
307             // would get stuck restarting the request
308             closeAfterSending();
309
310         } else if (path == "/test_args") {
311             if ((args["foo"] != "abc") || (args["bar"] != "1234") || (args["username"] != "johndoe")) {
312                 sendErrorResponse(400, true, "bad arguments");
313                 return;
314             }
315
316             string contentStr(BODY1);
317             stringstream d;
318             d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
319             d << "Content-Length:" << contentStr.size() << "\r\n";
320             d << "\r\n"; // final CRLF to terminate the headers
321             d << contentStr;
322             push(d.str().c_str());
323         } else if (path == "/test_post") {
324             if (requestHeaders["Content-Type"] != "application/x-www-form-urlencoded") {
325                 cerr << "bad content type: '" << requestHeaders["Content-Type"] << "'" << endl;
326                  sendErrorResponse(400, true, "bad content type");
327                  return;
328             }
329
330             requestContentLength = strutils::to_int(requestHeaders["Content-Length"]);
331             setByteCount(requestContentLength);
332             state = STATE_REQUEST_BODY;
333         } else if ((path == "/test_put") || (path == "/test_create")) {
334               if (requestHeaders["Content-Type"] != "x-application/foobar") {
335                   cerr << "bad content type: '" << requestHeaders["Content-Type"] << "'" << endl;
336                    sendErrorResponse(400, true, "bad content type");
337                    return;
338               }
339
340               requestContentLength = strutils::to_int(requestHeaders["Content-Length"]);
341               setByteCount(requestContentLength);
342               state = STATE_REQUEST_BODY;
343         } else {
344             sendErrorResponse(404, false, "");
345         }
346     }
347
348     void closeAfterSending()
349     {
350       state = STATE_CLOSING;
351       closeWhenDone();
352     }
353
354     void receivedBody()
355     {
356         state = STATE_IDLE;
357         if (method == "POST") {
358             parseArgs(buffer);
359         }
360
361         if (path == "/test_post") {
362             if ((args["foo"] != "abc") || (args["bar"] != "1234") || (args["username"] != "johndoe")) {
363                 sendErrorResponse(400, true, "bad arguments");
364                 return;
365             }
366
367             stringstream d;
368             d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
369             d << "\r\n"; // final CRLF to terminate the headers
370             push(d.str().c_str());
371         } else if (path == "/test_put") {
372           std::cerr << "sending PUT response" << std::endl;
373
374           COMPARE(buffer, BODY3);
375           stringstream d;
376           d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
377           d << "\r\n"; // final CRLF to terminate the headers
378           push(d.str().c_str());
379         } else if (path == "/test_create") {
380           std::cerr << "sending create response" << std::endl;
381
382           std::string entityStr = "http://localhost:2000/something.txt";
383
384           COMPARE(buffer, BODY3);
385           stringstream d;
386           d << "HTTP/1.1 " << 201 << " " << reasonForCode(201) << "\r\n";
387           d << "Location:" << entityStr << "\r\n";
388           d << "Content-Length:" << entityStr.size() << "\r\n";
389           d << "\r\n"; // final CRLF to terminate the headers
390           d << entityStr;
391
392           push(d.str().c_str());
393         } else {
394           std::cerr << "weird URL " << path << std::endl;
395           sendErrorResponse(400, true, "bad URL:" + path);
396         }
397
398         buffer.clear();
399     }
400
401     void sendBody2()
402     {
403         stringstream d;
404         d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
405         d << "Content-Length:" << body2Size << "\r\n";
406         d << "\r\n"; // final CRLF to terminate the headers
407         push(d.str().c_str());
408         bufferSend(body2, body2Size);
409     }
410
411     void sendErrorResponse(int code, bool close, string content)
412     {
413         cerr << "sending error " << code << " for " << path << endl;
414         cerr << "\tcontent:" << content << endl;
415
416         stringstream headerData;
417         headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n";
418         headerData << "Content-Length:" << content.size() << "\r\n";
419         headerData << "\r\n"; // final CRLF to terminate the headers
420         push(headerData.str().c_str());
421         push(content.c_str());
422
423         if (close) {
424             closeWhenDone();
425         }
426     }
427
428     string reasonForCode(int code)
429     {
430         switch (code) {
431             case 200: return "OK";
432             case 201: return "Created";
433             case 204: return "no content";
434             case 404: return "not found";
435             case 407: return "proxy authentication required";
436             default: return "unknown code";
437         }
438     }
439
440     State state;
441     string buffer;
442     string method;
443     string path;
444     string httpVersion;
445     std::map<string, string> requestHeaders;
446     std::map<string, string> args;
447     int requestContentLength;
448 };
449
450 class TestServer : public NetChannel
451 {
452     simgear::NetChannelPoller _poller;
453 public:
454     TestServer()
455     {
456         Socket::initSockets();
457
458         open();
459         bind(NULL, 2000); // localhost, any port
460         listen(5);
461
462         _poller.addChannel(this);
463     }
464
465     virtual ~TestServer()
466     {
467     }
468
469     virtual bool writable (void) { return false ; }
470
471     virtual void handleAccept (void)
472     {
473         simgear::IPAddress addr ;
474         int handle = accept ( &addr ) ;
475         //cout << "did accept from " << addr.getHost() << ":" << addr.getPort() << endl;
476         TestServerChannel* chan = new TestServerChannel();
477         chan->setHandle(handle);
478
479         _poller.addChannel(chan);
480     }
481
482     void poll()
483     {
484         _poller.poll();
485     }
486 };
487
488 TestServer testServer;
489
490 void waitForComplete(HTTP::Client* cl, TestRequest* tr)
491 {
492     SGTimeStamp start(SGTimeStamp::now());
493     while (start.elapsedMSec() <  10000) {
494         cl->update();
495         testServer.poll();
496
497         if (tr->complete) {
498             return;
499         }
500         SGTimeStamp::sleepForMSec(15);
501     }
502
503     cerr << "timed out" << endl;
504 }
505
506 void waitForFailed(HTTP::Client* cl, TestRequest* tr)
507 {
508     SGTimeStamp start(SGTimeStamp::now());
509     while (start.elapsedMSec() <  10000) {
510         cl->update();
511         testServer.poll();
512
513         if (tr->failed) {
514             return;
515         }
516         SGTimeStamp::sleepForMSec(15);
517     }
518
519     cerr << "timed out waiting for failure" << endl;
520 }
521
522 int main(int argc, char* argv[])
523 {
524     sglog().setLogLevels( SG_ALL, SG_INFO );
525
526     HTTP::Client cl;
527     // force all requests to use the same connection for this test
528     cl.setMaxConnections(1);
529
530 // test URL parsing
531     TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
532     COMPARE(tr1->scheme(), "http");
533     COMPARE(tr1->hostAndPort(), "localhost.woo.zar:2000");
534     COMPARE(tr1->host(), "localhost.woo.zar");
535     COMPARE(tr1->port(), 2000);
536     COMPARE(tr1->path(), "/test1");
537
538     TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
539     COMPARE(tr2->scheme(), "http");
540     COMPARE(tr2->hostAndPort(), "192.168.1.1");
541     COMPARE(tr2->host(), "192.168.1.1");
542     COMPARE(tr2->port(), 80);
543     COMPARE(tr2->path(), "/test1/dir/thing/file.png");
544
545 // basic get request
546     {
547         TestRequest* tr = new TestRequest("http://localhost:2000/test1");
548         HTTP::Request_ptr own(tr);
549         cl.makeRequest(tr);
550
551         waitForComplete(&cl, tr);
552         COMPARE(tr->responseCode(), 200);
553         COMPARE(tr->responseReason(), string("OK"));
554         COMPARE(tr->responseLength(), strlen(BODY1));
555         COMPARE(tr->responseBytesReceived(), strlen(BODY1));
556         COMPARE(tr->bodyData, string(BODY1));
557     }
558
559     {
560       TestRequest* tr = new TestRequest("http://localhost:2000/testLorem");
561       HTTP::Request_ptr own(tr);
562       cl.makeRequest(tr);
563
564       waitForComplete(&cl, tr);
565       COMPARE(tr->responseCode(), 200);
566       COMPARE(tr->responseReason(), string("OK"));
567       COMPARE(tr->responseLength(), strlen(BODY3));
568       COMPARE(tr->responseBytesReceived(), strlen(BODY3));
569       COMPARE(tr->bodyData, string(BODY3));
570     }
571
572     {
573         TestRequest* tr = new TestRequest("http://localhost:2000/test_args?foo=abc&bar=1234&username=johndoe");
574         HTTP::Request_ptr own(tr);
575         cl.makeRequest(tr);
576         waitForComplete(&cl, tr);
577         COMPARE(tr->responseCode(), 200);
578     }
579
580     {
581         TestRequest* tr = new TestRequest("http://localhost:2000/test_headers");
582         HTTP::Request_ptr own(tr);
583         tr->requestHeader("X-Foo") = "Bar";
584         tr->requestHeader("X-AnotherHeader") = "A longer value";
585         cl.makeRequest(tr);
586
587         waitForComplete(&cl, tr);
588         COMPARE(tr->responseCode(), 200);
589         COMPARE(tr->responseReason(), string("OK"));
590         COMPARE(tr->responseLength(), strlen(BODY1));
591         COMPARE(tr->responseBytesReceived(), strlen(BODY1));
592         COMPARE(tr->bodyData, string(BODY1));
593     }
594
595 // larger get request
596     for (unsigned int i=0; i<body2Size; ++i) {
597         body2[i] = (i << 4) | (i >> 2);
598     }
599
600     {
601         TestRequest* tr = new TestRequest("http://localhost:2000/test2");
602         HTTP::Request_ptr own(tr);
603         cl.makeRequest(tr);
604         waitForComplete(&cl, tr);
605         COMPARE(tr->responseCode(), 200);
606         COMPARE(tr->responseBytesReceived(), body2Size);
607         COMPARE(tr->bodyData, string(body2, body2Size));
608     }
609
610     cerr << "testing chunked transfer encoding" << endl;
611     {
612         TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
613         HTTP::Request_ptr own(tr);
614         cl.makeRequest(tr);
615
616         waitForComplete(&cl, tr);
617         COMPARE(tr->responseCode(), 200);
618         COMPARE(tr->responseReason(), string("OK"));
619         COMPARE(tr->responseBytesReceived(), 30);
620         COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
621     // check trailers made it too
622         COMPARE(tr->headers["x-foobar"], string("wibble"));
623     }
624
625 // test 404
626     {
627         TestRequest* tr = new TestRequest("http://localhost:2000/not-found");
628         HTTP::Request_ptr own(tr);
629         cl.makeRequest(tr);
630         waitForComplete(&cl, tr);
631         COMPARE(tr->responseCode(), 404);
632         COMPARE(tr->responseReason(), string("not found"));
633         COMPARE(tr->responseLength(), 0);
634     }
635
636     cout << "done 404 test" << endl;
637
638     {
639         TestRequest* tr = new TestRequest("http://localhost:2000/test_args?foo=abc&bar=1234&username=johndoe");
640         HTTP::Request_ptr own(tr);
641         cl.makeRequest(tr);
642         waitForComplete(&cl, tr);
643         COMPARE(tr->responseCode(), 200);
644     }
645
646     cout << "done1" << endl;
647 // test HTTP/1.0
648     {
649         TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0");
650         HTTP::Request_ptr own(tr);
651         cl.makeRequest(tr);
652         waitForComplete(&cl, tr);
653         COMPARE(tr->responseCode(), 200);
654         COMPARE(tr->responseLength(), strlen(BODY1));
655         COMPARE(tr->bodyData, string(BODY1));
656     }
657
658     cout << "done2" << endl;
659 // test HTTP/1.1 Connection::close
660     {
661         TestRequest* tr = new TestRequest("http://localhost:2000/test_close");
662         HTTP::Request_ptr own(tr);
663         cl.makeRequest(tr);
664         waitForComplete(&cl, tr);
665         COMPARE(tr->responseCode(), 200);
666         COMPARE(tr->responseLength(), strlen(BODY1));
667         COMPARE(tr->bodyData, string(BODY1));
668     }
669     cout << "done3" << endl;
670 // test connectToHost failure
671
672     {
673         TestRequest* tr = new TestRequest("http://not.found/something");
674         HTTP::Request_ptr own(tr);
675         cl.makeRequest(tr);
676         waitForFailed(&cl, tr);
677
678
679
680 #if defined(ENABLE_CURL)
681       const int HOST_NOT_FOUND_CODE = CURLE_COULDNT_RESOLVE_HOST;
682 #else
683       const int HOST_NOT_FOUND_CODE = ENOENT;
684 #endif
685         COMPARE(tr->responseCode(), HOST_NOT_FOUND_CODE);
686     }
687
688   cout << "testing abrupt close" << endl;
689     // test server-side abrupt close
690     {
691         TestRequest* tr = new TestRequest("http://localhost:2000/test_abrupt_close");
692         HTTP::Request_ptr own(tr);
693         cl.makeRequest(tr);
694         waitForFailed(&cl, tr);
695
696   #if defined(ENABLE_CURL)
697         const int SERVER_NO_DATA_CODE = CURLE_GOT_NOTHING;
698   #else
699         const int SERVER_NO_DATA_CODESERVER_NO_DATA_CODE = 500;
700   #endif
701         COMPARE(tr->responseCode(), SERVER_NO_DATA_CODE);
702     }
703
704 cout << "testing proxy close" << endl;
705 // test proxy
706     {
707         cl.setProxy("localhost", 2000);
708         TestRequest* tr = new TestRequest("http://www.google.com/test2");
709         HTTP::Request_ptr own(tr);
710         cl.makeRequest(tr);
711         waitForComplete(&cl, tr);
712         COMPARE(tr->responseCode(), 200);
713         COMPARE(tr->responseLength(), body2Size);
714         COMPARE(tr->bodyData, string(body2, body2Size));
715     }
716
717 #if defined(ENABLE_CURL)
718     {
719         cl.setProxy("localhost", 2000, "johndoe:swordfish");
720         TestRequest* tr = new TestRequest("http://www.google.com/test3");
721         HTTP::Request_ptr own(tr);
722         cl.makeRequest(tr);
723         waitForComplete(&cl, tr);
724         COMPARE(tr->responseCode(), 200);
725         COMPARE(tr->responseBytesReceived(), body2Size);
726         COMPARE(tr->bodyData, string(body2, body2Size));
727     }
728 #endif
729
730 // pipelining
731     cout << "testing HTTP 1.1 pipelining" << endl;
732
733     {
734
735         cl.setProxy("", 80);
736         TestRequest* tr = new TestRequest("http://localhost:2000/test1");
737         HTTP::Request_ptr own(tr);
738         cl.makeRequest(tr);
739
740
741         TestRequest* tr2 = new TestRequest("http://localhost:2000/testLorem");
742         HTTP::Request_ptr own2(tr2);
743         cl.makeRequest(tr2);
744
745         TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
746         HTTP::Request_ptr own3(tr3);
747         cl.makeRequest(tr3);
748
749         waitForComplete(&cl, tr3);
750         VERIFY(tr->complete);
751         VERIFY(tr2->complete);
752         COMPARE(tr->bodyData, string(BODY1));
753
754         COMPARE(tr2->responseLength(), strlen(BODY3));
755         COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
756         COMPARE(tr2->bodyData, string(BODY3));
757
758         COMPARE(tr3->bodyData, string(BODY1));
759     }
760
761 // multiple requests with an HTTP 1.0 server
762     {
763         cout << "http 1.0 multiple requests" << endl;
764
765         cl.setProxy("", 80);
766         TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0/A");
767         HTTP::Request_ptr own(tr);
768         cl.makeRequest(tr);
769
770         TestRequest* tr2 = new TestRequest("http://localhost:2000/test_1_0/B");
771         HTTP::Request_ptr own2(tr2);
772         cl.makeRequest(tr2);
773
774         TestRequest* tr3 = new TestRequest("http://localhost:2000/test_1_0/C");
775         HTTP::Request_ptr own3(tr3);
776         cl.makeRequest(tr3);
777
778         waitForComplete(&cl, tr3);
779         VERIFY(tr->complete);
780         VERIFY(tr2->complete);
781
782         COMPARE(tr->responseLength(), strlen(BODY1));
783         COMPARE(tr->responseBytesReceived(), strlen(BODY1));
784         COMPARE(tr->bodyData, string(BODY1));
785
786         COMPARE(tr2->responseLength(), strlen(BODY3));
787         COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
788         COMPARE(tr2->bodyData, string(BODY3));
789         COMPARE(tr3->bodyData, string(BODY1));
790     }
791
792 // POST
793     {
794         cout << "testing POST" << endl;
795         TestRequest* tr = new TestRequest("http://localhost:2000/test_post?foo=abc&bar=1234&username=johndoe", "POST");
796         tr->setBodyData("", "application/x-www-form-urlencoded");
797
798         HTTP::Request_ptr own(tr);
799         cl.makeRequest(tr);
800         waitForComplete(&cl, tr);
801         COMPARE(tr->responseCode(), 204);
802     }
803
804     // PUT
805         {
806             cout << "testing PUT" << endl;
807             TestRequest* tr = new TestRequest("http://localhost:2000/test_put", "PUT");
808             tr->setBodyData(BODY3, "x-application/foobar");
809
810             HTTP::Request_ptr own(tr);
811             cl.makeRequest(tr);
812             waitForComplete(&cl, tr);
813             COMPARE(tr->responseCode(), 204);
814         }
815
816         {
817             cout << "testing PUT create" << endl;
818             TestRequest* tr = new TestRequest("http://localhost:2000/test_create", "PUT");
819             tr->setBodyData(BODY3, "x-application/foobar");
820
821             HTTP::Request_ptr own(tr);
822             cl.makeRequest(tr);
823             waitForComplete(&cl, tr);
824             COMPARE(tr->responseCode(), 201);
825         }
826
827     // test_zero_length_content
828     {
829         cout << "zero-length-content-response" << endl;
830         TestRequest* tr = new TestRequest("http://localhost:2000/test_zero_length_content");
831         HTTP::Request_ptr own(tr);
832         cl.makeRequest(tr);
833         waitForComplete(&cl, tr);
834         COMPARE(tr->responseCode(), 200);
835         COMPARE(tr->bodyData, string());
836         COMPARE(tr->responseBytesReceived(), 0);
837     }
838
839
840     cout << "all tests passed ok" << endl;
841     return EXIT_SUCCESS;
842 }