comparison Core/HttpServer/HttpServer.cpp @ 3395:0ce9b4f5fdf5

new abstraction: IHttpHandler::CreateStreamHandler()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 06 Jun 2019 18:54:27 +0200
parents c0aa5f1cf2f5
children e280ced38a4c
comparison
equal deleted inserted replaced
3394:2f82ef41bf5a 3395:0ce9b4f5fdf5
77 #define ORTHANC_REALM "Orthanc Secure Area" 77 #define ORTHANC_REALM "Orthanc Secure Area"
78 78
79 79
80 namespace Orthanc 80 namespace Orthanc
81 { 81 {
82 static const char multipart[] = "multipart/form-data; boundary="; 82 static const char MULTIPART_FORM[] = "multipart/form-data; boundary=";
83 static unsigned int multipartLength = sizeof(multipart) / sizeof(char) - 1; 83 static unsigned int MULTIPART_FORM_LENGTH = sizeof(MULTIPART_FORM) / sizeof(char) - 1;
84 84
85 85
86 namespace 86 namespace
87 { 87 {
88 // Anonymous namespace to avoid clashes between compilation modules 88 // Anonymous namespace to avoid clashes between compilation modules
300 { 300 {
301 return pimpl_->chunkStore_; 301 return pimpl_->chunkStore_;
302 } 302 }
303 303
304 304
305 305 static PostDataStatus ReadBodyWithContentLength(std::string& body,
306 static PostDataStatus ReadBody(std::string& postData, 306 struct mg_connection *connection,
307 struct mg_connection *connection, 307 const std::string& contentLength)
308 const IHttpHandler::Arguments& headers) 308 {
309 { 309 int length;
310 IHttpHandler::Arguments::const_iterator cs = headers.find("content-length"); 310 try
311 if (cs == headers.end()) 311 {
312 { 312 length = boost::lexical_cast<int>(contentLength);
313 // Store all the individual chunks within a temporary file, then 313 }
314 // read it back into the memory buffer "postData" 314 catch (boost::bad_lexical_cast&)
315 {
316 return PostDataStatus_NoLength;
317 }
318
319 if (length < 0)
320 {
321 length = 0;
322 }
323
324 body.resize(length);
325
326 size_t pos = 0;
327 while (length > 0)
328 {
329 int r = mg_read(connection, &body[pos], length);
330 if (r <= 0)
331 {
332 return PostDataStatus_Failure;
333 }
334
335 assert(r <= length);
336 length -= r;
337 pos += r;
338 }
339
340 return PostDataStatus_Success;
341 }
342
343
344 static PostDataStatus ReadBodyToString(std::string& body,
345 struct mg_connection *connection,
346 const IHttpHandler::Arguments& headers)
347 {
348 IHttpHandler::Arguments::const_iterator contentLength = headers.find("content-length");
349
350 if (contentLength != headers.end())
351 {
352 // "Content-Length" is available
353 return ReadBodyWithContentLength(body, connection, contentLength->second);
354 }
355 else
356 {
357 // No Content-Length. Store the individual chunks in a temporary
358 // file, then read it back into the memory buffer "body"
315 FileBuffer buffer; 359 FileBuffer buffer;
316 360
317 std::string tmp(1024 * 1024, 0); 361 std::string tmp(1024 * 1024, 0);
318 362
319 for (;;) 363 for (;;)
331 { 375 {
332 buffer.Append(tmp.c_str(), r); 376 buffer.Append(tmp.c_str(), r);
333 } 377 }
334 } 378 }
335 379
336 buffer.Read(postData); 380 buffer.Read(body);
337 381
338 return PostDataStatus_Success; 382 return PostDataStatus_Success;
339 } 383 }
384 }
385
386
387 static PostDataStatus ReadBodyToStream(IHttpHandler::IStream& stream,
388 struct mg_connection *connection,
389 const IHttpHandler::Arguments& headers)
390 {
391 IHttpHandler::Arguments::const_iterator contentLength = headers.find("content-length");
392
393 if (contentLength != headers.end())
394 {
395 // "Content-Length" is available
396
397 std::string body;
398 PostDataStatus status = ReadBodyWithContentLength(body, connection, contentLength->second);
399
400 if (status == PostDataStatus_Success &&
401 !body.empty())
402 {
403 stream.AddBodyChunk(body.c_str(), body.size());
404 }
405
406 return status;
407 }
340 else 408 else
341 { 409 {
342 // "Content-Length" is available 410 // No Content-Length. Stream the HTTP connection.
343 int length; 411 std::string tmp(1024 * 1024, 0);
344 try 412
345 { 413 for (;;)
346 length = boost::lexical_cast<int>(cs->second); 414 {
347 } 415 int r = mg_read(connection, &tmp[0], tmp.size());
348 catch (boost::bad_lexical_cast&) 416 if (r < 0)
349 {
350 return PostDataStatus_NoLength;
351 }
352
353 if (length < 0)
354 {
355 length = 0;
356 }
357
358 postData.resize(length);
359
360 size_t pos = 0;
361 while (length > 0)
362 {
363 int r = mg_read(connection, &postData[pos], length);
364 if (r <= 0)
365 { 417 {
366 return PostDataStatus_Failure; 418 return PostDataStatus_Failure;
367 } 419 }
368 420 else if (r == 0)
369 assert(r <= length); 421 {
370 length -= r; 422 break;
371 pos += r; 423 }
424 else
425 {
426 stream.AddBodyChunk(tmp.c_str(), r);
427 }
372 } 428 }
373 429
374 return PostDataStatus_Success; 430 return PostDataStatus_Success;
375 } 431 }
376 } 432 }
377 433
378 434
379 435 static PostDataStatus ParseMultipartForm(std::string &completedFile,
380 static PostDataStatus ParseMultipartPost(std::string &completedFile,
381 struct mg_connection *connection, 436 struct mg_connection *connection,
382 const IHttpHandler::Arguments& headers, 437 const IHttpHandler::Arguments& headers,
383 const std::string& contentType, 438 const std::string& contentType,
384 ChunkStore& chunkStore) 439 ChunkStore& chunkStore)
385 { 440 {
386 std::string boundary = "--" + contentType.substr(multipartLength); 441 std::string boundary = "--" + contentType.substr(MULTIPART_FORM_LENGTH);
387 442
388 std::string postData; 443 std::string body;
389 PostDataStatus status = ReadBody(postData, connection, headers); 444 PostDataStatus status = ReadBodyToString(body, connection, headers);
390 445
391 if (status != PostDataStatus_Success) 446 if (status != PostDataStatus_Success)
392 { 447 {
393 return status; 448 return status;
394 } 449 }
431 486
432 try 487 try
433 { 488 {
434 FindIterator last; 489 FindIterator last;
435 for (FindIterator it = 490 for (FindIterator it =
436 make_find_iterator(postData, boost::first_finder(boundary)); 491 make_find_iterator(body, boost::first_finder(boundary));
437 it!=FindIterator(); 492 it!=FindIterator();
438 ++it) 493 ++it)
439 { 494 {
440 if (last != FindIterator()) 495 if (last != FindIterator())
441 { 496 {
750 return; 805 return;
751 } 806 }
752 } 807 }
753 808
754 809
755 // Extract the body of the request for PUT and POST 810 // Decompose the URI into its components
811 UriComponents uri;
812 try
813 {
814 Toolbox::SplitUriComponents(uri, requestUri);
815 }
816 catch (OrthancException&)
817 {
818 output.SendStatus(HttpStatus_400_BadRequest);
819 return;
820 }
821
822 LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri);
823
824
825 bool found = false;
826
827 // Extract the body of the request for PUT and POST, or process
828 // the body as a stream
756 829
757 // TODO Avoid unneccessary memcopy of the body 830 // TODO Avoid unneccessary memcopy of the body
758 831
759 std::string body; 832 std::string body;
760 if (method == HttpMethod_Post || 833 if (method == HttpMethod_Post ||
761 method == HttpMethod_Put) 834 method == HttpMethod_Put)
762 { 835 {
763 PostDataStatus status; 836 PostDataStatus status;
764 837
838 bool isMultipartForm = false;
839
765 IHttpHandler::Arguments::const_iterator ct = headers.find("content-type"); 840 IHttpHandler::Arguments::const_iterator ct = headers.find("content-type");
766 if (ct == headers.end()) 841 if (ct != headers.end() &&
767 { 842 ct->second.size() >= MULTIPART_FORM_LENGTH &&
768 // No content-type specified. Assume no multi-part content occurs at this point. 843 !memcmp(ct->second.c_str(), MULTIPART_FORM, MULTIPART_FORM_LENGTH))
769 status = ReadBody(body, connection, headers); 844 {
770 } 845 status = ParseMultipartForm(body, connection, headers, ct->second, server.GetChunkStore());
771 else 846 isMultipartForm = true;
772 { 847 }
773 std::string contentType = ct->second; 848
774 if (contentType.size() >= multipartLength && 849 if (!isMultipartForm)
775 !memcmp(contentType.c_str(), multipart, multipartLength)) 850 {
776 { 851 std::auto_ptr<IHttpHandler::IStream> stream;
777 status = ParseMultipartPost(body, connection, headers, contentType, server.GetChunkStore()); 852
853 if (server.HasHandler())
854 {
855 stream.reset(server.GetHandler().CreateStreamHandler
856 (RequestOrigin_RestApi, remoteIp, username.c_str(), method, uri, headers));
857 }
858
859 if (stream.get() != NULL)
860 {
861 status = ReadBodyToStream(*stream, connection, headers);
862
863 if (status == PostDataStatus_Success)
864 {
865 stream->Execute(output);
866 }
778 } 867 }
779 else 868 else
780 { 869 {
781 status = ReadBody(body, connection, headers); 870 status = ReadBodyToString(body, connection, headers);
782 } 871 }
783 } 872 }
784 873
785 switch (status) 874 switch (status)
786 { 875 {
794 883
795 case PostDataStatus_Pending: 884 case PostDataStatus_Pending:
796 output.AnswerEmpty(); 885 output.AnswerEmpty();
797 return; 886 return;
798 887
888 case PostDataStatus_Success:
889 break;
890
799 default: 891 default:
800 break; 892 throw OrthancException(ErrorCode_InternalError);
801 } 893 }
802 } 894 }
803 895
804 896
805 // Decompose the URI into its components 897 if (!found &&
806 UriComponents uri; 898 server.HasHandler())
807 try
808 {
809 Toolbox::SplitUriComponents(uri, requestUri);
810 }
811 catch (OrthancException&)
812 {
813 output.SendStatus(HttpStatus_400_BadRequest);
814 return;
815 }
816
817
818 LOG(INFO) << EnumerationToString(method) << " " << Toolbox::FlattenUri(uri);
819
820 bool found = false;
821
822 if (server.HasHandler())
823 { 899 {
824 found = server.GetHandler().Handle(output, RequestOrigin_RestApi, remoteIp, username.c_str(), 900 found = server.GetHandler().Handle(output, RequestOrigin_RestApi, remoteIp, username.c_str(),
825 method, uri, headers, argumentsGET, body.c_str(), body.size()); 901 method, uri, headers, argumentsGET, body.c_str(), body.size());
826 } 902 }
827 903