]> git.mxchange.org Git - simgear.git/blob - simgear/io/test_HTTP.cxx
Remove automake ignores.
[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
26 const unsigned int body2Size = 8 * 1024;
27 char body2[body2Size];
28
29 #define COMPARE(a, b) \
30     if ((a) != (b))  { \
31         cerr << "failed:" << #a << " != " << #b << endl; \
32         cerr << "\tgot:" << a << endl; \
33         exit(1); \
34     }
35
36 #define VERIFY(a) \
37     if (!(a))  { \
38         cerr << "failed:" << #a << endl; \
39         exit(1); \
40     }
41     
42 class TestRequest : public HTTP::Request
43 {
44 public:
45     bool complete;
46     bool failed;
47     string bodyData;
48     
49     TestRequest(const std::string& url) : 
50         HTTP::Request(url),
51         complete(false)
52     {
53     }
54     
55     std::map<string, string> sendHeaders;
56     std::map<string, string> headers;
57 protected:
58     string_list requestHeaders() const
59     {
60         string_list r;
61         std::map<string, string>::const_iterator it;
62         for (it = sendHeaders.begin(); it != sendHeaders.end(); ++it) {
63             r.push_back(it->first);
64         }
65         return r;
66     }
67     
68     string header(const string& name) const
69     {
70         std::map<string, string>::const_iterator it = sendHeaders.find(name);
71         if (it == sendHeaders.end()) {
72             return string();
73         }
74         
75         return it->second;
76     }
77     
78     virtual void responseHeadersComplete()
79     {
80     }
81     
82     virtual void responseComplete()
83     {
84         complete = true;
85     }  
86     
87     virtual void failure()
88     {
89         failed = true;
90     }
91     
92     virtual void gotBodyData(const char* s, int n)
93     {
94         bodyData += string(s, n);
95     }
96     
97     virtual void responseHeader(const string& header, const string& value)
98     {
99         headers[header] =  value;
100     }
101 };
102
103 class TestServerChannel : public NetChat
104 {
105 public:  
106     enum State
107     {
108         STATE_IDLE = 0,
109         STATE_HEADERS,
110         STATE_REQUEST_BODY
111     };
112     
113     TestServerChannel()
114     {
115         state = STATE_IDLE;
116         setTerminator("\r\n");
117     }
118     
119     virtual void collectIncomingData(const char* s, int n)
120     {
121         buffer += string(s, n);
122     }
123     
124     virtual void foundTerminator(void)
125     {
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;
131                 exit(-1);
132             }
133             
134             method = line[0];
135             path = line[1];
136             httpVersion = line[2];
137             requestHeaders.clear();
138             buffer.clear();
139         } else if (state == STATE_HEADERS) {
140             string s = strutils::simplify(buffer);
141             if (s.empty()) {
142                 buffer.clear();
143                 receivedRequestHeaders();
144                 return;
145             }
146             
147             int colonPos = buffer.find(':');
148             if (colonPos < 0) {
149                 cerr << "malformed HTTP response header:" << buffer << endl;
150                 buffer.clear();
151                 return;
152             }
153
154             string key = strutils::simplify(buffer.substr(0, colonPos));
155             string value = strutils::strip(buffer.substr(colonPos + 1));
156             requestHeaders[key] = value;
157             buffer.clear();
158         } else if (state == STATE_REQUEST_BODY) {
159             
160         }
161     }  
162     
163     void receivedRequestHeaders()
164     {
165         state = STATE_IDLE;
166         
167         if (path == "/test1") {
168             string contentStr(BODY1);
169             stringstream d;
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
173             d << contentStr;
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"));
178             
179             string contentStr(BODY1);
180             stringstream d;
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
184             d << contentStr;
185             push(d.str().c_str());
186         } else if (path == "/test2") {
187             sendBody2();
188         } else if (path == "/testchunked") {
189             stringstream d;
190             d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
191             d << "Transfer-Encoding:chunked\r\n";
192             d << "\r\n";
193             d << "8\r\n"; // first chunk
194             d << "ABCDEFGH\r\n";
195             d << "6\r\n"; // second chunk
196             d << "ABCDEF\r\n";
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
201             d << "\r\n";
202             push(d.str().c_str());
203         } else if (path == "http://www.google.com/test2") {
204             // proxy test
205             if (requestHeaders["Host"] != "www.google.com") {
206                 sendErrorResponse(400, true, "bad destination");
207             }
208             
209             if (requestHeaders["Proxy-Authorization"] != string()) {
210                 sendErrorResponse(401, false, "bad auth"); // shouldn't supply auth
211             }
212             
213             sendBody2();
214         } else if (path == "http://www.google.com/test3") {
215             // proxy test
216             if (requestHeaders["Host"] != "www.google.com") {
217                 sendErrorResponse(400, true, "bad destination");
218             }
219
220             if (requestHeaders["Proxy-Authorization"] != "ABCDEF") {
221                 sendErrorResponse(401, false, "bad auth"); // forbidden
222             }
223
224             sendBody2();
225         } else if (strutils::starts_with(path, "/test_1_0")) {
226             string contentStr(BODY1);
227             stringstream d;
228             d << "HTTP/1.0 " << 200 << " " << reasonForCode(200) << "\r\n";
229             d << "\r\n"; // final CRLF to terminate the headers
230             d << contentStr;
231             push(d.str().c_str());
232             closeWhenDone();
233         } else if (path == "/test_close") {
234             string contentStr(BODY1);
235             stringstream d;
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
239             d << contentStr;
240             push(d.str().c_str());
241             closeWhenDone();
242         } else {
243             sendErrorResponse(404, true, "");
244         }
245     }
246     
247     void sendBody2()
248     {
249         stringstream d;
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);
255     }
256     
257     void sendErrorResponse(int code, bool close, string content)
258     {
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());
266         
267         if (close) {
268             closeWhenDone();
269         }
270     }
271     
272     string reasonForCode(int code) 
273     {
274         switch (code) {
275             case 200: return "OK";
276             case 404: return "not found";
277             default: return "unknown code";
278         }
279     }
280     
281     State state;
282     string buffer;
283     string method;
284     string path;
285     string httpVersion;
286     std::map<string, string> requestHeaders;
287 };
288
289 class TestServer : public NetChannel
290 {
291 public:   
292     TestServer()
293     {
294         open();
295         bind(NULL, 2000); // localhost, any port
296         listen(5);
297     }
298     
299     virtual ~TestServer()
300     {    
301     }
302     
303     virtual bool writable (void) { return false ; }
304
305     virtual void handleAccept (void)
306     {
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);
312     }
313 };
314
315 void waitForComplete(HTTP::Client* cl, TestRequest* tr)
316 {
317     SGTimeStamp start(SGTimeStamp::now());
318     while (start.elapsedMSec() <  1000) {
319         cl->update();
320         if (tr->complete) {
321             return;
322         }
323         SGTimeStamp::sleepForMSec(1);
324     }
325     
326     cerr << "timed out" << endl;
327 }
328
329 void waitForFailed(HTTP::Client* cl, TestRequest* tr)
330 {
331     SGTimeStamp start(SGTimeStamp::now());
332     while (start.elapsedMSec() <  1000) {
333         cl->update();
334         if (tr->failed) {
335             return;
336         }
337         SGTimeStamp::sleepForMSec(1);
338     }
339     
340     cerr << "timed out waiting for failure" << endl;
341 }
342
343 int main(int argc, char* argv[])
344 {
345     TestServer s;
346     
347     HTTP::Client cl;
348
349 // test URL parsing
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");
356     
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");
363     
364 // basic get request
365     {
366         TestRequest* tr = new TestRequest("http://localhost:2000/test1");
367         HTTP::Request_ptr own(tr);
368         cl.makeRequest(tr);
369
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));
376     }
377
378     {
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";
383         cl.makeRequest(tr);
384
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));
391     }
392     
393 // larger get request
394     for (unsigned int i=0; i<body2Size; ++i) {
395         body2[i] = (i << 4) | (i >> 2);
396     }
397     
398     {
399         TestRequest* tr = new TestRequest("http://localhost:2000/test2");
400         HTTP::Request_ptr own(tr);
401         cl.makeRequest(tr);
402         waitForComplete(&cl, tr);
403         COMPARE(tr->responseCode(), 200);
404         COMPARE(tr->responseBytesReceived(), body2Size);
405         COMPARE(tr->bodyData, string(body2, body2Size));
406     }
407     
408     {
409         TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
410         HTTP::Request_ptr own(tr);
411         cl.makeRequest(tr);
412
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"));
420     }
421     
422 // test 404
423     {
424         TestRequest* tr = new TestRequest("http://localhost:2000/not-found");
425         HTTP::Request_ptr own(tr);
426         cl.makeRequest(tr);
427         waitForComplete(&cl, tr);
428         COMPARE(tr->responseCode(), 404);
429         COMPARE(tr->responseReason(), string("not found"));
430         COMPARE(tr->responseLength(), 0);
431     }
432
433     cout << "done1" << endl;
434 // test HTTP/1.0
435     {
436         TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0");
437         HTTP::Request_ptr own(tr);
438         cl.makeRequest(tr);
439         waitForComplete(&cl, tr);
440         COMPARE(tr->responseCode(), 200);
441         COMPARE(tr->responseLength(), strlen(BODY1));
442         COMPARE(tr->bodyData, string(BODY1));
443     }
444
445     cout << "done2" << endl;
446 // test HTTP/1.1 Connection::close
447     {
448         TestRequest* tr = new TestRequest("http://localhost:2000/test_close");
449         HTTP::Request_ptr own(tr);
450         cl.makeRequest(tr);
451         waitForComplete(&cl, tr);
452         COMPARE(tr->responseCode(), 200);
453         COMPARE(tr->responseLength(), strlen(BODY1));
454         COMPARE(tr->bodyData, string(BODY1));
455     }
456     cout << "done3" << endl;
457 // test connectToHost failure
458 /*
459     {
460         TestRequest* tr = new TestRequest("http://not.found/something");
461         HTTP::Request_ptr own(tr);
462         cl.makeRequest(tr);
463         waitForFailed(tr);
464         COMPARE(tr->responseCode(), -1);
465     }
466     */
467 // test proxy
468     {
469         cl.setProxy("localhost", 2000);
470         TestRequest* tr = new TestRequest("http://www.google.com/test2");
471         HTTP::Request_ptr own(tr);
472         cl.makeRequest(tr);
473         waitForComplete(&cl, tr);
474         COMPARE(tr->responseCode(), 200);
475         COMPARE(tr->responseLength(), body2Size);
476         COMPARE(tr->bodyData, string(body2, body2Size));
477     }
478     
479     {
480         cl.setProxy("localhost", 2000, "ABCDEF");
481         TestRequest* tr = new TestRequest("http://www.google.com/test3");
482         HTTP::Request_ptr own(tr);
483         cl.makeRequest(tr);
484         waitForComplete(&cl, tr);
485         COMPARE(tr->responseCode(), 200);
486         COMPARE(tr->responseBytesReceived(), body2Size);
487         COMPARE(tr->bodyData, string(body2, body2Size));
488     }
489     
490 // pipelining
491     {
492         cl.setProxy("", 80);
493         TestRequest* tr = new TestRequest("http://localhost:2000/test1");
494         HTTP::Request_ptr own(tr);
495         cl.makeRequest(tr);
496         
497         
498         TestRequest* tr2 = new TestRequest("http://localhost:2000/test1");
499         HTTP::Request_ptr own2(tr2);
500         cl.makeRequest(tr2);
501         
502         TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
503         HTTP::Request_ptr own3(tr3);
504         cl.makeRequest(tr3);
505         
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));
512     }
513     
514 // multiple requests with an HTTP 1.0 server
515     {
516         cout << "http 1.0 multiple requests" << endl;
517         
518         cl.setProxy("", 80);
519         TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0/A");
520         HTTP::Request_ptr own(tr);
521         cl.makeRequest(tr);
522         
523         TestRequest* tr2 = new TestRequest("http://localhost:2000/test_1_0/B");
524         HTTP::Request_ptr own2(tr2);
525         cl.makeRequest(tr2);
526         
527         TestRequest* tr3 = new TestRequest("http://localhost:2000/test_1_0/C");
528         HTTP::Request_ptr own3(tr3);
529         cl.makeRequest(tr3);
530         
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));
537     }
538     
539     cout << "all tests passed ok" << endl;
540     return EXIT_SUCCESS;
541 }