1 /*****************************************************************
2 |
3 |   Platinum - HTTP tests
4 |
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
6 | All rights reserved.
7 | http://www.plutinosoft.com
8 |
9 | This program is free software; you can redistribute it and/or
10 | modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.
13 |
14 | OEMs, ISVs, VARs and other distributors that combine and
15 | distribute commercially licensed software with Platinum software
16 | and do not wish to distribute the source code for the commercially
17 | licensed software under version 2, or (at your option) any later
18 | version, of the GNU General Public License (the "GPL") must enter
19 | into a commercial license agreement with Plutinosoft, LLC.
20 | licensing@plutinosoft.com
21 |
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 | GNU General Public License for more details.
26 |
27 | You should have received a copy of the GNU General Public License
28 | along with this program; see the file LICENSE.txt. If not, write to
29 | the Free Software Foundation, Inc.,
30 | 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
31 | http://www.gnu.org/licenses/gpl-2.0.html
32 |
33 ****************************************************************/
34 
35 /*----------------------------------------------------------------------
36 |   includes
37 +---------------------------------------------------------------------*/
38 #include "Neptune.h"
39 #include "PltTaskManager.h"
40 #include "PltHttpServer.h"
41 #include "PltDownloader.h"
42 #include "PltRingBufferStream.h"
43 
44 #include <stdio.h>
45 #include <string.h>
46 #include <stdlib.h>
47 
48 NPT_SET_LOCAL_LOGGER("platinum.core.http.test")
49 
50 //#define TEST1
51 //#define TEST2
52 //#define TEST3
53 //#define TEST4
54 #define TEST5
55 
56 /*----------------------------------------------------------------------
57 |   globals
58 +---------------------------------------------------------------------*/
59 struct Options {
60     NPT_UInt32  port;
61     NPT_String  path;
62 } Options;
63 
64 /*----------------------------------------------------------------------
65 |   PLT_HttpCustomRequestHandler
66 +---------------------------------------------------------------------*/
67 class PLT_HttpCustomRequestHandler : public NPT_HttpRequestHandler
68 {
69 public:
70     // constructors
PLT_HttpCustomRequestHandler(NPT_InputStreamReference & body,const char * mime_type,bool update_content_length=false)71     PLT_HttpCustomRequestHandler(NPT_InputStreamReference& body,
72                                  const char*               mime_type,
73                                  bool                      update_content_length = false) :
74         m_Body(body),
75         m_MimeType(mime_type),
76         m_UpdateContentLength(update_content_length) {}
77 
78     // NPT_HttpRequetsHandler methods
SetupResponse(NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)79     virtual NPT_Result SetupResponse(NPT_HttpRequest&              request,
80                                      const NPT_HttpRequestContext& context,
81                                      NPT_HttpResponse&             response) {
82         NPT_COMPILER_UNUSED(request);
83         NPT_COMPILER_UNUSED(context);
84 
85         NPT_HttpEntity* entity = response.GetEntity();
86         if (entity == NULL) return NPT_ERROR_INVALID_STATE;
87 
88         entity->SetContentType(m_MimeType);
89         entity->SetInputStream(m_Body, m_UpdateContentLength);
90 
91         return NPT_SUCCESS;
92     }
93 
94 private:
95     NPT_InputStreamReference m_Body;
96     NPT_String               m_MimeType;
97     bool                     m_UpdateContentLength;
98 };
99 
100 
101 #ifdef TEST1
102 /*----------------------------------------------------------------------
103 |   Test1
104 +---------------------------------------------------------------------*/
105 static bool
Test1(PLT_TaskManager * task_manager,NPT_HttpUrl url,NPT_Size & size)106 Test1(PLT_TaskManager* task_manager, NPT_HttpUrl url, NPT_Size& size)
107 {
108     NPT_LOG_INFO("########### TEST 1 ######################");
109 
110     NPT_MemoryStreamReference memory_stream(new NPT_MemoryStream());
111     NPT_OutputStreamReference output_stream(memory_stream);
112     PLT_Downloader downloader(task_manager, url, output_stream);
113     downloader.Start();
114 
115     while (1) {
116         switch(downloader.GetState()) {
117             case PLT_DOWNLOADER_SUCCESS: {
118                 size = memory_stream->GetDataSize();
119                 return true;
120             }
121 
122             case PLT_DOWNLOADER_ERROR:
123                 return false;
124 
125             default:
126                 NPT_System::Sleep(NPT_TimeInterval(.1f));
127                 break;
128         }
129     };
130 
131     return false;
132 }
133 #endif
134 
135 #ifdef TEST2
136 /*----------------------------------------------------------------------
137 |   DumpBody
138 +---------------------------------------------------------------------*/
139 static NPT_Result
ReadBody(PLT_Downloader & downloader,NPT_InputStreamReference & stream,NPT_Size & size)140 ReadBody(PLT_Downloader& downloader, NPT_InputStreamReference& stream, NPT_Size& size)
141 {
142     NPT_LargeSize avail;
143     char buffer[2048];
144     NPT_Result ret = NPT_ERROR_WOULD_BLOCK;
145 
146     /* reset output param first */
147     size = 0;
148 
149     /*
150        we test for availability first to avoid
151        getting stuck in Read forever in case blocking is true
152        and the download is done writing to the stream
153     */
154     NPT_CHECK(stream->GetAvailable(avail));
155 
156     if (avail) {
157          ret = stream->Read(buffer, 2048, &size);
158          NPT_LOG_FINER_2("Read %d bytes (result = %d)\n", size, ret);
159          return ret;
160      } else {
161          Plt_DowloaderState state = downloader.GetState();
162          switch (state) {
163              case PLT_DOWNLOADER_ERROR:
164                  return NPT_FAILURE;
165 
166              case PLT_DOWNLOADER_SUCCESS:
167                  /* no more data expected */
168                  return NPT_ERROR_EOS;
169 
170              default:
171                  NPT_System::Sleep(NPT_TimeInterval(.1f));
172                  break;
173          }
174      }
175 
176      return NPT_SUCCESS;
177 }
178 
179 /*----------------------------------------------------------------------
180 |   Test2
181 +---------------------------------------------------------------------*/
182 static bool
Test2(PLT_TaskManager * task_manager,NPT_HttpUrl url,NPT_Size & size)183 Test2(PLT_TaskManager* task_manager, NPT_HttpUrl url, NPT_Size& size)
184 {
185     NPT_LOG_INFO("########### TEST 2 ######################");
186 
187     /* reset output param first */
188     size = 0;
189 
190     PLT_RingBufferStreamReference ringbuffer_stream(new PLT_RingBufferStream());
191     NPT_OutputStreamReference output_stream(ringbuffer_stream);
192     NPT_InputStreamReference  input_stream(ringbuffer_stream);
193     PLT_Downloader downloader(task_manager, url, output_stream);
194     downloader.Start();
195 
196     while (1) {
197         switch(downloader.GetState()) {
198             case PLT_DOWNLOADER_SUCCESS:
199                 ringbuffer_stream->SetEOS();
200                 /* fallthrough */
201 
202             case PLT_DOWNLOADER_DOWNLOADING: {
203                     NPT_Size bytes_read;
204                     NPT_Result res = ReadBody(downloader, input_stream, bytes_read);
205                     if (NPT_FAILED(res)) {
206                         return (res==NPT_ERROR_EOS)?true:false;
207                     }
208                     size += bytes_read;
209                 }
210                 break;
211 
212             case PLT_DOWNLOADER_ERROR:
213                 return false;
214 
215             default:
216                 NPT_System::Sleep(NPT_TimeInterval(.1f));
217                 break;
218         }
219     };
220 
221     return false;
222 }
223 #endif
224 
225 
226 #ifdef TEST3
227 class RingBufferWriterTask : public PLT_ThreadTask
228 {
229 public:
RingBufferWriterTask(PLT_RingBufferStreamReference & ringbuffer_stream)230     RingBufferWriterTask(PLT_RingBufferStreamReference& ringbuffer_stream)
231     : m_RingBufferStream(ringbuffer_stream) {}
232 
233     // PLT_ThreadTask methods
DoRun()234     virtual void DoRun() {
235         char buffer[32768];
236         m_RingBufferStream->WriteFully(buffer, 32768);
237 
238         /* mark as done */
239         m_RingBufferStream->SetEOS();
240     }
241 
242 private:
243     PLT_RingBufferStreamReference m_RingBufferStream;
244 };
245 
246 /*----------------------------------------------------------------------
247 |   Test3
248 +---------------------------------------------------------------------*/
249 static bool
Test3(PLT_TaskManager * task_manager,NPT_HttpUrl url,PLT_RingBufferStreamReference & ringbuffer_stream,NPT_Size & size)250 Test3(PLT_TaskManager* task_manager, NPT_HttpUrl url, PLT_RingBufferStreamReference& ringbuffer_stream, NPT_Size& size)
251 {
252     NPT_LOG_INFO("########### TEST 3 ######################");
253 
254     /* reset output param first */
255     size = 0;
256 
257     NPT_MemoryStreamReference memory_stream(new NPT_MemoryStream());
258     NPT_OutputStreamReference output_stream(memory_stream);
259     PLT_Downloader downloader(task_manager, url, output_stream);
260     downloader.Start();
261 
262     /* asynchronously write onto ring buffer stream */
263     task_manager->StartTask(new RingBufferWriterTask(ringbuffer_stream));
264 
265     /* start pulling data */
266     while (1) {
267         switch(downloader.GetState()) {
268             case PLT_DOWNLOADER_SUCCESS:
269                 size = memory_stream->GetDataSize();
270                 return true;
271 
272             case PLT_DOWNLOADER_ERROR:
273                 return false;
274 
275             default:
276                 NPT_System::Sleep(NPT_TimeInterval(.1f));
277                 break;
278         }
279     };
280 
281     return false;
282 }
283 #endif
284 
285 #ifdef TEST4
286 class ClientStuckTask : public PLT_ThreadTask
287 {
288 public:
ClientStuckTask(NPT_HttpUrl & url,NPT_HttpClient & client)289     ClientStuckTask(NPT_HttpUrl& url, NPT_HttpClient& client)
290     : m_Url(url), m_Client(client) {}
291 
292     // PLT_ThreadTask methods
DoRun()293     virtual void DoRun() {
294         NPT_HttpRequest request(m_Url, NPT_HTTP_METHOD_GET);
295         NPT_HttpResponse* response = NULL;
296         m_Client.SendRequest(request, response);
297     }
298 
DoAbort()299     virtual void DoAbort()   {
300         m_Client.Abort();
301     }
302 
303 private:
304     NPT_HttpUrl     m_Url;
305     NPT_HttpClient& m_Client;
306     bool            m_Aborted;
307 };
308 /*----------------------------------------------------------------------
309 |   Test4
310 +---------------------------------------------------------------------*/
311 static bool
Test4(PLT_TaskManager * task_manager,NPT_HttpUrl url,NPT_TimeInterval wait_before_kill)312 Test4(PLT_TaskManager* task_manager, NPT_HttpUrl url, NPT_TimeInterval wait_before_kill)
313 {
314     NPT_LOG_INFO("########### TEST 4 ######################");
315 
316     NPT_HttpClient client;
317 
318     /* start task to asynchronously fetch url */
319     ClientStuckTask* task = new ClientStuckTask(url, client);
320     task_manager->StartTask(task, NULL, false);
321 
322     /* wait a bit and abort client */
323     NPT_System::Sleep(wait_before_kill);
324 
325     task->Kill();
326     return true;
327 }
328 #endif
329 
330 #ifdef TEST5
331 /*----------------------------------------------------------------------
332 |   Test5
333 +---------------------------------------------------------------------*/
334 static bool
Test5(NPT_HttpUrl url)335 Test5(NPT_HttpUrl url)
336 {
337     NPT_LOG_INFO("########### TEST 5 ######################");
338 
339     NPT_HttpClient client;
340 
341     // first request
342     NPT_HttpRequest request(url, NPT_HTTP_METHOD_POST, NPT_HTTP_PROTOCOL_1_1);
343     NPT_HttpEntity* request_entity = new NPT_HttpEntity();
344     request_entity->SetInputStream("Testing");
345     request.SetEntity(request_entity);
346 
347     NPT_HttpResponse* response = NULL;
348     client.SendRequest(request, response);
349     NPT_HttpEntity* entity = NULL;
350     if (response && (entity = response->GetEntity())) {
351         NPT_DataBuffer buffer;
352         if (NPT_FAILED(entity->Load(buffer))) return false;
353     }
354 
355     // try again
356     delete response;
357     response = NULL;
358     request_entity = new NPT_HttpEntity();
359     request_entity->SetInputStream("Testing2");
360     request.SetEntity(request_entity);
361     client.SendRequest(request, response);
362     entity = NULL;
363     if (response && (entity = response->GetEntity())) {
364         NPT_DataBuffer buffer;
365         if (NPT_FAILED(entity->Load(buffer))) return false;
366     }
367 
368     return true;
369 }
370 #endif
371 
372 /*----------------------------------------------------------------------
373 |   PrintUsageAndExit
374 +---------------------------------------------------------------------*/
375 static void
PrintUsageAndExit(char ** args)376 PrintUsageAndExit(char** args)
377 {
378     fprintf(stderr, "usage: %s [-p <port>] [-f <filepath>]\n", args[0]);
379     fprintf(stderr, "-p : optional server port\n");
380     fprintf(stderr, "-f : optional local filepath to serve\n");
381     exit(1);
382 }
383 
384 /*----------------------------------------------------------------------
385 |   ParseCommandLine
386 +---------------------------------------------------------------------*/
387 static void
ParseCommandLine(char ** args)388 ParseCommandLine(char** args)
389 {
390     const char* arg;
391     char**      tmp = args+1;
392 
393     /* default values */
394     Options.port = 0;
395     Options.path = "";
396 
397     while ((arg = *tmp++)) {
398         if (Options.port == 0 && !strcmp(arg, "-p")) {
399             NPT_UInt32 port;
400             if (NPT_FAILED(NPT_ParseInteger32(*tmp++, port, false))) {
401                 fprintf(stderr, "ERROR: invalid port\n");
402                 exit(1);
403             }
404             Options.port = port;
405         } else if (Options.path.IsEmpty() && !strcmp(arg, "-f")) {
406             Options.path = *tmp++;
407         } else {
408             fprintf(stderr, "ERROR: too many arguments\n");
409             PrintUsageAndExit(args);
410         }
411     }
412 }
413 
414 /*----------------------------------------------------------------------
415 |   main
416 +---------------------------------------------------------------------*/
417 int
main(int argc,char ** argv)418 main(int argc, char** argv)
419 {
420     NPT_COMPILER_UNUSED(argc);
421 
422     NPT_HttpRequestHandler* handler;
423     NPT_Reference<NPT_DataBuffer> buffer;
424     bool result;
425 
426     /* parse command line */
427     ParseCommandLine(argv);
428 
429     /* create http server */
430     PLT_HttpServer http_server(Options.port?Options.port:8089);
431     NPT_String url;
432 
433     if (!Options.path.IsEmpty()) {
434         /* extract folder path */
435         int index1 = Options.path.ReverseFind('\\');
436         int index2 = Options.path.ReverseFind('/');
437         if (index1 <= 0 && index2 <=0) {
438             fprintf(stderr, "ERROR: invalid path\n");
439             exit(1);
440         }
441 
442         NPT_FileInfo info;
443         NPT_CHECK_SEVERE(NPT_File::GetInfo(Options.path, &info));
444 
445         /* add file request handler */
446         handler = new NPT_HttpFileRequestHandler(
447             Options.path.Left(index1>index2?index1:index2),
448             "/");
449         http_server.AddRequestHandler(handler, "/", true);
450 
451         /* build url */
452         url = "/" + Options.path.SubString((index1>index2?index1:index2)+1);
453     } else {
454         /* create random garbage data */
455         buffer = new NPT_DataBuffer(32768);
456         buffer->SetDataSize(32768);
457 
458         /* add static handler */
459         handler = new NPT_HttpStaticRequestHandler(buffer->GetData(),
460             buffer->GetDataSize(),
461             "application/octet-stream");
462         http_server.AddRequestHandler(handler, "/test");
463 
464         /* build url */
465         url = "/test";
466     }
467 
468     /* add custom handler */
469     PLT_RingBufferStreamReference ringbuffer_stream(new PLT_RingBufferStream());
470     NPT_InputStreamReference stream(ringbuffer_stream);
471     NPT_HttpRequestHandler* custom_handler = new PLT_HttpCustomRequestHandler(stream, "text/xml");
472     http_server.AddRequestHandler(custom_handler, "/custom");
473 
474     /* start server */
475     NPT_CHECK_SEVERE(http_server.Start());
476 
477     /* a task manager for the tests downloader */
478     PLT_TaskManager task_manager;
479 
480     /* small delay to let the server start */
481     NPT_System::Sleep(NPT_TimeInterval(1.f));
482 
483     /* execute tests */
484     NPT_Size size;
485     NPT_COMPILER_UNUSED(size);
486 
487 #ifdef TEST1
488     result = Test1(&task_manager, NPT_HttpUrl("127.0.0.1", http_server.GetPort(), url), size);
489     if (!result) return -1;
490 #endif
491 
492 #ifdef TEST2
493     result = Test2(&task_manager, NPT_HttpUrl("127.0.0.1", http_server.GetPort(), url), size);
494     if (!result) return -1;
495 #endif
496 
497 #ifdef TEST3
498     result = Test3(&task_manager, NPT_HttpUrl("127.0.0.1", http_server.GetPort(), "/custom"), ringbuffer_stream, size);
499     if (!result) return -1;
500 #endif
501 
502 #ifdef TEST4
503     result = Test4(&task_manager, NPT_HttpUrl("127.0.0.1", http_server.GetPort(), "/custom"), NPT_TimeInterval(.1f));
504     if (!result) return -1;
505 
506     result = Test4(&task_manager, NPT_HttpUrl("127.0.0.1", http_server.GetPort(), "/custom"), NPT_TimeInterval(1.f));
507     if (!result) return -1;
508 
509     result = Test4(&task_manager, NPT_HttpUrl("127.0.0.1", http_server.GetPort(), "/custom"), NPT_TimeInterval(2.f));
510     if (!result) return -1;
511 #endif
512 
513 #ifdef TEST5
514     result = Test5(NPT_HttpUrl("127.0.0.1", http_server.GetPort(), "/test"));
515     if (!result) return -1;
516 #endif
517 
518     NPT_System::Sleep(NPT_TimeInterval(1.f));
519 
520     // abort server tasks that are waiting on ring buffer stream
521     ringbuffer_stream->Abort();
522 
523     http_server.Stop();
524 
525     return 0;
526 }
527