]> git.mxchange.org Git - simgear.git/blob - simgear/io/test_HTTP.cxx
Further HTTP improvements, wget-alike test program to check it all behaves!
[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     string bodyData;
47     
48     TestRequest(const std::string& url) : 
49         HTTP::Request(url),
50         complete(false)
51     {
52         
53     }
54     
55 protected:
56     virtual void responseHeadersComplete()
57     {
58     }
59     
60     virtual void responseComplete()
61     {
62         complete = true;
63     }  
64     
65     virtual void gotBodyData(const char* s, int n)
66     {
67         bodyData += string(s, n);
68     }
69 };
70
71 class TestServerChannel : public NetChat
72 {
73 public:  
74     enum State
75     {
76         STATE_IDLE = 0,
77         STATE_HEADERS,
78         STATE_REQUEST_BODY
79     };
80     
81     TestServerChannel()
82     {
83         state = STATE_IDLE;
84         setTerminator("\r\n");
85     }
86     
87     virtual void collectIncomingData(const char* s, int n)
88     {
89         buffer += string(s, n);
90     }
91     
92     virtual void foundTerminator(void)
93     {
94         if (state == STATE_IDLE) {
95             state = STATE_HEADERS;
96             string_list line = strutils::split(buffer, NULL, 3);
97             if (line.size() < 4) {
98                 cerr << "malformed request:" << buffer << endl;
99                 exit(-1);
100             }
101             
102             method = line[0];
103             path = line[1];
104             httpVersion = line[2];
105             userAgent = line[3];
106             requestHeaders.clear();
107             buffer.clear();
108         } else if (state == STATE_HEADERS) {
109             string s = strutils::simplify(buffer);
110             if (s.empty()) {
111                 buffer.clear();
112                 receivedRequestHeaders();
113                 return;
114             }
115             
116             int colonPos = buffer.find(':');
117             if (colonPos < 0) {
118                 cerr << "malformed HTTP response header:" << buffer << endl;
119                 buffer.clear();
120                 return;
121             }
122
123             string key = strutils::simplify(buffer.substr(0, colonPos));
124             string lkey = boost::to_lower_copy(key);
125             string value = strutils::strip(buffer.substr(colonPos + 1));
126             requestHeaders[lkey] = value;
127             buffer.clear();
128         } else if (state == STATE_REQUEST_BODY) {
129             
130         }
131     }  
132     
133     void receivedRequestHeaders()
134     {
135         state = STATE_IDLE;
136         if (path == "/test1") {
137             string contentStr(BODY1);
138             stringstream d;
139             d << "HTTP1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
140             d << "Content-Length:" << contentStr.size() << "\r\n";
141             d << "\r\n"; // final CRLF to terminate the headers
142             d << contentStr;
143             push(d.str().c_str());
144         } else if (path == "/test2") {
145             sendBody2();
146         } else if (path == "http://www.google.com/test2") {
147             // proxy test
148             if (requestHeaders["host"] != "www.google.com") {
149                 sendErrorResponse(400);
150             }
151             
152             if (requestHeaders["proxy-authorization"] != string()) {
153                 sendErrorResponse(401); // shouldn't supply auth
154             }
155             
156             sendBody2();
157         } else if (path == "http://www.google.com/test3") {
158             // proxy test
159             if (requestHeaders["host"] != "www.google.com") {
160                 sendErrorResponse(400);
161             }
162
163             if (requestHeaders["proxy-authorization"] != "ABCDEF") {
164                 sendErrorResponse(401); // forbidden
165             }
166
167             sendBody2();
168         } else {
169             sendErrorResponse(404);
170         }
171     }
172     
173     void sendBody2()
174     {
175         stringstream d;
176         d << "HTTP1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
177         d << "Content-Length:" << body2Size << "\r\n";
178         d << "\r\n"; // final CRLF to terminate the headers
179         push(d.str().c_str());
180         bufferSend(body2, body2Size);
181     }
182     
183     void sendErrorResponse(int code)
184     {
185         cerr << "sending error " << code << " for " << path << endl;
186         stringstream headerData;
187         headerData << "HTTP1.1 " << code << " " << reasonForCode(code) << "\r\n";
188         headerData << "\r\n"; // final CRLF to terminate the headers
189         push(headerData.str().c_str());
190     }
191     
192     string reasonForCode(int code) 
193     {
194         switch (code) {
195             case 200: return "OK";
196             case 404: return "not found";
197             default: return "unknown code";
198         }
199     }
200     
201     State state;
202     string buffer;
203     string method;
204     string path;
205     string httpVersion;
206     string userAgent;
207     std::map<string, string> requestHeaders;
208 };
209
210 class TestServer : public NetChannel
211 {
212 public:   
213     TestServer()
214     {
215         open();
216         bind(NULL, 2000); // localhost, any port
217         listen(5);
218     }
219     
220     virtual ~TestServer()
221     {    
222     }
223     
224     virtual bool writable (void) { return false ; }
225
226     virtual void handleAccept (void)
227     {
228         simgear::IPAddress addr ;
229         int handle = accept ( &addr ) ;
230
231         TestServerChannel* chan = new TestServerChannel();
232         chan->setHandle(handle);
233     }
234 };
235
236 void waitForComplete(TestRequest* tr)
237 {
238     SGTimeStamp start(SGTimeStamp::now());
239     while (start.elapsedMSec() <  1000) {
240         NetChannel::poll(10);
241         if (tr->complete) {
242             return;
243         }
244     }
245     
246     cerr << "timed out" << endl;
247 }
248
249 int main(int argc, char* argv[])
250 {
251     TestServer s;
252     
253     HTTP::Client cl;
254
255 // test URL parsing
256     TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
257     COMPARE(tr1->scheme(), "http");
258     COMPARE(tr1->hostAndPort(), "localhost.woo.zar:2000");
259     COMPARE(tr1->host(), "localhost.woo.zar");
260     COMPARE(tr1->port(), 2000);
261     COMPARE(tr1->path(), "/test1");
262     
263     TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
264     COMPARE(tr2->scheme(), "http");
265     COMPARE(tr2->hostAndPort(), "192.168.1.1");
266     COMPARE(tr2->host(), "192.168.1.1");
267     COMPARE(tr2->port(), 80);
268     COMPARE(tr2->path(), "/test1/dir/thing/file.png");
269     
270 // basic get request
271     {
272         TestRequest* tr = new TestRequest("http://localhost:2000/test1");
273         HTTP::Request_ptr own(tr);
274         cl.makeRequest(tr);
275
276         waitForComplete(tr);
277         COMPARE(tr->responseCode(), 200);
278         COMPARE(tr->responseReason(), string("OK"));
279         COMPARE(tr->contentLength(), strlen(BODY1));
280         COMPARE(tr->bodyData, string(BODY1));
281     }
282
283 // larger get request
284     for (unsigned int i=0; i<body2Size; ++i) {
285         body2[i] = (i << 4) | (i >> 2);
286     }
287     
288     {
289         TestRequest* tr = new TestRequest("http://localhost:2000/test2");
290         HTTP::Request_ptr own(tr);
291         cl.makeRequest(tr);
292         waitForComplete(tr);
293         COMPARE(tr->responseCode(), 200);
294         COMPARE(tr->contentLength(), body2Size);
295         COMPARE(tr->bodyData, string(body2, body2Size));
296     }
297     
298 // test 404
299     {
300         TestRequest* tr = new TestRequest("http://localhost:2000/not-found");
301         HTTP::Request_ptr own(tr);
302         cl.makeRequest(tr);
303         waitForComplete(tr);
304         COMPARE(tr->responseCode(), 404);
305         COMPARE(tr->responseReason(), string("not found"));
306         COMPARE(tr->contentLength(), 0);
307     }
308     
309 // test proxy
310     {
311         cl.setProxy("localhost:2000");
312         TestRequest* tr = new TestRequest("http://www.google.com/test2");
313         HTTP::Request_ptr own(tr);
314         cl.makeRequest(tr);
315         waitForComplete(tr);
316         COMPARE(tr->responseCode(), 200);
317         COMPARE(tr->contentLength(), body2Size);
318         COMPARE(tr->bodyData, string(body2, body2Size));
319     }
320     
321     {
322         cl.setProxy("localhost:2000", "ABCDEF");
323         TestRequest* tr = new TestRequest("http://www.google.com/test3");
324         HTTP::Request_ptr own(tr);
325         cl.makeRequest(tr);
326         waitForComplete(tr);
327         COMPARE(tr->responseCode(), 200);
328         COMPARE(tr->contentLength(), body2Size);
329         COMPARE(tr->bodyData, string(body2, body2Size));
330     }
331     
332     cout << "all tests passed ok" << endl;
333     return EXIT_SUCCESS;
334 }