comparison OrthancServer/Sources/ServerContext.cpp @ 4819:70d2a97ca8cb openssl-3.x

integration mainline->openssl-3.x
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 25 Nov 2021 13:12:32 +0100
parents b2417ac5055a 7afbb54bd028
children 2e71a08eea15
comparison
equal deleted inserted replaced
4785:61da49321754 4819:70d2a97ca8cb
43 #include "ServerJobs/OrthancJobUnserializer.h" 43 #include "ServerJobs/OrthancJobUnserializer.h"
44 #include "ServerToolbox.h" 44 #include "ServerToolbox.h"
45 #include "StorageCommitmentReports.h" 45 #include "StorageCommitmentReports.h"
46 46
47 #include <dcmtk/dcmdata/dcfilefo.h> 47 #include <dcmtk/dcmdata/dcfilefo.h>
48 #include <dcmtk/dcmnet/dimse.h>
48 49
49 50
50 static size_t DICOM_CACHE_SIZE = 128 * 1024 * 1024; // 128 MB 51 static size_t DICOM_CACHE_SIZE = 128 * 1024 * 1024; // 128 MB
51 52
52 53
84 transferSyntax != DicomTransferSyntax_HEVCMain10ProfileLevel5_1 && 85 transferSyntax != DicomTransferSyntax_HEVCMain10ProfileLevel5_1 &&
85 86
86 // Do not try to transcode special transfer syntaxes 87 // Do not try to transcode special transfer syntaxes
87 transferSyntax != DicomTransferSyntax_RFC2557MimeEncapsulation && 88 transferSyntax != DicomTransferSyntax_RFC2557MimeEncapsulation &&
88 transferSyntax != DicomTransferSyntax_XML); 89 transferSyntax != DicomTransferSyntax_XML);
90 }
91
92
93 ServerContext::StoreResult::StoreResult() :
94 status_(StoreStatus_Failure),
95 cstoreStatusCode_(0)
96 {
89 } 97 }
90 98
91 99
92 void ServerContext::ChangeThread(ServerContext* that, 100 void ServerContext::ChangeThread(ServerContext* that,
93 unsigned int sleepDelay) 101 unsigned int sleepDelay)
470 478
471 479
472 void ServerContext::RemoveFile(const std::string& fileUuid, 480 void ServerContext::RemoveFile(const std::string& fileUuid,
473 FileContentType type) 481 FileContentType type)
474 { 482 {
475 StorageAccessor accessor(area_, GetMetricsRegistry()); 483 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
476 accessor.Remove(fileUuid, type); 484 accessor.Remove(fileUuid, type);
477 } 485 }
478 486
479 487
480 StoreStatus ServerContext::StoreAfterTranscoding(std::string& resultPublicId, 488 ServerContext::StoreResult ServerContext::StoreAfterTranscoding(std::string& resultPublicId,
481 DicomInstanceToStore& dicom, 489 DicomInstanceToStore& dicom,
482 StoreInstanceMode mode) 490 StoreInstanceMode mode)
483 { 491 {
484 bool overwrite; 492 bool overwrite;
485 switch (mode) 493 switch (mode)
486 { 494 {
487 case StoreInstanceMode_Default: 495 case StoreInstanceMode_Default:
512 dicom.GetSummary(summary); 520 dicom.GetSummary(summary);
513 521
514 try 522 try
515 { 523 {
516 MetricsRegistry::Timer timer(GetMetricsRegistry(), "orthanc_store_dicom_duration_ms"); 524 MetricsRegistry::Timer timer(GetMetricsRegistry(), "orthanc_store_dicom_duration_ms");
517 StorageAccessor accessor(area_, GetMetricsRegistry()); 525 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
518 526
519 DicomInstanceHasher hasher(summary); 527 DicomInstanceHasher hasher(summary);
520 resultPublicId = hasher.HashInstance(); 528 resultPublicId = hasher.HashInstance();
521 529
522 Json::Value dicomAsJson; 530 Json::Value dicomAsJson;
524 532
525 Json::Value simplifiedTags; 533 Json::Value simplifiedTags;
526 Toolbox::SimplifyDicomAsJson(simplifiedTags, dicomAsJson, DicomToJsonFormat_Human); 534 Toolbox::SimplifyDicomAsJson(simplifiedTags, dicomAsJson, DicomToJsonFormat_Human);
527 535
528 // Test if the instance must be filtered out 536 // Test if the instance must be filtered out
529 bool accepted = true; 537 StoreResult result;
530 538
531 { 539 {
532 boost::shared_lock<boost::shared_mutex> lock(listenersMutex_); 540 boost::shared_lock<boost::shared_mutex> lock(listenersMutex_);
533 541
534 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) 542 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it)
535 { 543 {
536 try 544 try
537 { 545 {
538 if (!it->GetListener().FilterIncomingInstance(dicom, simplifiedTags)) 546 if (!it->GetListener().FilterIncomingInstance(dicom, simplifiedTags))
539 { 547 {
540 accepted = false; 548 result.SetStatus(StoreStatus_FilteredOut);
549 result.SetCStoreStatusCode(STATUS_Success); // to keep backward compatibility, we still return 'success'
541 break; 550 break;
542 } 551 }
552
553 if (dicom.GetOrigin().GetRequestOrigin() == Orthanc::RequestOrigin_DicomProtocol)
554 {
555 uint16_t filterResult = it->GetListener().FilterIncomingCStoreInstance(dicom, simplifiedTags);
556 if (filterResult != 0x0000)
557 {
558 result.SetStatus(StoreStatus_FilteredOut);
559 result.SetCStoreStatusCode(filterResult);
560 break;
561 }
562 }
563
543 } 564 }
544 catch (OrthancException& e) 565 catch (OrthancException& e)
545 { 566 {
546 LOG(ERROR) << "Error in the " << it->GetDescription() 567 LOG(ERROR) << "Error in the " << it->GetDescription()
547 << " callback while receiving an instance: " << e.What() 568 << " callback while receiving an instance: " << e.What()
549 throw; 570 throw;
550 } 571 }
551 } 572 }
552 } 573 }
553 574
554 if (!accepted) 575 if (result.GetStatus() == StoreStatus_FilteredOut)
555 { 576 {
556 LOG(INFO) << "An incoming instance has been discarded by the filter"; 577 LOG(INFO) << "An incoming instance has been discarded by the filter";
557 return StoreStatus_FilteredOut; 578 return result;
558 } 579 }
559 580
560 // Remove the file from the DicomCache (useful if 581 // Remove the file from the DicomCache (useful if
561 // "OverwriteInstances" is set to "true") 582 // "OverwriteInstances" is set to "true")
562 dicomCache_.Invalidate(resultPublicId); 583 dicomCache_.Invalidate(resultPublicId);
581 attachments.push_back(dicomUntilPixelData); 602 attachments.push_back(dicomUntilPixelData);
582 } 603 }
583 604
584 typedef std::map<MetadataType, std::string> InstanceMetadata; 605 typedef std::map<MetadataType, std::string> InstanceMetadata;
585 InstanceMetadata instanceMetadata; 606 InstanceMetadata instanceMetadata;
586 StoreStatus status = index_.Store( 607 result.SetStatus(index_.Store(
587 instanceMetadata, summary, attachments, dicom.GetMetadata(), dicom.GetOrigin(), overwrite, 608 instanceMetadata, summary, attachments, dicom.GetMetadata(), dicom.GetOrigin(), overwrite,
588 hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset); 609 hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset));
589 610
590 // Only keep the metadata for the "instance" level 611 // Only keep the metadata for the "instance" level
591 dicom.ClearMetadata(); 612 dicom.ClearMetadata();
592 613
593 for (InstanceMetadata::const_iterator it = instanceMetadata.begin(); 614 for (InstanceMetadata::const_iterator it = instanceMetadata.begin();
594 it != instanceMetadata.end(); ++it) 615 it != instanceMetadata.end(); ++it)
595 { 616 {
596 dicom.AddMetadata(ResourceType_Instance, it->first, it->second); 617 dicom.AddMetadata(ResourceType_Instance, it->first, it->second);
597 } 618 }
598 619
599 if (status != StoreStatus_Success) 620 if (result.GetStatus() != StoreStatus_Success)
600 { 621 {
601 accessor.Remove(dicomInfo); 622 accessor.Remove(dicomInfo);
602 623
603 if (dicomUntilPixelData.IsValid()) 624 if (dicomUntilPixelData.IsValid())
604 { 625 {
605 accessor.Remove(dicomUntilPixelData); 626 accessor.Remove(dicomUntilPixelData);
606 } 627 }
607 } 628 }
608 629
609 switch (status) 630 switch (result.GetStatus())
610 { 631 {
611 case StoreStatus_Success: 632 case StoreStatus_Success:
612 LOG(INFO) << "New instance stored"; 633 LOG(INFO) << "New instance stored";
613 break; 634 break;
614 635
623 default: 644 default:
624 // This should never happen 645 // This should never happen
625 break; 646 break;
626 } 647 }
627 648
628 if (status == StoreStatus_Success || 649 if (result.GetStatus() == StoreStatus_Success ||
629 status == StoreStatus_AlreadyStored) 650 result.GetStatus() == StoreStatus_AlreadyStored)
630 { 651 {
631 boost::shared_lock<boost::shared_mutex> lock(listenersMutex_); 652 boost::shared_lock<boost::shared_mutex> lock(listenersMutex_);
632 653
633 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) 654 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it)
634 { 655 {
643 << " (code " << e.GetErrorCode() << ")"; 664 << " (code " << e.GetErrorCode() << ")";
644 } 665 }
645 } 666 }
646 } 667 }
647 668
648 return status; 669 return result;
649 } 670 }
650 catch (OrthancException& e) 671 catch (OrthancException& e)
651 { 672 {
652 if (e.GetErrorCode() == ErrorCode_InexistentTag) 673 if (e.GetErrorCode() == ErrorCode_InexistentTag)
653 { 674 {
657 throw; 678 throw;
658 } 679 }
659 } 680 }
660 681
661 682
662 StoreStatus ServerContext::Store(std::string& resultPublicId, 683 ServerContext::StoreResult ServerContext::Store(std::string& resultPublicId,
663 DicomInstanceToStore& dicom, 684 DicomInstanceToStore& dicom,
664 StoreInstanceMode mode) 685 StoreInstanceMode mode)
665 { 686 {
666 if (!isIngestTranscoding_) 687 if (!isIngestTranscoding_)
667 { 688 {
668 // No automated transcoding. This was the only path in Orthanc <= 1.6.1. 689 // No automated transcoding. This was the only path in Orthanc <= 1.6.1.
669 return StoreAfterTranscoding(resultPublicId, dicom, mode); 690 return StoreAfterTranscoding(resultPublicId, dicom, mode);
719 std::unique_ptr<ParsedDicomFile> tmp(transcoded.ReleaseAsParsedDicomFile()); 740 std::unique_ptr<ParsedDicomFile> tmp(transcoded.ReleaseAsParsedDicomFile());
720 741
721 std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*tmp)); 742 std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*tmp));
722 toStore->SetOrigin(dicom.GetOrigin()); 743 toStore->SetOrigin(dicom.GetOrigin());
723 744
724 StoreStatus ok = StoreAfterTranscoding(resultPublicId, *toStore, mode); 745 StoreResult result = StoreAfterTranscoding(resultPublicId, *toStore, mode);
725 assert(resultPublicId == tmp->GetHasher().HashInstance()); 746 assert(resultPublicId == tmp->GetHasher().HashInstance());
726 747
727 return ok; 748 return result;
728 } 749 }
729 else 750 else
730 { 751 {
731 // Cannot transcode => store the original file 752 // Cannot transcode => store the original file
732 return StoreAfterTranscoding(resultPublicId, dicom, mode); 753 return StoreAfterTranscoding(resultPublicId, dicom, mode);
746 { 767 {
747 throw OrthancException(ErrorCode_UnknownResource); 768 throw OrthancException(ErrorCode_UnknownResource);
748 } 769 }
749 else 770 else
750 { 771 {
751 StorageAccessor accessor(area_, GetMetricsRegistry()); 772 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
752 accessor.AnswerFile(output, attachment, GetFileContentMime(content)); 773 accessor.AnswerFile(output, attachment, GetFileContentMime(content));
753 } 774 }
754 } 775 }
755 776
756 777
776 return; 797 return;
777 } 798 }
778 799
779 std::string content; 800 std::string content;
780 801
781 StorageAccessor accessor(area_, GetMetricsRegistry()); 802 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
782 accessor.Read(content, attachment); 803 accessor.Read(content, attachment);
783 804
784 FileInfo modified = accessor.Write(content.empty() ? NULL : content.c_str(), 805 FileInfo modified = accessor.Write(content.empty() ? NULL : content.c_str(),
785 content.size(), attachmentType, compression, storeMD5_); 806 content.size(), attachmentType, compression, storeMD5_);
786 807
832 if (index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomUntilPixelData)) 853 if (index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomUntilPixelData))
833 { 854 {
834 std::string dicom; 855 std::string dicom;
835 856
836 { 857 {
837 StorageAccessor accessor(area_, GetMetricsRegistry()); 858 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
838 accessor.Read(dicom, attachment); 859 accessor.Read(dicom, attachment);
839 } 860 }
840 861
841 ParsedDicomFile parsed(dicom); 862 ParsedDicomFile parsed(dicom);
842 OrthancConfiguration::DefaultDicomDatasetToJson(result, parsed, ignoreTagLength); 863 OrthancConfiguration::DefaultDicomDatasetToJson(result, parsed, ignoreTagLength);
897 * "true". 918 * "true".
898 **/ 919 **/
899 920
900 std::unique_ptr<IMemoryBuffer> dicom; 921 std::unique_ptr<IMemoryBuffer> dicom;
901 { 922 {
902 MetricsRegistry::Timer timer(GetMetricsRegistry(), "orthanc_storage_read_range_duration_ms"); 923 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
903 dicom.reset(area_.ReadRange(attachment.GetUuid(), FileContentType_Dicom, 0, pixelDataOffset)); 924 dicom.reset(accessor.ReadStartRange(attachment.GetUuid(), FileContentType_Dicom, pixelDataOffset, FileContentType_DicomUntilPixelData));
904 } 925 }
905 926
906 if (dicom.get() == NULL) 927 if (dicom.get() == NULL)
907 { 928 {
908 throw OrthancException(ErrorCode_InternalError); 929 throw OrthancException(ErrorCode_InternalError);
927 **/ 948 **/
928 949
929 std::string dicomAsJson; 950 std::string dicomAsJson;
930 951
931 { 952 {
932 StorageAccessor accessor(area_, GetMetricsRegistry()); 953 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
933 accessor.Read(dicomAsJson, attachment); 954 accessor.Read(dicomAsJson, attachment);
934 } 955 }
935 956
936 if (!Toolbox::ReadJson(result, dicomAsJson)) 957 if (!Toolbox::ReadJson(result, dicomAsJson))
937 { 958 {
996 const std::string& instancePublicId) 1017 const std::string& instancePublicId)
997 { 1018 {
998 int64_t revision; 1019 int64_t revision;
999 ReadAttachment(dicom, revision, instancePublicId, FileContentType_Dicom, true /* uncompress */); 1020 ReadAttachment(dicom, revision, instancePublicId, FileContentType_Dicom, true /* uncompress */);
1000 } 1021 }
1001 1022
1023 void ServerContext::ReadDicomForHeader(std::string& dicom,
1024 const std::string& instancePublicId)
1025 {
1026 if (!ReadDicomUntilPixelData(dicom, instancePublicId))
1027 {
1028 ReadDicom(dicom, instancePublicId);
1029 }
1030 }
1002 1031
1003 bool ServerContext::ReadDicomUntilPixelData(std::string& dicom, 1032 bool ServerContext::ReadDicomUntilPixelData(std::string& dicom,
1004 const std::string& instancePublicId) 1033 const std::string& instancePublicId)
1005 { 1034 {
1006 if (!area_.HasReadRange()) 1035 if (!area_.HasReadRange())
1025 { 1054 {
1026 try 1055 try
1027 { 1056 {
1028 uint64_t pixelDataOffset = boost::lexical_cast<uint64_t>(s); 1057 uint64_t pixelDataOffset = boost::lexical_cast<uint64_t>(s);
1029 1058
1059 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
1060
1030 std::unique_ptr<IMemoryBuffer> buffer( 1061 std::unique_ptr<IMemoryBuffer> buffer(
1031 area_.ReadRange(attachment.GetUuid(), attachment.GetContentType(), 0, pixelDataOffset)); 1062 accessor.ReadStartRange(attachment.GetUuid(), attachment.GetContentType(), pixelDataOffset, FileContentType_DicomUntilPixelData));
1032 buffer->MoveToString(dicom); 1063 buffer->MoveToString(dicom);
1033 return true; // Success 1064 return true; // Success
1034 } 1065 }
1035 catch (boost::bad_lexical_cast&) 1066 catch (boost::bad_lexical_cast&)
1036 { 1067 {
1057 } 1088 }
1058 1089
1059 assert(attachment.GetContentType() == content); 1090 assert(attachment.GetContentType() == content);
1060 1091
1061 { 1092 {
1062 StorageAccessor accessor(area_, GetMetricsRegistry()); 1093 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
1063 1094
1064 if (uncompressIfNeeded) 1095 if (uncompressIfNeeded)
1065 { 1096 {
1066 accessor.Read(result, attachment); 1097 accessor.Read(result, attachment);
1067 } 1098 }
1157 LOG(INFO) << "Adding attachment " << EnumerationToString(attachmentType) << " to resource " << resourceId; 1188 LOG(INFO) << "Adding attachment " << EnumerationToString(attachmentType) << " to resource " << resourceId;
1158 1189
1159 // TODO Should we use "gzip" instead? 1190 // TODO Should we use "gzip" instead?
1160 CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None); 1191 CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None);
1161 1192
1162 StorageAccessor accessor(area_, GetMetricsRegistry()); 1193 StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
1163 FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_); 1194 FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_);
1164 1195
1165 try 1196 try
1166 { 1197 {
1167 StoreStatus status = index_.AddAttachment( 1198 StoreStatus status = index_.AddAttachment(
1973 const std::string& ServerContext::GetDeidentifiedContent(const DicomElement &element) const 2004 const std::string& ServerContext::GetDeidentifiedContent(const DicomElement &element) const
1974 { 2005 {
1975 static const std::string redactedContent = "*** POTENTIAL PHI ***"; 2006 static const std::string redactedContent = "*** POTENTIAL PHI ***";
1976 2007
1977 const DicomTag& tag = element.GetTag(); 2008 const DicomTag& tag = element.GetTag();
1978 if (deidentifyLogs_ && ( 2009 if (deidentifyLogs_ &&
1979 logsDeidentifierRules_.IsCleared(tag) || 2010 !element.GetValue().GetContent().empty() &&
1980 logsDeidentifierRules_.IsRemoved(tag) || 2011 logsDeidentifierRules_.IsAlteredTag(tag))
1981 logsDeidentifierRules_.IsReplaced(tag)))
1982 { 2012 {
1983 return redactedContent; 2013 return redactedContent;
1984 } 2014 }
1985 else 2015 else
1986 { 2016 {