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