Mercurial > hg > orthanc
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 { |