source: box/trunk/test/httpserver/testhttpserver.cpp @ 2513

Revision 2513, 15.6 KB checked in by chris, 3 years ago (diff)

Fix httpserver tests on win32.

Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    testhttpserver.cpp
5//              Purpose: Test code for HTTP server class
6//              Created: 26/3/04
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <algorithm>
13#include <cstdio>
14#include <cstring>
15#include <ctime>
16
17#ifdef HAVE_SIGNAL_H
18        #include <signal.h>
19#endif
20
21#include <openssl/hmac.h>
22
23#include "autogen_HTTPException.h"
24#include "HTTPRequest.h"
25#include "HTTPResponse.h"
26#include "HTTPServer.h"
27#include "IOStreamGetLine.h"
28#include "S3Client.h"
29#include "S3Simulator.h"
30#include "ServerControl.h"
31#include "Test.h"
32#include "decode.h"
33#include "encode.h"
34
35#include "MemLeakFindOn.h"
36
37class TestWebServer : public HTTPServer
38{
39public:
40        TestWebServer();
41        ~TestWebServer();
42
43        virtual void Handle(HTTPRequest &rRequest, HTTPResponse &rResponse);
44
45};
46
47// Build a nice HTML response, so this can also be tested neatly in a browser
48void TestWebServer::Handle(HTTPRequest &rRequest, HTTPResponse &rResponse)
49{
50        // Test redirection mechanism
51        if(rRequest.GetRequestURI() == "/redirect")
52        {
53                rResponse.SetAsRedirect("/redirected");
54                return;
55        }
56
57        // Set a cookie?
58        if(rRequest.GetRequestURI() == "/set-cookie")
59        {
60                rResponse.SetCookie("SetByServer", "Value1");
61        }
62
63        #define DEFAULT_RESPONSE_1 "<html>\n<head><title>TEST SERVER RESPONSE</title></head>\n<body><h1>Test response</h1>\n<p><b>URI:</b> "
64        #define DEFAULT_RESPONSE_3 "</p>\n<p><b>Query string:</b> "
65        #define DEFAULT_RESPONSE_4 "</p>\n<p><b>Method:</b> "
66        #define DEFAULT_RESPONSE_5 "</p>\n<p><b>Decoded query:</b><br>"
67        #define DEFAULT_RESPONSE_6 "</p>\n<p><b>Content type:</b> "
68        #define DEFAULT_RESPONSE_7 "</p>\n<p><b>Content length:</b> "
69        #define DEFAULT_RESPONSE_8 "</p>\n<p><b>Cookies:</b><br>\n"
70        #define DEFAULT_RESPONSE_2 "</p>\n</body>\n</html>\n"
71
72        rResponse.SetResponseCode(HTTPResponse::Code_OK);
73        rResponse.SetContentType("text/html");
74        rResponse.Write(DEFAULT_RESPONSE_1, sizeof(DEFAULT_RESPONSE_1) - 1);
75        const std::string &ruri(rRequest.GetRequestURI());
76        rResponse.Write(ruri.c_str(), ruri.size());
77        rResponse.Write(DEFAULT_RESPONSE_3, sizeof(DEFAULT_RESPONSE_3) - 1);
78        const std::string &rquery(rRequest.GetQueryString());
79        rResponse.Write(rquery.c_str(), rquery.size());
80        rResponse.Write(DEFAULT_RESPONSE_4, sizeof(DEFAULT_RESPONSE_4) - 1);
81        {
82                const char *m = "????";
83                switch(rRequest.GetMethod())
84                {
85                case HTTPRequest::Method_GET: m = "GET "; break;
86                case HTTPRequest::Method_HEAD: m = "HEAD"; break;
87                case HTTPRequest::Method_POST: m = "POST"; break;
88                default: m = "UNKNOWN";
89                }
90                rResponse.Write(m, 4);
91        }
92        rResponse.Write(DEFAULT_RESPONSE_5, sizeof(DEFAULT_RESPONSE_5) - 1);
93        {
94                const HTTPRequest::Query_t &rquery(rRequest.GetQuery());
95                for(HTTPRequest::Query_t::const_iterator i(rquery.begin()); i != rquery.end(); ++i)
96                {
97                        rResponse.Write("\nPARAM:", 7);
98                        rResponse.Write(i->first.c_str(), i->first.size());
99                        rResponse.Write("=", 1);
100                        rResponse.Write(i->second.c_str(), i->second.size());
101                        rResponse.Write("<br>\n", 4);
102                }
103        }
104        rResponse.Write(DEFAULT_RESPONSE_6, sizeof(DEFAULT_RESPONSE_6) - 1);
105        const std::string &rctype(rRequest.GetContentType());
106        rResponse.Write(rctype.c_str(), rctype.size());
107        rResponse.Write(DEFAULT_RESPONSE_7, sizeof(DEFAULT_RESPONSE_7) - 1);
108        {
109                char l[32];
110                rResponse.Write(l, ::sprintf(l, "%d", rRequest.GetContentLength()));
111        }
112        rResponse.Write(DEFAULT_RESPONSE_8, sizeof(DEFAULT_RESPONSE_8) - 1);
113        const HTTPRequest::CookieJar_t *pcookies = rRequest.GetCookies();
114        if(pcookies != 0)
115        {
116                HTTPRequest::CookieJar_t::const_iterator i(pcookies->begin());
117                for(; i != pcookies->end(); ++i)
118                {
119                        char t[512];
120                        rResponse.Write(t, ::sprintf(t, "COOKIE:%s=%s<br>\n", i->first.c_str(), i->second.c_str()));
121                }
122        }
123        rResponse.Write(DEFAULT_RESPONSE_2, sizeof(DEFAULT_RESPONSE_2) - 1);
124}
125
126TestWebServer::TestWebServer() {}
127TestWebServer::~TestWebServer() {}
128
129int test(int argc, const char *argv[])
130{
131        if(argc >= 2 && ::strcmp(argv[1], "server") == 0)
132        {
133                // Run a server
134                TestWebServer server;
135                return server.Main("doesnotexist", argc - 1, argv + 1);
136        }
137       
138        if(argc >= 2 && ::strcmp(argv[1], "s3server") == 0)
139        {
140                // Run a server
141                S3Simulator server;
142                return server.Main("doesnotexist", argc - 1, argv + 1);
143        }
144       
145        // Start the server
146        int pid = LaunchServer("./test server testfiles/httpserver.conf", "testfiles/httpserver.pid");
147        TEST_THAT(pid != -1 && pid != 0);
148        if(pid <= 0)
149        {
150                return 0;
151        }
152
153        // Run the request script
154        TEST_THAT(::system("perl testfiles/testrequests.pl") == 0);
155
156        #ifndef WIN32
157        signal(SIGPIPE, SIG_IGN);
158        #endif
159
160        SocketStream sock;
161        sock.Open(Socket::TypeINET, "localhost", 1080);
162
163        for (int i = 0; i < 4; i++)
164        {
165                HTTPRequest request(HTTPRequest::Method_GET,
166                        "/test-one/34/341s/234?p1=vOne&p2=vTwo");
167
168                if (i < 2)
169                {
170                        // first set of passes has keepalive off by default,
171                        // so when i == 1 the socket has already been closed
172                        // by the server, and we'll get -EPIPE when we try
173                        // to send the request.
174                        request.SetClientKeepAliveRequested(false);
175                }
176                else
177                {
178                        request.SetClientKeepAliveRequested(true);
179                }
180
181                if (i == 1)
182                {
183                        sleep(1); // need time for our process to realise
184                        // that the peer has died, otherwise no SIGPIPE :(
185                        TEST_CHECK_THROWS(request.Send(sock,
186                                IOStream::TimeOutInfinite),
187                                ConnectionException, SocketWriteError);
188                        sock.Close();
189                        sock.Open(Socket::TypeINET, "localhost", 1080);
190                        continue;
191                }
192                else
193                {
194                        request.Send(sock, IOStream::TimeOutInfinite);
195                }
196
197                HTTPResponse response;
198                response.Receive(sock);
199               
200                TEST_THAT(response.GetResponseCode() == HTTPResponse::Code_OK);
201                TEST_THAT(response.GetContentType() == "text/html");
202
203                IOStreamGetLine getline(response);
204                std::string line;
205
206                TEST_THAT(getline.GetLine(line));
207                TEST_EQUAL("<html>", line);
208                TEST_THAT(getline.GetLine(line));
209                TEST_EQUAL("<head><title>TEST SERVER RESPONSE</title></head>",
210                        line);
211                TEST_THAT(getline.GetLine(line));
212                TEST_EQUAL("<body><h1>Test response</h1>", line);
213                TEST_THAT(getline.GetLine(line));
214                TEST_EQUAL("<p><b>URI:</b> /test-one/34/341s/234</p>", line);
215                TEST_THAT(getline.GetLine(line));
216                TEST_EQUAL("<p><b>Query string:</b> p1=vOne&p2=vTwo</p>", line);
217                TEST_THAT(getline.GetLine(line));
218                TEST_EQUAL("<p><b>Method:</b> GET </p>", line);
219                TEST_THAT(getline.GetLine(line));
220                TEST_EQUAL("<p><b>Decoded query:</b><br>", line);
221                TEST_THAT(getline.GetLine(line));
222                TEST_EQUAL("PARAM:p1=vOne<br>", line);
223                TEST_THAT(getline.GetLine(line));
224                TEST_EQUAL("PARAM:p2=vTwo<br></p>", line);
225                TEST_THAT(getline.GetLine(line));
226                TEST_EQUAL("<p><b>Content type:</b> </p>", line);
227                TEST_THAT(getline.GetLine(line));
228                TEST_EQUAL("<p><b>Content length:</b> -1</p>", line);
229                TEST_THAT(getline.GetLine(line));
230                TEST_EQUAL("<p><b>Cookies:</b><br>", line);
231                TEST_THAT(getline.GetLine(line));
232                TEST_EQUAL("</p>", line);
233                TEST_THAT(getline.GetLine(line));
234                TEST_EQUAL("</body>", line);
235                TEST_THAT(getline.GetLine(line));
236                TEST_EQUAL("</html>", line);
237        }
238       
239        // Kill it
240        TEST_THAT(KillServer(pid));
241
242        #ifdef WIN32
243                TEST_THAT(unlink("testfiles/httpserver.pid") == 0);
244        #else
245                TestRemoteProcessMemLeaks("generic-httpserver.memleaks");
246        #endif
247
248        // correct, official signature should succeed, with lower-case header
249        {
250                // http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAuthentication.html
251                HTTPRequest request(HTTPRequest::Method_GET, "/photos/puppy.jpg");
252                request.SetHostName("johnsmith.s3.amazonaws.com");
253                request.AddHeader("date", "Tue, 27 Mar 2007 19:36:42 +0000");
254                request.AddHeader("authorization",
255                        "AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA=");
256               
257                S3Simulator simulator;
258                simulator.Configure("testfiles/s3simulator.conf");
259               
260                CollectInBufferStream response_buffer;
261                HTTPResponse response(&response_buffer);
262               
263                simulator.Handle(request, response);
264                TEST_EQUAL(200, response.GetResponseCode());
265               
266                std::string response_data((const char *)response.GetBuffer(),
267                        response.GetSize());
268                TEST_EQUAL("omgpuppies!\n", response_data);
269        }
270
271        // modified signature should fail
272        {
273                // http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAuthentication.html
274                HTTPRequest request(HTTPRequest::Method_GET, "/photos/puppy.jpg");
275                request.SetHostName("johnsmith.s3.amazonaws.com");
276                request.AddHeader("date", "Tue, 27 Mar 2007 19:36:42 +0000");
277                request.AddHeader("authorization",
278                        "AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbB=");
279               
280                S3Simulator simulator;
281                simulator.Configure("testfiles/s3simulator.conf");
282               
283                CollectInBufferStream response_buffer;
284                HTTPResponse response(&response_buffer);
285               
286                simulator.Handle(request, response);
287                TEST_EQUAL(401, response.GetResponseCode());
288               
289                std::string response_data((const char *)response.GetBuffer(),
290                        response.GetSize());
291                TEST_EQUAL("<html><head>"
292                        "<title>Internal Server Error</title></head>\n"
293                        "<h1>Internal Server Error</h1>\n"
294                        "<p>An error, type Authentication Failed occured "
295                        "when processing the request.</p>"
296                        "<p>Please try again later.</p></body>\n"
297                        "</html>\n", response_data);
298        }
299
300        // S3Client tests
301        {
302                S3Simulator simulator;
303                simulator.Configure("testfiles/s3simulator.conf");
304                S3Client client(&simulator, "johnsmith.s3.amazonaws.com",
305                        "0PN5J17HBGZHT7JJ3X82",
306                        "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o");
307               
308                HTTPResponse response = client.GetObject("/photos/puppy.jpg");
309                TEST_EQUAL(200, response.GetResponseCode());
310                std::string response_data((const char *)response.GetBuffer(),
311                        response.GetSize());
312                TEST_EQUAL("omgpuppies!\n", response_data);
313
314                // make sure that assigning to HTTPResponse does clear stream
315                response = client.GetObject("/photos/puppy.jpg");
316                TEST_EQUAL(200, response.GetResponseCode());
317                response_data = std::string((const char *)response.GetBuffer(),
318                        response.GetSize());
319                TEST_EQUAL("omgpuppies!\n", response_data);
320
321                response = client.GetObject("/nonexist");
322                TEST_EQUAL(404, response.GetResponseCode());
323               
324                FileStream fs("testfiles/testrequests.pl");
325                response = client.PutObject("/newfile", fs);
326                TEST_EQUAL(200, response.GetResponseCode());
327
328                response = client.GetObject("/newfile");
329                TEST_EQUAL(200, response.GetResponseCode());
330                TEST_THAT(fs.CompareWith(response));
331                TEST_EQUAL(0, ::unlink("testfiles/newfile"));
332        }
333
334        {
335                HTTPRequest request(HTTPRequest::Method_PUT,
336                        "/newfile");
337                request.SetHostName("quotes.s3.amazonaws.com");
338                request.AddHeader("date", "Wed, 01 Mar  2006 12:00:00 GMT");
339                request.AddHeader("authorization", "AWS 0PN5J17HBGZHT7JJ3X82:XtMYZf0hdOo4TdPYQknZk0Lz7rw=");
340                request.AddHeader("Content-Type", "text/plain");
341               
342                FileStream fs("testfiles/testrequests.pl");
343                fs.CopyStreamTo(request);
344                request.SetForReading();
345
346                CollectInBufferStream response_buffer;
347                HTTPResponse response(&response_buffer);
348               
349                S3Simulator simulator;
350                simulator.Configure("testfiles/s3simulator.conf");
351                simulator.Handle(request, response);
352               
353                TEST_EQUAL(200, response.GetResponseCode());
354                TEST_EQUAL("LriYPLdmOdAiIfgSm/F1YsViT1LW94/xUQxMsF7xiEb1a0wiIOIxl+zbwZ163pt7", response.GetHeaderValue("x-amz-id-2"));
355                TEST_EQUAL("F2A8CCCA26B4B26D", response.GetHeaderValue("x-amz-request-id"));
356                TEST_EQUAL("Wed, 01 Mar  2006 12:00:00 GMT", response.GetHeaderValue("Date"));
357                TEST_EQUAL("Sun, 1 Jan 2006 12:00:00 GMT", response.GetHeaderValue("Last-Modified"));
358                TEST_EQUAL("\"828ef3fdfa96f00ad9f27c383fc9ac7f\"", response.GetHeaderValue("ETag"));
359                TEST_EQUAL("", response.GetContentType());
360                TEST_EQUAL("AmazonS3", response.GetHeaderValue("Server"));
361                TEST_EQUAL(0, response.GetSize());
362
363                FileStream f1("testfiles/testrequests.pl");
364                FileStream f2("testfiles/newfile");
365                TEST_THAT(f1.CompareWith(f2));
366                TEST_EQUAL(0, ::unlink("testfiles/newfile"));
367        }
368
369        // Start the S3Simulator server
370        pid = LaunchServer("./test s3server testfiles/s3simulator.conf",
371                "testfiles/s3simulator.pid");
372        TEST_THAT(pid != -1 && pid != 0);
373        if(pid <= 0)
374        {
375                return 0;
376        }
377
378        sock.Close();
379        sock.Open(Socket::TypeINET, "localhost", 1080);
380
381        {
382                HTTPRequest request(HTTPRequest::Method_GET, "/nonexist");
383                request.SetHostName("quotes.s3.amazonaws.com");
384                request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
385                request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:0cSX/YPdtXua1aFFpYmH1tc0ajA=");
386                request.SetClientKeepAliveRequested(true);
387                request.Send(sock, IOStream::TimeOutInfinite);
388
389                HTTPResponse response;
390                response.Receive(sock);
391                std::string value;
392                TEST_EQUAL(404, response.GetResponseCode());
393        }
394
395        #ifndef WIN32 // much harder to make files inaccessible on WIN32
396        // Make file inaccessible, should cause server to return a 403 error,
397        // unless of course the test is run as root :)
398        {
399                TEST_THAT(chmod("testfiles/testrequests.pl", 0) == 0);
400                HTTPRequest request(HTTPRequest::Method_GET,
401                        "/testrequests.pl");
402                request.SetHostName("quotes.s3.amazonaws.com");
403                request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
404                request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:qc1e8u8TVl2BpIxwZwsursIb8U8=");
405                request.SetClientKeepAliveRequested(true);
406                request.Send(sock, IOStream::TimeOutInfinite);
407
408                HTTPResponse response;
409                response.Receive(sock);
410                std::string value;
411                TEST_EQUAL(403, response.GetResponseCode());
412                TEST_THAT(chmod("testfiles/testrequests.pl", 0755) == 0);
413        }
414        #endif
415
416        {
417                HTTPRequest request(HTTPRequest::Method_GET,
418                        "/testrequests.pl");
419                request.SetHostName("quotes.s3.amazonaws.com");
420                request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
421                request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:qc1e8u8TVl2BpIxwZwsursIb8U8=");
422                request.SetClientKeepAliveRequested(true);
423                request.Send(sock, IOStream::TimeOutInfinite);
424
425                HTTPResponse response;
426                response.Receive(sock);
427                std::string value;
428                TEST_EQUAL(200, response.GetResponseCode());
429                TEST_EQUAL("qBmKRcEWBBhH6XAqsKU/eg24V3jf/kWKN9dJip1L/FpbYr9FDy7wWFurfdQOEMcY", response.GetHeaderValue("x-amz-id-2"));
430                TEST_EQUAL("F2A8CCCA26B4B26D", response.GetHeaderValue("x-amz-request-id"));
431                TEST_EQUAL("Wed, 01 Mar  2006 12:00:00 GMT", response.GetHeaderValue("Date"));
432                TEST_EQUAL("Sun, 1 Jan 2006 12:00:00 GMT", response.GetHeaderValue("Last-Modified"));
433                TEST_EQUAL("\"828ef3fdfa96f00ad9f27c383fc9ac7f\"", response.GetHeaderValue("ETag"));
434                TEST_EQUAL("text/plain", response.GetContentType());
435                TEST_EQUAL("AmazonS3", response.GetHeaderValue("Server"));
436
437                FileStream file("testfiles/testrequests.pl");
438                TEST_THAT(file.CompareWith(response));
439        }
440
441        {
442                HTTPRequest request(HTTPRequest::Method_PUT,
443                        "/newfile");
444                request.SetHostName("quotes.s3.amazonaws.com");
445                request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
446                request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:kfY1m6V3zTufRy2kj92FpQGKz4M=");
447                request.AddHeader("Content-Type", "text/plain");
448                FileStream fs("testfiles/testrequests.pl");
449                HTTPResponse response;
450                request.SendWithStream(sock,
451                        IOStream::TimeOutInfinite /* or 10000 milliseconds */,
452                        &fs, response);
453                std::string value;
454                TEST_EQUAL(200, response.GetResponseCode());
455                TEST_EQUAL("LriYPLdmOdAiIfgSm/F1YsViT1LW94/xUQxMsF7xiEb1a0wiIOIxl+zbwZ163pt7", response.GetHeaderValue("x-amz-id-2"));
456                TEST_EQUAL("F2A8CCCA26B4B26D", response.GetHeaderValue("x-amz-request-id"));
457                TEST_EQUAL("Wed, 01 Mar  2006 12:00:00 GMT", response.GetHeaderValue("Date"));
458                TEST_EQUAL("Sun, 1 Jan 2006 12:00:00 GMT", response.GetHeaderValue("Last-Modified"));
459                TEST_EQUAL("\"828ef3fdfa96f00ad9f27c383fc9ac7f\"", response.GetHeaderValue("ETag"));
460                TEST_EQUAL("", response.GetContentType());
461                TEST_EQUAL("AmazonS3", response.GetHeaderValue("Server"));
462                TEST_EQUAL(0, response.GetSize());
463
464                FileStream f1("testfiles/testrequests.pl");
465                FileStream f2("testfiles/newfile");
466                TEST_THAT(f1.CompareWith(f2));
467        }
468
469        // Kill it
470        TEST_THAT(KillServer(pid));
471
472        #ifdef WIN32
473                TEST_THAT(unlink("testfiles/s3simulator.pid") == 0);
474        #else
475                TestRemoteProcessMemLeaks("generic-httpserver.memleaks");
476        #endif
477
478        return 0;
479}
480
Note: See TracBrowser for help on using the repository browser.