Mercurial > hg > orthanc
changeset 6422:36aff9f84264 queues-timeout tip
integration mainline->queues-timeout
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Sat, 15 Nov 2025 12:28:27 +0100 |
| parents | b86411c11092 (current diff) c557f6bdcbfd (diff) |
| children | |
| files | NEWS OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp |
| diffstat | 42 files changed, 526 insertions(+), 295 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Wed Nov 12 19:30:44 2025 +0100 +++ b/NEWS Sat Nov 15 12:28:27 2025 +0100 @@ -15,12 +15,11 @@ * Fix: C-Get SCU jobs or HTTP responses were always successful even if the C-Get operation actually failed. -* C-Store, C-Move and C-Get jobs and HTTP responses now include a new "DimseErrorStatus" - field (ONLY if the operation fails). +* When an error occurs while Orthanc handles an HTTP request, it may now include a new + "ErrorPayload" in the HTTP response with details about the failed operation. + This is currently implemented for C-Store, C-Move and C-Get operations. * C-Move and C-Get jobs and HTTP responses now include a new "Details" field with the "DimseErrorStatus" - of each operation and the list of "RetrievedInstancesIds". Note that, if you have multiple - resources to retrieve and one of them fails, you'll only get those details if you have set - "Permissive" to true to allow other operations to continue after the failure. + of each operation and the list of "RetrievedInstancesIds". Maintenance -----------
--- a/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Sat Nov 15 12:28:27 2025 +0100 @@ -574,6 +574,7 @@ ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomControlUserConnection.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomServer.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomStoreUserConnection.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DimseErrorPayload.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/Internals/CommandDispatcher.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/Internals/FindScp.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/Internals/MoveScp.cpp
--- a/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -24,6 +24,7 @@ #include "../PrecompiledHeaders.h" #include "DicomControlUserConnection.h" +#include "DimseErrorPayload.h" #include "../Compatibility.h" #include "../DicomFormat/DicomArray.h" @@ -389,17 +390,19 @@ if (response.DimseStatus == STATUS_FIND_Failed_UnableToProcess) { throw OrthancException(ErrorCode_NetworkProtocol, - HttpStatus_422_UnprocessableEntity, "C-FIND SCU to AET \"" + parameters_.GetRemoteModality().GetApplicationEntityTitle() + "\" has failed with DIMSE status " + DimseToHexString(response.DimseStatus) + - " (unable to process - invalid query ?)"); + " (unable to process - invalid query ?)") + .SetHttpStatus(HttpStatus_422_UnprocessableEntity) + .SetPayload(MakeDimseErrorStatusPayload(response.DimseStatus)); } else { throw OrthancException(ErrorCode_NetworkProtocol, "C-FIND SCU to AET \"" + parameters_.GetRemoteModality().GetApplicationEntityTitle() + - "\" has failed with DIMSE status " + DimseToHexString(response.DimseStatus)); + "\" has failed with DIMSE status " + DimseToHexString(response.DimseStatus)) + .SetPayload(MakeDimseErrorStatusPayload(response.DimseStatus)); } } } @@ -517,19 +520,19 @@ if (response.DimseStatus == STATUS_MOVE_Failed_UnableToProcess) { throw OrthancException(ErrorCode_NetworkProtocol, - HttpStatus_422_UnprocessableEntity, "C-MOVE SCU to AET \"" + parameters_.GetRemoteModality().GetApplicationEntityTitle() + "\" has failed with DIMSE status " + DimseToHexString(response.DimseStatus) + - " (unable to process - resource not found ?)", - response.DimseStatus); + " (unable to process - resource not found ?)") + .SetHttpStatus(HttpStatus_422_UnprocessableEntity) + .SetPayload(MakeDimseErrorStatusPayload(response.DimseStatus)); } else { throw OrthancException(ErrorCode_NetworkProtocol, "C-MOVE SCU to AET \"" + parameters_.GetRemoteModality().GetApplicationEntityTitle() + - "\" has failed with DIMSE status " + DimseToHexString(response.DimseStatus), - response.DimseStatus); + "\" has failed with DIMSE status " + DimseToHexString(response.DimseStatus)) + .SetPayload(MakeDimseErrorStatusPayload(response.DimseStatus)); } } } @@ -664,8 +667,8 @@ throw OrthancException(ErrorCode_NetworkProtocol, "C-GET SCU to AET \"" + parameters_.GetRemoteModality().GetApplicationEntityTitle() + - "\" has failed with DIMSE status " + DimseToHexString(rsp.msg.CGetRSP.DimseStatus), - rsp.msg.CGetRSP.DimseStatus); + "\" has failed with DIMSE status " + DimseToHexString(rsp.msg.CGetRSP.DimseStatus)) + .SetPayload(MakeDimseErrorStatusPayload(rsp.msg.CGetRSP.DimseStatus)); } } // Handle C-STORE Request
--- a/OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -24,6 +24,7 @@ #include "../PrecompiledHeaders.h" #include "DicomStoreUserConnection.h" +#include "DimseErrorPayload.h" #include "../DicomParsing/FromDcmtkBridge.h" #include "../DicomParsing/ParsedDicomFile.h" @@ -463,8 +464,8 @@ throw OrthancException(ErrorCode_NetworkProtocol, "C-STORE SCU to AET \"" + GetParameters().GetRemoteModality().GetApplicationEntityTitle() + - "\" has failed with DIMSE status " + DimseToHexString(response.DimseStatus), - response.DimseStatus); + "\" has failed with DIMSE status " + DimseToHexString(response.DimseStatus)) + .SetPayload(MakeDimseErrorStatusPayload(response.DimseStatus)); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/DicomNetworking/DimseErrorPayload.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -0,0 +1,66 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium + * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#include "../PrecompiledHeaders.h" +#include "DimseErrorPayload.h" + +#include "../SerializationToolbox.h" + +namespace Orthanc +{ + static const char* const DIMSE_ERROR_STATUS = "DimseErrorStatus"; // uint16_t + + + ErrorPayload MakeDimseErrorStatusPayload(uint16_t dimseErrorStatus) + { + Json::Value content; + content[DIMSE_ERROR_STATUS] = dimseErrorStatus; + + ErrorPayload payload; + payload.SetContent(ErrorPayloadType_Dimse, content); + return payload; + } + + + uint16_t GetDimseErrorStatusFromPayload(const ErrorPayload& payload) + { + if (payload.GetType() == ErrorPayloadType_Dimse) + { + unsigned int status = SerializationToolbox::ReadUnsignedInteger(payload.GetContent(), DIMSE_ERROR_STATUS); + + if (status <= 65535) + { + return static_cast<uint16_t>(status); + } + else + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + else + { + throw OrthancException(ErrorCode_BadParameterType); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/DicomNetworking/DimseErrorPayload.h Sat Nov 15 12:28:27 2025 +0100 @@ -0,0 +1,39 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium + * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_DCMTK_NETWORKING != 1 +# error The macro ORTHANC_ENABLE_DCMTK_NETWORKING must be set to 1 +#endif + +#include "../OrthancException.h" + + +namespace Orthanc +{ + ErrorPayload MakeDimseErrorStatusPayload(uint16_t dimseErrorStatus); + + uint16_t GetDimseErrorStatusFromPayload(const ErrorPayload& payload); +}
--- a/OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -141,7 +141,7 @@ T_ASC_SC_ROLE acceptedRole) { int n, i, k; - DUL_PRESENTATIONCONTEXT *dpc; + const DUL_PRESENTATIONCONTEXT *dpc; T_ASC_PresentationContext pc; n = ASC_countPresentationContexts(params);
--- a/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -275,10 +275,10 @@ else if (DecodePsmctRle1(psmct_, dataset)) { LOG(INFO) << "The PMSCT_RLE1 decoding has succeeded"; - Uint8* pixData = NULL; + const Uint8* pixData = NULL; if (psmct_.size() > 0) { - pixData = reinterpret_cast<Uint8*>(&psmct_[0]); + pixData = reinterpret_cast<const Uint8*>(&psmct_[0]); } slowAccessor_.reset(new DicomIntegerPixelAccessor(m, pixData, psmct_.size())); @@ -966,12 +966,12 @@ { throw OrthancException(ErrorCode_NotImplemented, "The built-in DCMTK decoder cannot decode some DICOM instance " - "whose transfer syntax is: " + std::string(GetTransferSyntaxUid(s)), false /* don't log here*/); + "whose transfer syntax is: " + std::string(GetTransferSyntaxUid(s)), false); } else { throw OrthancException(ErrorCode_NotImplemented, - "The built-in DCMTK decoder cannot decode some DICOM instance", false /* don't log here*/); + "The built-in DCMTK decoder cannot decode some DICOM instance", false); } }
--- a/OrthancFramework/Sources/Enumerations.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/Enumerations.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -1192,6 +1192,19 @@ } + const char* EnumerationToString(ErrorPayloadType type) + { + switch (type) + { + case ErrorPayloadType_Dimse: + return "Dimse"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + Encoding StringToEncoding(const char* encoding) { std::string s(encoding); @@ -1913,6 +1926,19 @@ } + ErrorPayloadType StringToErrorPayloadType(const std::string& type) + { + if (type == "Dimse") + { + return ErrorPayloadType_Dimse; + } + else + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + unsigned int GetBytesPerPixel(PixelFormat format) { switch (format)
--- a/OrthancFramework/Sources/Enumerations.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/Enumerations.h Sat Nov 15 12:28:27 2025 +0100 @@ -802,6 +802,12 @@ RetrieveMethod_SystemDefault = 65535 }; + enum ErrorPayloadType + { + ErrorPayloadType_None = 1, + ErrorPayloadType_Dimse = 2 + }; + ORTHANC_PUBLIC const char* EnumerationToString(ErrorCode code); @@ -861,6 +867,9 @@ const char* EnumerationToString(RetrieveMethod method); ORTHANC_PUBLIC + const char* EnumerationToString(ErrorPayloadType type); + + ORTHANC_PUBLIC Encoding StringToEncoding(const char* encoding); ORTHANC_PUBLIC @@ -893,6 +902,9 @@ ORTHANC_PUBLIC DicomToJsonFormat StringToDicomToJsonFormat(const std::string& format); + + ORTHANC_PUBLIC + ErrorPayloadType StringToErrorPayloadType(const std::string& type); ORTHANC_PUBLIC bool LookupMimeType(MimeType& target, @@ -915,7 +927,7 @@ bool IsResourceLevelAboveOrEqual(ResourceType level, ResourceType reference); -ORTHANC_PUBLIC + ORTHANC_PUBLIC const char* ResourceTypeToDicomQueryRetrieveLevel(ResourceType type); ORTHANC_PUBLIC
--- a/OrthancFramework/Sources/HttpServer/HttpOutput.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/HttpServer/HttpOutput.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -619,7 +619,7 @@ throw OrthancException(ErrorCode_InternalError); } - boundary = boundary.substr(0, 70); + boundary.resize(70); contentTypeHeader = ("multipart/" + subType + "; type=" + tmp + "; boundary=" + boundary); }
--- a/OrthancFramework/Sources/Images/ImageAccessor.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/Images/ImageAccessor.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -141,7 +141,7 @@ size_t ImageAccessor::GetSize() const { - return GetHeight() * GetPitch(); + return static_cast<size_t>(GetHeight()) * static_cast<size_t>(GetPitch()); } const void *ImageAccessor::GetConstBuffer() const
--- a/OrthancFramework/Sources/Images/ImageProcessing.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/Images/ImageProcessing.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -1224,7 +1224,7 @@ for (unsigned int y = 0; y < height; y++) { uint8_t* q = reinterpret_cast<uint8_t*>(image.GetRow(y)); - uint8_t* a = reinterpret_cast<uint8_t*>(alpha.GetRow(y)); + const uint8_t* a = reinterpret_cast<uint8_t*>(alpha.GetRow(y)); for (unsigned int x = 0; x < width; x++) {
--- a/OrthancFramework/Sources/Images/PamReader.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/Images/PamReader.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -207,8 +207,17 @@ if (enforceAligned_) { if (alignedImageBuffer_ != NULL) + { free(alignedImageBuffer_); + } + alignedImageBuffer_ = malloc(pitch * height); + + if (alignedImageBuffer_ == NULL) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + memcpy(alignedImageBuffer_, &content_[offset], pitch* height); content_ = ""; AssignWritable(format, width, height, pitch, alignedImageBuffer_);
--- a/OrthancFramework/Sources/JobsEngine/IJob.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/IJob.h Sat Nov 15 12:28:27 2025 +0100 @@ -24,6 +24,7 @@ #pragma once +#include "../OrthancException.h" #include "JobStepResult.h" #include <boost/noncopyable.hpp> @@ -81,5 +82,7 @@ virtual bool GetUserData(Json::Value& userData) const = 0; virtual void SetUserData(const Json::Value& userData) = 0; + + virtual void LookupErrorPayload(ErrorPayload& payload) const = 0; }; }
--- a/OrthancFramework/Sources/JobsEngine/JobInfo.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobInfo.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -196,9 +196,9 @@ target["UserData"] = status_.GetUserData(); } - if (status_.HasDimseErrorStatus()) + if (status_.GetErrorPayload().HasContent()) { - target["DimseErrorStatus"] = status_.GetDimseErrorStatus(); + status_.GetErrorPayload().Format(target["ErrorPayload"]); } target["Type"] = status_.GetJobType();
--- a/OrthancFramework/Sources/JobsEngine/JobStatus.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobStatus.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -34,13 +34,19 @@ progress_(0), jobType_("Invalid"), publicContent_(Json::objectValue), - hasSerialized_(false), - hasDimseErrorStatus_(false), - dimseErrorStatus_(0x0000) + hasSerialized_(false) { } - void JobStatus::InitInternal(const IJob& job) + + JobStatus::JobStatus(ErrorCode code, + const std::string& details, + const IJob& job) : + errorCode_(code), + progress_(job.GetProgress()), + publicContent_(Json::objectValue), + hasSerialized_(false), + details_(details) { if (progress_ < 0) { @@ -59,36 +65,6 @@ hasSerialized_ = job.Serialize(serialized_); } - JobStatus::JobStatus(ErrorCode code, - const std::string& details, - const IJob& job, - uint16_t dimseErrorStatus) : - errorCode_(code), - progress_(job.GetProgress()), - publicContent_(Json::objectValue), - hasSerialized_(false), - details_(details), - hasDimseErrorStatus_(true), - dimseErrorStatus_(dimseErrorStatus) - { - InitInternal(job); - } - - - JobStatus::JobStatus(ErrorCode code, - const std::string& details, - const IJob& job) : - errorCode_(code), - progress_(job.GetProgress()), - publicContent_(Json::objectValue), - hasSerialized_(false), - details_(details), - hasDimseErrorStatus_(false), - dimseErrorStatus_(0x0000) - { - InitInternal(job); - } - const Json::Value& JobStatus::GetSerialized() const {
--- a/OrthancFramework/Sources/JobsEngine/JobStatus.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobStatus.h Sat Nov 15 12:28:27 2025 +0100 @@ -25,7 +25,7 @@ #pragma once #include "IJob.h" -#include "../OrthancException.h" +#include "../OrthancException.h" // For ErrorPayload namespace Orthanc { @@ -39,22 +39,14 @@ Json::Value serialized_; bool hasSerialized_; std::string details_; - bool hasDimseErrorStatus_; - uint16_t dimseErrorStatus_; Json::Value userData_; + ErrorPayload errorPayload_; - void InitInternal(const IJob& job); - public: JobStatus(); JobStatus(ErrorCode code, const std::string& details, - const IJob& job, - uint16_t dimseErrorStatus); - - JobStatus(ErrorCode code, - const std::string& details, const IJob& job); ErrorCode GetErrorCode() const @@ -109,19 +101,14 @@ return userData_; } - bool HasDimseErrorStatus() const + ErrorPayload& GetErrorPayload() { - return hasDimseErrorStatus_; + return errorPayload_; } - uint16_t GetDimseErrorStatus() const + const ErrorPayload& GetErrorPayload() const { - if (!hasDimseErrorStatus_) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - return dimseErrorStatus_; + return errorPayload_; } }; }
--- a/OrthancFramework/Sources/JobsEngine/JobStepResult.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobStepResult.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -32,9 +32,7 @@ JobStepResult::JobStepResult() : code_(JobStepCode_Failure), timeout_(0), - error_(ErrorCode_InternalError), - hasDimseErrorStatus_(false), - dimseErrorStatus_(0x0000) + error_(ErrorCode_InternalError) { } @@ -72,12 +70,10 @@ JobStepResult JobStepResult::Failure(const ErrorCode& error, const char* details, - uint16_t dimseErrorStatus) + const ErrorPayload& payload) { JobStepResult result = Failure(error, details); - - result.hasDimseErrorStatus_ = true; - result.dimseErrorStatus_ = dimseErrorStatus; + result.failurePayload_ = payload; return result; } @@ -85,11 +81,11 @@ JobStepResult JobStepResult::Failure(const OrthancException& exception) { - if (exception.HasDimseErrorStatus()) + if (exception.GetPayload().HasContent()) { return Failure(exception.GetErrorCode(), exception.HasDetails() ? exception.GetDetails() : NULL, - exception.GetDimseErrorStatus()); + exception.GetPayload()); } else { @@ -141,20 +137,4 @@ throw OrthancException(ErrorCode_BadSequenceOfCalls); } } - - bool JobStepResult::HasDimseErrorStatus() const - { - return hasDimseErrorStatus_; - } - - uint16_t JobStepResult::GetDimseErrorStatus() const - { - if (!hasDimseErrorStatus_) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - return dimseErrorStatus_; - } - }
--- a/OrthancFramework/Sources/JobsEngine/JobStepResult.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobStepResult.h Sat Nov 15 12:28:27 2025 +0100 @@ -25,14 +25,14 @@ #pragma once #include "../Enumerations.h" +#include "../OrthancException.h" +#include <boost/shared_ptr.hpp> #include <json/value.h> #include <stdint.h> namespace Orthanc { - class OrthancException; - class ORTHANC_PUBLIC JobStepResult { private: @@ -40,16 +40,12 @@ unsigned int timeout_; ErrorCode error_; std::string failureDetails_; - bool hasDimseErrorStatus_; - uint16_t dimseErrorStatus_; - + ErrorPayload failurePayload_; explicit JobStepResult(JobStepCode code) : code_(code), timeout_(0), - error_(ErrorCode_Success), - hasDimseErrorStatus_(false), - dimseErrorStatus_(0x0000) + error_(ErrorCode_Success) { } @@ -67,7 +63,7 @@ static JobStepResult Failure(const ErrorCode& error, const char* details, - uint16_t dimseErrorStatus); + const ErrorPayload& payload); static JobStepResult Failure(const OrthancException& exception); @@ -79,8 +75,9 @@ const std::string& GetFailureDetails() const; - bool HasDimseErrorStatus() const; - - uint16_t GetDimseErrorStatus() const; + const ErrorPayload& GetFailurePayload() const + { + return failurePayload_; + } }; }
--- a/OrthancFramework/Sources/JobsEngine/JobsEngine.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobsEngine.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -87,14 +87,7 @@ case JobStepCode_Failure: running.GetJob().Stop(JobStopReason_Failure); - if (result.HasDimseErrorStatus()) - { - running.UpdateStatus(result.GetFailureCode(), result.GetFailureDetails(), result.GetDimseErrorStatus()); - } - else - { - running.UpdateStatus(result.GetFailureCode(), result.GetFailureDetails()); - } + running.UpdateStatus(result.GetFailureCode(), result.GetFailureDetails(), result.GetFailurePayload()); running.MarkFailure(); return false;
--- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -44,7 +44,8 @@ static const char* ERROR_CODE = "ErrorCode"; static const char* ERROR_DETAILS = "ErrorDetails"; static const char* USER_DATA = "UserData"; - static const char* DIMSE_ERROR_STATUS = "DimseErrorStatus"; + static const char* ERROR_PAYLOAD_TYPE = "ErrorPayloadType"; + static const char* ERROR_PAYLOAD = "ErrorPayload"; class JobsRegistry::JobHandler : public boost::noncopyable @@ -305,9 +306,10 @@ target[USER_DATA] = userData; } - if (lastStatus_.HasDimseErrorStatus()) + if (lastStatus_.GetErrorPayload().HasContent()) { - target[DIMSE_ERROR_STATUS] = lastStatus_.GetDimseErrorStatus(); + target[ERROR_PAYLOAD_TYPE] = lastStatus_.GetErrorPayload().GetType(); + target[ERROR_PAYLOAD] = lastStatus_.GetErrorPayload().GetContent(); } return true; @@ -361,13 +363,13 @@ job_->SetUserData(serialized[USER_DATA]); } - if (serialized.isMember(DIMSE_ERROR_STATUS)) + lastStatus_ = JobStatus(errorCode, details, *job_); + + if (serialized.isMember(ERROR_PAYLOAD_TYPE) && + serialized.isMember(ERROR_PAYLOAD)) { - lastStatus_ = JobStatus(errorCode, details, *job_, static_cast<uint16_t>(serialized[DIMSE_ERROR_STATUS].asUInt())); - } - else - { - lastStatus_ = JobStatus(errorCode, details, *job_); + const std::string type = SerializationToolbox::ReadString(serialized, ERROR_PAYLOAD_TYPE); + lastStatus_.GetErrorPayload().SetContent(StringToErrorPayloadType(type.c_str()), serialized[ERROR_PAYLOAD]); } job_->Start(); @@ -896,9 +898,17 @@ ErrorCode code = it->second->GetLastStatus().GetErrorCode(); const std::string& details = it->second->GetLastStatus().GetDetails(); - if (it->second->GetLastStatus().HasDimseErrorStatus()) + // Prefer the error payload from the job level if there is one since it should contain more information than the step error payload. + ErrorPayload jobErrorPayload; + it->second->GetJob().LookupErrorPayload(jobErrorPayload); + + if (jobErrorPayload.HasContent()) { - throw OrthancException(code, details, it->second->GetLastStatus().GetDimseErrorStatus()); + throw OrthancException(code, details).SetPayload(jobErrorPayload); + } + else if (it->second->GetLastStatus().GetErrorPayload().HasContent()) + { + throw OrthancException(code, details).SetPayload(it->second->GetLastStatus().GetErrorPayload()); } else if (!details.empty()) { @@ -1465,7 +1475,7 @@ void JobsRegistry::RunningJob::UpdateStatus(ErrorCode code, const std::string& details, - uint16_t dimseErrorStatus) + const ErrorPayload& errorPayload) { if (!IsValid()) { @@ -1473,7 +1483,8 @@ } else { - JobStatus status(code, details, *job_, dimseErrorStatus); + JobStatus status(code, details, *job_); + status.GetErrorPayload() = errorPayload; boost::mutex::scoped_lock lock(registry_.mutex_); registry_.CheckInvariants();
--- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.h Sat Nov 15 12:28:27 2025 +0100 @@ -246,7 +246,7 @@ void UpdateStatus(ErrorCode code, const std::string& details, - uint16_t dimseErrorStatus); + const ErrorPayload& errorPayload); }; }; }
--- a/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h Sat Nov 15 12:28:27 2025 +0100 @@ -150,5 +150,9 @@ { return false; } + + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE + { + } }; }
--- a/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h Sat Nov 15 12:28:27 2025 +0100 @@ -133,5 +133,8 @@ return false; } + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE + { + } }; }
--- a/OrthancFramework/Sources/OrthancException.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/OrthancException.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -30,12 +30,92 @@ namespace Orthanc { + ErrorPayload::ErrorPayload(const ErrorPayload& other) : + type_(other.type_) + { + if (other.content_.get() != NULL) + { + content_.reset(new Json::Value(*other.content_)); + } + } + + + ErrorPayload& ErrorPayload::operator= (const ErrorPayload& other) + { + if (other.HasContent()) + { + SetContent(other.GetType(), other.GetContent()); + } + else + { + ClearContent(); + } + + return *this; + } + + + void ErrorPayload::ClearContent() + { + type_ = ErrorPayloadType_None; + content_.reset(NULL); + } + + + void ErrorPayload::SetContent(ErrorPayloadType type, + const Json::Value& content) + { + if (type == ErrorPayloadType_None || + content.isNull()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + type_ = type; + content_.reset(new Json::Value(content)); + } + } + + + ErrorPayloadType ErrorPayload::GetType() const + { + if (HasContent()) + { + return type_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + const Json::Value& ErrorPayload::GetContent() const + { + if (HasContent()) + { + return *content_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + void ErrorPayload::Format(Json::Value& target) const + { + target["Type"] = EnumerationToString(GetType()); + target["Content"] = GetContent(); + } + + OrthancException::OrthancException(const OrthancException& other) : errorCode_(other.errorCode_), httpStatus_(other.httpStatus_), - logged_(false), - hasDimseErrorStatus_(other.hasDimseErrorStatus_), - dimseErrorStatus_(other.dimseErrorStatus_) + logged_(other.logged_), + payload_(other.payload_) { if (other.details_.get() != NULL) { @@ -46,9 +126,7 @@ OrthancException::OrthancException(ErrorCode errorCode) : errorCode_(errorCode), httpStatus_(ConvertErrorCodeToHttpStatus(errorCode)), - logged_(false), - hasDimseErrorStatus_(false), - dimseErrorStatus_(0x0000) + logged_(false) { } @@ -58,28 +136,7 @@ errorCode_(errorCode), httpStatus_(ConvertErrorCodeToHttpStatus(errorCode)), logged_(log), - details_(new std::string(details)), - hasDimseErrorStatus_(false), - dimseErrorStatus_(0x0000) - { -#if ORTHANC_ENABLE_LOGGING == 1 - if (log) - { - LOG(ERROR) << EnumerationToString(errorCode_) << ": " << details; - } -#endif - } - - OrthancException::OrthancException(ErrorCode errorCode, - const std::string& details, - uint16_t dimseErrorStatus, - bool log) : - errorCode_(errorCode), - httpStatus_(ConvertErrorCodeToHttpStatus(errorCode)), - logged_(log), - details_(new std::string(details)), - hasDimseErrorStatus_(true), - dimseErrorStatus_(dimseErrorStatus) + details_(new std::string(details)) { #if ORTHANC_ENABLE_LOGGING == 1 if (log) @@ -89,54 +146,6 @@ #endif } - OrthancException::OrthancException(ErrorCode errorCode, - HttpStatus httpStatus) : - errorCode_(errorCode), - httpStatus_(httpStatus), - logged_(false), - hasDimseErrorStatus_(false), - dimseErrorStatus_(0x0000) - { - } - - OrthancException::OrthancException(ErrorCode errorCode, - HttpStatus httpStatus, - const std::string& details, - uint16_t dimseErrorStatus, - bool log) : - errorCode_(errorCode), - httpStatus_(httpStatus), - logged_(log), - details_(new std::string(details)), - hasDimseErrorStatus_(true), - dimseErrorStatus_(dimseErrorStatus) - { -#if ORTHANC_ENABLE_LOGGING == 1 - if (log) - { - LOG(ERROR) << EnumerationToString(errorCode_) << ": " << details; - } -#endif - } - - OrthancException::OrthancException(ErrorCode errorCode, - HttpStatus httpStatus, - const std::string& details, - bool log) : - errorCode_(errorCode), - httpStatus_(httpStatus), - logged_(log), - details_(new std::string(details)), - hasDimseErrorStatus_(false), - dimseErrorStatus_(0x0000) - { -#if ORTHANC_ENABLE_LOGGING == 1 - if (log) - { - LOG(ERROR) << EnumerationToString(errorCode_) << ": " << details; - } -#endif - } ErrorCode OrthancException::GetErrorCode() const { @@ -169,25 +178,4 @@ return details_->c_str(); } } - - bool OrthancException::HasDimseErrorStatus() const - { - return hasDimseErrorStatus_; - } - - uint16_t OrthancException::GetDimseErrorStatus() const - { - if (!hasDimseErrorStatus_) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - return dimseErrorStatus_; - } - - bool OrthancException::HasBeenLogged() const - { - return logged_; - } - }
--- a/OrthancFramework/Sources/OrthancException.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/OrthancException.h Sat Nov 15 12:28:27 2025 +0100 @@ -28,10 +28,47 @@ #include "Enumerations.h" #include "OrthancFramework.h" +#include <json/value.h> #include <stdint.h> // For uint16_t namespace Orthanc { + class ORTHANC_PUBLIC ErrorPayload + { + private: + ErrorPayloadType type_; + std::unique_ptr<Json::Value> content_; + + public: + ErrorPayload() : + type_(ErrorPayloadType_None) + { + } + + ErrorPayload(const ErrorPayload& other); + + ErrorPayload& operator= (const ErrorPayload& other); + + void ClearContent(); + + void SetContent(ErrorPayloadType type, + const Json::Value& content); + + bool HasContent() const + { + return content_.get() != NULL; + } + + ErrorPayloadType GetType() const; + + const Json::Value& GetContent() const; + + void Format(Json::Value& target) const; + }; + + + // TODO: Shouldn't copies of OrthancException be avoided completely + // (i.e., tag OrthancException as boost::noncopyable)? class ORTHANC_PUBLIC OrthancException { private: @@ -47,10 +84,10 @@ std::unique_ptr<std::string> details_; // New in Orthanc 1.12.10 - bool hasDimseErrorStatus_; - uint16_t dimseErrorStatus_; - + ErrorPayload payload_; + public: + // WARNING: Do not add additional constructors, to prevent ambiguities in overloading OrthancException(const OrthancException& other); explicit OrthancException(ErrorCode errorCode); @@ -59,25 +96,6 @@ const std::string& details, bool log = true); - OrthancException(ErrorCode errorCode, - const std::string& details, - uint16_t dimseErrorStatus, - bool log = true); - - OrthancException(ErrorCode errorCode, - HttpStatus httpStatus); - - OrthancException(ErrorCode errorCode, - HttpStatus httpStatus, - const std::string& details, - bool log = true); - - OrthancException(ErrorCode errorCode, - HttpStatus httpStatus, - const std::string& details, - uint16_t dimseErrorStatus, - bool log = true); - ErrorCode GetErrorCode() const; HttpStatus GetHttpStatus() const; @@ -88,10 +106,33 @@ const char* GetDetails() const; - bool HasBeenLogged() const; + bool HasBeenLogged() const + { + return logged_; + } + + OrthancException& SetHttpStatus(HttpStatus status) + { + httpStatus_ = status; + return *this; + } - bool HasDimseErrorStatus() const; - - uint16_t GetDimseErrorStatus() const; + OrthancException& SetPayload(const ErrorPayload& payload) + { + payload_ = payload; + return *this; + } + + OrthancException& SetPayload(ErrorPayloadType type, + const Json::Value& content) + { + payload_.SetContent(type, content); + return *this; + } + + const ErrorPayload& GetPayload() const + { + return payload_; + } }; }
--- a/OrthancFramework/Sources/SystemToolbox.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/Sources/SystemToolbox.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -228,8 +228,7 @@ if (!IsRegularFile(path)) { throw OrthancException(ErrorCode_RegularFileExpected, - "The path does not point to a regular file: " + PathToUtf8(path), - log); + "The path does not point to a regular file: " + PathToUtf8(path), log); } try @@ -238,8 +237,7 @@ f.open(path, std::ifstream::in | std::ifstream::binary); if (!f.good()) { - throw OrthancException(ErrorCode_InexistentFile, "File not found: " + PathToUtf8(path), - log); + throw OrthancException(ErrorCode_InexistentFile, "File not found: " + PathToUtf8(path), log); } std::streamsize size = GetStreamSize(f);
--- a/OrthancFramework/UnitTestsSources/FrameworkTests.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/UnitTestsSources/FrameworkTests.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -1793,3 +1793,64 @@ ASSERT_FALSE(Toolbox::IsVersionAbove("18.19.20", 18, 20, 0)); ASSERT_FALSE(Toolbox::IsVersionAbove("18.19.20", 19, 0, 0)); } + + +TEST(OrthancException, ErrorPayload) +{ + ErrorPayload b; + + { + ErrorPayload a; + ASSERT_THROW(a.SetContent(ErrorPayloadType_None, ""), OrthancException); + ASSERT_FALSE(a.HasContent()); + ASSERT_THROW(a.GetType(), OrthancException); + ASSERT_THROW(a.GetContent(), OrthancException); + + { + ErrorPayload c(a); + ASSERT_FALSE(c.HasContent()); + ASSERT_THROW(c.GetType(), OrthancException); + ASSERT_THROW(c.GetContent(), OrthancException); + } + + b = a; + } + + ASSERT_FALSE(b.HasContent()); + ASSERT_THROW(b.GetType(), OrthancException); + ASSERT_THROW(b.GetContent(), OrthancException); + + { + ErrorPayload a; + a.SetContent(ErrorPayloadType_Dimse, "Hello"); + ASSERT_TRUE(a.HasContent()); + ASSERT_EQ(ErrorPayloadType_Dimse, a.GetType()); + ASSERT_TRUE(a.GetContent().isString()); + ASSERT_EQ("Hello", a.GetContent().asString()); + + { + ErrorPayload c(a); + ASSERT_TRUE(c.HasContent()); + ASSERT_EQ(ErrorPayloadType_Dimse, c.GetType()); + ASSERT_TRUE(c.GetContent().isString()); + ASSERT_EQ("Hello", c.GetContent().asString()); + + c = ErrorPayload(); + ASSERT_FALSE(c.HasContent()); + ASSERT_THROW(c.GetType(), OrthancException); + ASSERT_THROW(c.GetContent(), OrthancException); + } + + b = a; + + a.ClearContent(); + ASSERT_FALSE(a.HasContent()); + ASSERT_THROW(a.GetType(), OrthancException); + ASSERT_THROW(a.GetContent(), OrthancException); + } + + ASSERT_TRUE(b.HasContent()); + ASSERT_EQ(ErrorPayloadType_Dimse, b.GetType()); + ASSERT_TRUE(b.GetContent().isString()); + ASSERT_EQ("Hello", b.GetContent().asString()); +}
--- a/OrthancFramework/UnitTestsSources/JobsTests.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancFramework/UnitTestsSources/JobsTests.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -147,6 +147,10 @@ { return false; } + + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE + { + } };
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -1453,9 +1453,7 @@ if (HasErrorDetails()) { - throw OrthancException(static_cast<ErrorCode>(error), - GetErrorDetails(), - IsLogDetails()); + throw OrthancException(static_cast<ErrorCode>(error), GetErrorDetails(), IsLogDetails()); } else { @@ -2547,7 +2545,7 @@ } - static std::string GetAllowedMethods(_OrthancPluginChunkedRestCallback parameters) + static std::string GetAllowedMethods(const _OrthancPluginChunkedRestCallback& parameters) { std::string s;
--- a/OrthancServer/Plugins/Engine/PluginsJob.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Plugins/Engine/PluginsJob.h Sat Nov 15 12:28:27 2025 +0100 @@ -95,6 +95,9 @@ return false; } + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE + { + } }; }
--- a/OrthancServer/Resources/RunCppCheck.sh Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Resources/RunCppCheck.sh Sat Nov 15 12:28:27 2025 +0100 @@ -1,5 +1,5 @@ #!/bin/bash - +# note: this script was last tuned to run with cppcheck v2.17.1 set -ex CPPCHECK=cppcheck @@ -9,18 +9,11 @@ fi cat <<EOF > /tmp/cppcheck-suppressions.txt -constParameter:../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp -knownArgument:../../OrthancFramework/UnitTestsSources/ImageTests.cpp knownConditionTrueFalse:../../OrthancServer/Plugins/Engine/OrthancPlugins.cpp nullPointer:../../OrthancFramework/UnitTestsSources/RestApiTests.cpp:322 stlFindInsert:../../OrthancFramework/Sources/DicomFormat/DicomMap.cpp:1525 stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:166 stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:74 -stlFindInsert:../../OrthancServer/Sources/Database/MainDicomTagsRegistry.cpp:65 -stlFindInsert:../../OrthancServer/Sources/OrthancWebDav.cpp:328 -stlFindInsert:../../OrthancServer/Sources/ServerJobs/MergeStudyJob.cpp:41 -stlFindInsert:../../OrthancServer/Sources/ServerJobs/SplitStudyJob.cpp:191 -stlFindInsert:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:361 syntaxError:../../OrthancFramework/Sources/SQLite/FunctionContext.h:53 syntaxError:../../OrthancFramework/UnitTestsSources/DicomMapTests.cpp:74 syntaxError:../../OrthancFramework/UnitTestsSources/ZipTests.cpp:133 @@ -29,13 +22,9 @@ useInitializationList:../../OrthancFramework/Sources/Images/PngReader.cpp:91 useInitializationList:../../OrthancFramework/Sources/Images/PngWriter.cpp:99 useInitializationList:../../OrthancServer/Sources/ServerJobs/DicomModalityStoreJob.cpp:275 -assertWithSideEffect:../../OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp:277 -assertWithSideEffect:../../OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp:1026 assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:292 assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:391 -assertWithSideEffect:../../OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp:3066 assertWithSideEffect:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:286 -assertWithSideEffect:../../OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp:454 variableScope:../../OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp:228 variableScope:../../OrthancServer/Sources/ServerJobs/OrthancPeerStoreJob.cpp:94 uselessOverride:../../OrthancFramework/Sources/MultiThreading/IRunnableBySteps.h:35 @@ -66,15 +55,20 @@ throwInNoexceptFunction:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h:496 rethrowNoCurrentException:../../OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp rethrowNoCurrentException:../../OrthancFramework/UnitTestsSources/RestApiTests.cpp +constParameterPointer:../../OrthancFramework/Sources/Toolbox.cpp:3046 +constParameterCallback:../../OrthancServer/Sources/OrthancGetRequestHandler.cpp +constParameterCallback:../../OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp +constParameterCallback:../../OrthancFramework/Sources/DicomNetworking/Internals/StoreScp.cpp +constParameterCallback:../../OrthancFramework/Sources/Pkcs11.cpp +constParameterCallback:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp:3449 +unknownMacro:../../OrthancFramework/Sources/DicomParsing/DicomModification.cpp:39 EOF -# TODO: re-enable nullPointerOutOfMemory ${CPPCHECK} -j 8 --enable=all --quiet --std=c++11 \ --suppress=missingIncludeSystem \ --suppress=missingInclude \ --suppress=useStlAlgorithm \ - --suppress=nullPointerOutOfMemory \ --check-level=exhaustive \ --suppressions-list=/tmp/cppcheck-suppressions.txt \ -DBOOST_HAS_DATE_TIME=1 \
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -2718,12 +2718,14 @@ if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize)) { storeStatus_ = StoreStatus_StorageFull; - throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction + throw OrthancException(ErrorCode_FullStorage, "Maximum storage size reached") // throw to cancel the transaction + .SetHttpStatus(HttpStatus_507_InsufficientStorage); } if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_)) { storeStatus_ = StoreStatus_StorageFull; - throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction + throw OrthancException(ErrorCode_FullStorage, "Maximum patient count reached") // throw to cancel the transaction + .SetHttpStatus(HttpStatus_507_InsufficientStorage); } } else @@ -3015,7 +3017,7 @@ int64_t resourceId; if (!transaction.LookupResource(resourceId, resourceType, publicId_)) { - throw OrthancException(ErrorCode_InexistentItem, HttpStatus_404_NotFound); + throw OrthancException(ErrorCode_InexistentItem).SetHttpStatus(HttpStatus_404_NotFound); } else {
--- a/OrthancServer/Sources/DicomInstanceOrigin.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Sources/DicomInstanceOrigin.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -121,7 +121,7 @@ { if (origin_ == RequestOrigin_DicomProtocol) { - result = dicomRemoteAet_.c_str(); + result = dicomRemoteAet_; return true; } else
--- a/OrthancServer/Sources/ServerContext.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Sources/ServerContext.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -855,11 +855,11 @@ case StoreStatus_Failure: LOG(ERROR) << "Unknown store failure while storing instance " << resultPublicId; - throw OrthancException(ErrorCode_InternalError, HttpStatus_500_InternalServerError); + throw OrthancException(ErrorCode_InternalError).SetHttpStatus(HttpStatus_500_InternalServerError); case StoreStatus_StorageFull: LOG(ERROR) << "Storage full while storing instance " << resultPublicId; - throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage); + throw OrthancException(ErrorCode_FullStorage).SetHttpStatus(HttpStatus_507_InsufficientStorage); default: // This should never happen
--- a/OrthancServer/Sources/ServerJobs/ArchiveJob.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.h Sat Nov 15 12:28:27 2025 +0100 @@ -153,5 +153,9 @@ } return false; } + + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE + { + } }; }
--- a/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -24,6 +24,7 @@ #include "DicomGetScuJob.h" #include "../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" +#include "../../../OrthancFramework/Sources/DicomNetworking/DimseErrorPayload.h" #include "../../../OrthancFramework/Sources/SerializationToolbox.h" #include "../ServerContext.h" #include <dcmtk/dcmnet/dimse.h> @@ -100,8 +101,13 @@ } catch (OrthancException& e) { - dimseErrorStatus_ = e.GetDimseErrorStatus(); - throw e; + if (e.GetPayload().HasContent() && + e.GetPayload().GetType() == ErrorPayloadType_Dimse) + { + dimseErrorStatus_ = GetDimseErrorStatusFromPayload(e.GetPayload()); + } + + throw; } return true; } @@ -368,4 +374,12 @@ currentCommand_->AddReceivedInstance(instanceId); } } + + void DicomRetrieveScuBaseJob::LookupErrorPayload(ErrorPayload& payload) const + { + Json::Value publicContent; + GetPublicContent(publicContent); + + payload.SetContent(ErrorPayloadType_Dimse, publicContent["Details"]); + } }
--- a/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.h Sat Nov 15 12:28:27 2025 +0100 @@ -173,5 +173,7 @@ static void AddReceivedInstanceFromCStore(uint16_t originatorMessageId, const std::string& originatorAet, const std::string& instanceId); + + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE; }; }
--- a/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.h Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.h Sat Nov 15 12:28:27 2025 +0100 @@ -174,5 +174,9 @@ virtual void SetUserData(const Json::Value& userData) ORTHANC_OVERRIDE; virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE; + + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE + { + } }; }
--- a/OrthancServer/Sources/main.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/Sources/main.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -714,9 +714,9 @@ message["Details"] = exception.GetDetails(); } - if (exception.HasDimseErrorStatus()) + if (exception.GetPayload().HasContent()) { - message["DimseErrorStatus"] = exception.GetDimseErrorStatus(); + exception.GetPayload().Format(message["ErrorPayload"]); } std::string info = message.toStyledString();
--- a/OrthancServer/UnitTestsSources/ServerJobsTests.cpp Wed Nov 12 19:30:44 2025 +0100 +++ b/OrthancServer/UnitTestsSources/ServerJobsTests.cpp Sat Nov 15 12:28:27 2025 +0100 @@ -151,6 +151,10 @@ { return false; } + + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE + { + } }; @@ -226,6 +230,10 @@ { return false; } + + virtual void LookupErrorPayload(ErrorPayload& payload) const ORTHANC_OVERRIDE + { + } };
