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