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