comparison Plugins/Engine/OrthancPlugins.cpp @ 3528:f6fe095f7130

don't open a multipart stream if plugin only sends one part
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 26 Sep 2019 13:40:49 +0200
parents 69e49fc044f8
children 94f4a18a79cc
comparison
equal deleted inserted replaced
3527:40c80049fac7 3528:f6fe095f7130
420 { 420 {
421 private: 421 private:
422 boost::mutex contextMutex_; 422 boost::mutex contextMutex_;
423 ServerContext* context_; 423 ServerContext* context_;
424 424
425
426 public: 425 public:
427 class PluginHttpOutput : public boost::noncopyable 426 class PluginHttpOutput : public boost::noncopyable
428 { 427 {
429 private: 428 private:
429 enum MultipartState
430 {
431 MultipartState_None,
432 MultipartState_FirstPart,
433 MultipartState_SecondPart,
434 MultipartState_NextParts
435 };
436
430 HttpOutput& output_; 437 HttpOutput& output_;
431 std::auto_ptr<std::string> errorDetails_; 438 std::auto_ptr<std::string> errorDetails_;
432 bool logDetails_; 439 bool logDetails_;
440 MultipartState multipartState_;
441 std::string multipartSubType_;
442 std::string multipartContentType_;
443 std::string multipartFirstPart_;
444 std::map<std::string, std::string> multipartFirstHeaders_;
433 445
434 public: 446 public:
435 PluginHttpOutput(HttpOutput& output) : 447 PluginHttpOutput(HttpOutput& output) :
436 output_(output), 448 output_(output),
437 logDetails_(false) 449 logDetails_(false),
450 multipartState_(MultipartState_None)
438 { 451 {
439 } 452 }
440 453
441 HttpOutput& GetOutput() 454 HttpOutput& GetOutput()
442 { 455 {
443 return output_; 456 if (multipartState_ == MultipartState_None)
457 {
458 return output_;
459 }
460 else
461 {
462 // Must use "SendMultipartItem()" on multipart streams
463 throw OrthancException(ErrorCode_BadSequenceOfCalls);
464 }
444 } 465 }
445 466
446 void SetErrorDetails(const std::string& details, 467 void SetErrorDetails(const std::string& details,
447 bool logDetails) 468 bool logDetails)
448 { 469 {
470 { 491 {
471 return *errorDetails_; 492 return *errorDetails_;
472 } 493 }
473 } 494 }
474 495
496 void StartMultipart(const char* subType,
497 const char* contentType)
498 {
499 if (multipartState_ != MultipartState_None)
500 {
501 throw OrthancException(ErrorCode_BadSequenceOfCalls);
502 }
503 else
504 {
505 multipartState_ = MultipartState_FirstPart;
506 multipartSubType_ = subType;
507 multipartContentType_ = contentType;
508 }
509 }
510
511 void SendMultipartItem(const void* data,
512 size_t size,
513 const std::map<std::string, std::string>& headers)
514 {
515 if (size != 0 && data == NULL)
516 {
517 throw OrthancException(ErrorCode_NullPointer);
518 }
519
520 switch (multipartState_)
521 {
522 case MultipartState_None:
523 // Must call "StartMultipart()" before
524 throw OrthancException(ErrorCode_BadSequenceOfCalls);
525
526 case MultipartState_FirstPart:
527 multipartFirstPart_.assign(reinterpret_cast<const char*>(data), size);
528 multipartFirstHeaders_ = headers;
529 multipartState_ = MultipartState_SecondPart;
530 break;
531
532 case MultipartState_SecondPart:
533 // Start an actual stream for chunked transfer as soon as
534 // there are more than 2 elements in the multipart stream
535 output_.StartMultipart(multipartSubType_, multipartContentType_);
536 output_.SendMultipartItem(multipartFirstPart_.c_str(), multipartFirstPart_.size(),
537 multipartFirstHeaders_);
538 multipartFirstPart_.clear(); // Release memory
539
540 output_.SendMultipartItem(data, size, headers);
541 multipartState_ = MultipartState_NextParts;
542 break;
543
544 case MultipartState_NextParts:
545 output_.SendMultipartItem(data, size, headers);
546 break;
547
548 default:
549 throw OrthancException(ErrorCode_ParameterOutOfRange);
550 }
551 }
552
475 void Close(OrthancPluginErrorCode error, 553 void Close(OrthancPluginErrorCode error,
476 PluginsErrorDictionary& dictionary) 554 PluginsErrorDictionary& dictionary)
477 { 555 {
478 if (error == OrthancPluginErrorCode_Success) 556 if (error == OrthancPluginErrorCode_Success)
479 { 557 {
480 if (GetOutput().IsWritingMultipart()) 558 switch (multipartState_)
481 { 559 {
482 GetOutput().CloseMultipart(); 560 case MultipartState_None:
561 assert(!output_.IsWritingMultipart());
562 break;
563
564 case MultipartState_FirstPart: // Multipart started, but no part was sent
565 case MultipartState_SecondPart: // Multipart started, first part is pending
566 {
567 assert(!output_.IsWritingMultipart());
568 std::vector<const void*> parts;
569 std::vector<size_t> sizes;
570 std::vector<const std::map<std::string, std::string>*> headers;
571
572 if (multipartState_ == MultipartState_SecondPart)
573 {
574 parts.push_back(multipartFirstPart_.c_str());
575 sizes.push_back(multipartFirstPart_.size());
576 headers.push_back(&multipartFirstHeaders_);
577 }
578
579 output_.AnswerMultipartWithoutChunkedTransfer(multipartSubType_, multipartContentType_,
580 parts, sizes, headers);
581 break;
582 }
583
584 case MultipartState_NextParts:
585 assert(output_.IsWritingMultipart());
586 output_.CloseMultipart();
587
588 default:
589 throw OrthancException(ErrorCode_InternalError);
483 } 590 }
484 } 591 }
485 else 592 else
486 { 593 {
487 dictionary.LogError(error, false); 594 dictionary.LogError(error, false);
2909 // An exception might be raised in this function if the 3016 // An exception might be raised in this function if the
2910 // connection was closed by the HTTP client. 3017 // connection was closed by the HTTP client.
2911 const _OrthancPluginAnswerBuffer& p = 3018 const _OrthancPluginAnswerBuffer& p =
2912 *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters); 3019 *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
2913 3020
2914 HttpOutput& output = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2915
2916 std::map<std::string, std::string> headers; // No custom headers 3021 std::map<std::string, std::string> headers; // No custom headers
2917 output.SendMultipartItem(p.answer, p.answerSize, headers); 3022 reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->SendMultipartItem(p.answer, p.answerSize, headers);
2918 } 3023 }
2919 3024
2920 3025
2921 void OrthancPlugins::ApplySendMultipartItem2(const void* parameters) 3026 void OrthancPlugins::ApplySendMultipartItem2(const void* parameters)
2922 { 3027 {
2923 // An exception might be raised in this function if the 3028 // An exception might be raised in this function if the
2924 // connection was closed by the HTTP client. 3029 // connection was closed by the HTTP client.
2925 const _OrthancPluginSendMultipartItem2& p = 3030 const _OrthancPluginSendMultipartItem2& p =
2926 *reinterpret_cast<const _OrthancPluginSendMultipartItem2*>(parameters); 3031 *reinterpret_cast<const _OrthancPluginSendMultipartItem2*>(parameters);
2927 HttpOutput& output = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2928 3032
2929 std::map<std::string, std::string> headers; 3033 std::map<std::string, std::string> headers;
2930 for (uint32_t i = 0; i < p.headersCount; i++) 3034 for (uint32_t i = 0; i < p.headersCount; i++)
2931 { 3035 {
2932 headers[p.headersKeys[i]] = p.headersValues[i]; 3036 headers[p.headersKeys[i]] = p.headersValues[i];
2933 } 3037 }
2934 3038
2935 output.SendMultipartItem(p.answer, p.answerSize, headers); 3039 reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->SendMultipartItem(p.answer, p.answerSize, headers);
2936 } 3040 }
2937 3041
2938 3042
2939 void OrthancPlugins::DatabaseAnswer(const void* parameters) 3043 void OrthancPlugins::DatabaseAnswer(const void* parameters)
2940 { 3044 {
3205 3309
3206 case _OrthancPluginService_StartMultipartAnswer: 3310 case _OrthancPluginService_StartMultipartAnswer:
3207 { 3311 {
3208 const _OrthancPluginStartMultipartAnswer& p = 3312 const _OrthancPluginStartMultipartAnswer& p =
3209 *reinterpret_cast<const _OrthancPluginStartMultipartAnswer*>(parameters); 3313 *reinterpret_cast<const _OrthancPluginStartMultipartAnswer*>(parameters);
3210 HttpOutput& output = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); 3314 reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->StartMultipart(p.subType, p.contentType);
3211 output.StartMultipart(p.subType, p.contentType);
3212 return true; 3315 return true;
3213 } 3316 }
3214 3317
3215 case _OrthancPluginService_SendMultipartItem: 3318 case _OrthancPluginService_SendMultipartItem:
3216 ApplySendMultipartItem(parameters); 3319 ApplySendMultipartItem(parameters);