Mercurial > hg > orthanc
comparison Plugins/Engine/OrthancPlugins.cpp @ 3393:2cd0369a156f
support of chunked answers in HttpClient and in SDK
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 06 Jun 2019 16:12:55 +0200 |
parents | ad434967a68c |
children | 0ce9b4f5fdf5 |
comparison
equal
deleted
inserted
replaced
3392:ad434967a68c | 3393:2cd0369a156f |
---|---|
41 #if !defined(DCMTK_VERSION_NUMBER) | 41 #if !defined(DCMTK_VERSION_NUMBER) |
42 # error The macro DCMTK_VERSION_NUMBER must be defined | 42 # error The macro DCMTK_VERSION_NUMBER must be defined |
43 #endif | 43 #endif |
44 | 44 |
45 | 45 |
46 #include "../../Core/ChunkedBuffer.h" | |
47 #include "../../Core/Compression/GzipCompressor.h" | 46 #include "../../Core/Compression/GzipCompressor.h" |
48 #include "../../Core/Compression/ZlibCompressor.h" | 47 #include "../../Core/Compression/ZlibCompressor.h" |
49 #include "../../Core/DicomFormat/DicomArray.h" | 48 #include "../../Core/DicomFormat/DicomArray.h" |
50 #include "../../Core/DicomParsing/DicomWebJsonVisitor.h" | 49 #include "../../Core/DicomParsing/DicomWebJsonVisitor.h" |
51 #include "../../Core/DicomParsing/FromDcmtkBridge.h" | 50 #include "../../Core/DicomParsing/FromDcmtkBridge.h" |
354 } | 353 } |
355 } | 354 } |
356 | 355 |
357 public: | 356 public: |
358 DicomWebBinaryFormatter(const _OrthancPluginEncodeDicomWeb& parameters) : | 357 DicomWebBinaryFormatter(const _OrthancPluginEncodeDicomWeb& parameters) : |
359 callback_(parameters.callback) | 358 callback_(parameters.callback) |
360 { | 359 { |
361 } | 360 } |
362 | 361 |
363 virtual DicomWebJsonVisitor::BinaryMode Format(std::string& bulkDataUri, | 362 virtual DicomWebJsonVisitor::BinaryMode Format(std::string& bulkDataUri, |
364 const std::vector<DicomTag>& parentTags, | 363 const std::vector<DicomTag>& parentTags, |
420 std::auto_ptr<std::string> errorDetails_; | 419 std::auto_ptr<std::string> errorDetails_; |
421 bool logDetails_; | 420 bool logDetails_; |
422 | 421 |
423 public: | 422 public: |
424 PluginHttpOutput(HttpOutput& output) : | 423 PluginHttpOutput(HttpOutput& output) : |
425 output_(output), | 424 output_(output), |
426 logDetails_(false) | 425 logDetails_(false) |
427 { | 426 { |
428 } | 427 } |
429 | 428 |
430 HttpOutput& GetOutput() | 429 HttpOutput& GetOutput() |
431 { | 430 { |
518 boost::mutex::scoped_lock lock_; | 517 boost::mutex::scoped_lock lock_; |
519 ServerContext* context_; | 518 ServerContext* context_; |
520 | 519 |
521 public: | 520 public: |
522 ServerContextLock(PImpl& that) : | 521 ServerContextLock(PImpl& that) : |
523 lock_(that.contextMutex_), | 522 lock_(that.contextMutex_), |
524 context_(that.context_) | 523 context_(that.context_) |
525 { | 524 { |
526 if (context_ == NULL) | 525 if (context_ == NULL) |
527 { | 526 { |
528 throw OrthancException(ErrorCode_DatabaseNotInitialized); | 527 throw OrthancException(ErrorCode_DatabaseNotInitialized); |
529 } | 528 } |
974 } | 973 } |
975 }; | 974 }; |
976 | 975 |
977 | 976 |
978 | 977 |
979 class OrthancPlugins::HttpRequestBody : public HttpClient::IRequestChunkedBody | 978 class OrthancPlugins::StreamingHttpRequest : public HttpClient::IRequestBody |
980 { | 979 { |
981 private: | 980 private: |
982 const _OrthancPluginHttpClientChunkedBody& params_; | 981 const _OrthancPluginStreamingHttpClient& params_; |
983 PluginsErrorDictionary& errorDictionary_; | 982 PluginsErrorDictionary& errorDictionary_; |
984 | 983 |
985 public: | 984 public: |
986 HttpRequestBody(const _OrthancPluginHttpClientChunkedBody& params, | 985 StreamingHttpRequest(const _OrthancPluginStreamingHttpClient& params, |
987 PluginsErrorDictionary& errorDictionary) : | 986 PluginsErrorDictionary& errorDictionary) : |
988 params_(params), | 987 params_(params), |
989 errorDictionary_(errorDictionary) | 988 errorDictionary_(errorDictionary) |
990 { | 989 { |
991 } | 990 } |
992 | 991 |
993 virtual bool ReadNextChunk(std::string& chunk) | 992 virtual bool ReadNextChunk(std::string& chunk) |
994 { | 993 { |
995 if (params_.requestBodyIsDone(params_.requestBody)) | 994 if (params_.requestIsDone(params_.request)) |
996 { | 995 { |
997 return false; | 996 return false; |
998 } | 997 } |
999 else | 998 else |
1000 { | 999 { |
1001 size_t size = params_.requestBodyChunkSize(params_.requestBody); | 1000 size_t size = params_.requestChunkSize(params_.request); |
1002 | 1001 |
1003 chunk.resize(size); | 1002 chunk.resize(size); |
1004 | 1003 |
1005 if (size != 0) | 1004 if (size != 0) |
1006 { | 1005 { |
1007 const void* data = params_.requestBodyChunkData(params_.requestBody); | 1006 const void* data = params_.requestChunkData(params_.request); |
1008 memcpy(&chunk[0], data, size); | 1007 memcpy(&chunk[0], data, size); |
1009 } | 1008 } |
1010 | 1009 |
1011 OrthancPluginErrorCode error = params_.requestBodyNext(params_.requestBody); | 1010 OrthancPluginErrorCode error = params_.requestNext(params_.request); |
1012 | 1011 |
1013 if (error != OrthancPluginErrorCode_Success) | 1012 if (error != OrthancPluginErrorCode_Success) |
1014 { | 1013 { |
1015 errorDictionary_.LogError(error, true); | 1014 errorDictionary_.LogError(error, true); |
1016 throw OrthancException(static_cast<ErrorCode>(error)); | 1015 throw OrthancException(static_cast<ErrorCode>(error)); |
1017 } | 1016 } |
1018 else | 1017 else |
1019 { | 1018 { |
1020 return true; | 1019 return true; |
1021 } | 1020 } |
1021 } | |
1022 } | |
1023 }; | |
1024 | |
1025 | |
1026 class OrthancPlugins::StreamingHttpAnswer : public HttpClient::IAnswer | |
1027 { | |
1028 private: | |
1029 const _OrthancPluginStreamingHttpClient& params_; | |
1030 PluginsErrorDictionary& errorDictionary_; | |
1031 | |
1032 public: | |
1033 StreamingHttpAnswer(const _OrthancPluginStreamingHttpClient& params, | |
1034 PluginsErrorDictionary& errorDictionary) : | |
1035 params_(params), | |
1036 errorDictionary_(errorDictionary) | |
1037 { | |
1038 } | |
1039 | |
1040 virtual void AddHeader(const std::string& key, | |
1041 const std::string& value) | |
1042 { | |
1043 OrthancPluginErrorCode error = params_.answerAddHeader(params_.answer, key.c_str(), value.c_str()); | |
1044 | |
1045 if (error != OrthancPluginErrorCode_Success) | |
1046 { | |
1047 errorDictionary_.LogError(error, true); | |
1048 throw OrthancException(static_cast<ErrorCode>(error)); | |
1049 } | |
1050 } | |
1051 | |
1052 virtual void AddChunk(const void* data, | |
1053 size_t size) | |
1054 { | |
1055 OrthancPluginErrorCode error = params_.answerAddChunk(params_.answer, data, size); | |
1056 | |
1057 if (error != OrthancPluginErrorCode_Success) | |
1058 { | |
1059 errorDictionary_.LogError(error, true); | |
1060 throw OrthancException(static_cast<ErrorCode>(error)); | |
1022 } | 1061 } |
1023 } | 1062 } |
1024 }; | 1063 }; |
1025 | 1064 |
1026 | 1065 |
2083 | 2122 |
2084 CopyToMemoryBuffer(*p.target, compressed.size() > 0 ? compressed.c_str() : NULL, compressed.size()); | 2123 CopyToMemoryBuffer(*p.target, compressed.size() > 0 ? compressed.c_str() : NULL, compressed.size()); |
2085 } | 2124 } |
2086 | 2125 |
2087 | 2126 |
2088 static void RunHttpClient(HttpClient& client, | 2127 static void SetupHttpClient(HttpClient& client, |
2089 const _OrthancPluginCallHttpClient2& parameters) | 2128 const _OrthancPluginCallHttpClient2& parameters) |
2090 { | 2129 { |
2091 client.SetUrl(parameters.url); | 2130 client.SetUrl(parameters.url); |
2092 client.SetConvertHeadersToLowerCase(false); | 2131 client.SetConvertHeadersToLowerCase(false); |
2093 | 2132 |
2094 if (parameters.timeout != 0) | 2133 if (parameters.timeout != 0) |
2152 break; | 2191 break; |
2153 | 2192 |
2154 default: | 2193 default: |
2155 throw OrthancException(ErrorCode_ParameterOutOfRange); | 2194 throw OrthancException(ErrorCode_ParameterOutOfRange); |
2156 } | 2195 } |
2196 } | |
2197 | |
2198 | |
2199 static void ExecuteHttpClientWithoutStream(uint16_t& httpStatus, | |
2200 OrthancPluginMemoryBuffer* answerBody, | |
2201 OrthancPluginMemoryBuffer* answerHeaders, | |
2202 HttpClient& client) | |
2203 { | |
2204 if (answerBody == NULL) | |
2205 { | |
2206 throw OrthancException(ErrorCode_NullPointer); | |
2207 } | |
2157 | 2208 |
2158 std::string body; | 2209 std::string body; |
2159 HttpClient::HttpHeaders headers; | 2210 HttpClient::HttpHeaders headers; |
2160 | 2211 |
2161 bool success = client.Apply(body, headers); | 2212 bool success = client.Apply(body, headers); |
2162 | 2213 |
2163 // The HTTP request has succeeded | 2214 // The HTTP request has succeeded |
2164 *parameters.httpStatus = static_cast<uint16_t>(client.GetLastStatus()); | 2215 httpStatus = static_cast<uint16_t>(client.GetLastStatus()); |
2165 | 2216 |
2166 if (!success) | 2217 if (!success) |
2167 { | 2218 { |
2168 HttpClient::ThrowException(client.GetLastStatus()); | 2219 HttpClient::ThrowException(client.GetLastStatus()); |
2169 } | 2220 } |
2170 | 2221 |
2171 // Copy the HTTP headers of the answer, if the plugin requested them | 2222 // Copy the HTTP headers of the answer, if the plugin requested them |
2172 if (parameters.answerHeaders != NULL) | 2223 if (answerHeaders != NULL) |
2173 { | 2224 { |
2174 Json::Value json = Json::objectValue; | 2225 Json::Value json = Json::objectValue; |
2175 | 2226 |
2176 for (HttpClient::HttpHeaders::const_iterator | 2227 for (HttpClient::HttpHeaders::const_iterator |
2177 it = headers.begin(); it != headers.end(); ++it) | 2228 it = headers.begin(); it != headers.end(); ++it) |
2178 { | 2229 { |
2179 json[it->first] = it->second; | 2230 json[it->first] = it->second; |
2180 } | 2231 } |
2181 | 2232 |
2182 std::string s = json.toStyledString(); | 2233 std::string s = json.toStyledString(); |
2183 CopyToMemoryBuffer(*parameters.answerHeaders, s); | 2234 CopyToMemoryBuffer(*answerHeaders, s); |
2184 } | 2235 } |
2185 | 2236 |
2186 // Copy the body of the answer if it makes sense | 2237 // Copy the body of the answer if it makes sense |
2187 if (parameters.method != OrthancPluginHttpMethod_Delete) | 2238 if (client.GetMethod() != HttpMethod_Delete) |
2188 { | 2239 { |
2189 CopyToMemoryBuffer(*parameters.answerBody, body); | 2240 CopyToMemoryBuffer(*answerBody, body); |
2190 } | 2241 } |
2191 } | 2242 } |
2192 | 2243 |
2193 | 2244 |
2194 void OrthancPlugins::CallHttpClient(const void* parameters) | 2245 void OrthancPlugins::CallHttpClient(const void* parameters) |
2195 { | 2246 { |
2196 const _OrthancPluginCallHttpClient& p = *reinterpret_cast<const _OrthancPluginCallHttpClient*>(parameters); | 2247 const _OrthancPluginCallHttpClient& p = *reinterpret_cast<const _OrthancPluginCallHttpClient*>(parameters); |
2197 | |
2198 _OrthancPluginCallHttpClient2 converted; | |
2199 memset(&converted, 0, sizeof(converted)); | |
2200 | |
2201 uint16_t httpStatus; | |
2202 | |
2203 converted.answerBody = p.target; | |
2204 converted.answerHeaders = NULL; | |
2205 converted.httpStatus = &httpStatus; | |
2206 converted.method = p.method; | |
2207 converted.url = p.url; | |
2208 converted.headersCount = 0; | |
2209 converted.headersKeys = NULL; | |
2210 converted.headersValues = NULL; | |
2211 converted.body = p.body; | |
2212 converted.bodySize = p.bodySize; | |
2213 converted.username = p.username; | |
2214 converted.password = p.password; | |
2215 converted.timeout = 0; // Use default timeout | |
2216 converted.certificateFile = NULL; | |
2217 converted.certificateKeyFile = NULL; | |
2218 converted.certificateKeyPassword = NULL; | |
2219 converted.pkcs11 = false; | |
2220 | 2248 |
2221 HttpClient client; | 2249 HttpClient client; |
2222 RunHttpClient(client, converted); | 2250 |
2251 { | |
2252 _OrthancPluginCallHttpClient2 converted; | |
2253 memset(&converted, 0, sizeof(converted)); | |
2254 | |
2255 converted.answerBody = NULL; | |
2256 converted.answerHeaders = NULL; | |
2257 converted.httpStatus = NULL; | |
2258 converted.method = p.method; | |
2259 converted.url = p.url; | |
2260 converted.headersCount = 0; | |
2261 converted.headersKeys = NULL; | |
2262 converted.headersValues = NULL; | |
2263 converted.body = p.body; | |
2264 converted.bodySize = p.bodySize; | |
2265 converted.username = p.username; | |
2266 converted.password = p.password; | |
2267 converted.timeout = 0; // Use default timeout | |
2268 converted.certificateFile = NULL; | |
2269 converted.certificateKeyFile = NULL; | |
2270 converted.certificateKeyPassword = NULL; | |
2271 converted.pkcs11 = false; | |
2272 | |
2273 SetupHttpClient(client, converted); | |
2274 } | |
2275 | |
2276 uint16_t status; | |
2277 ExecuteHttpClientWithoutStream(status, p.target, NULL, client); | |
2223 } | 2278 } |
2224 | 2279 |
2225 | 2280 |
2226 void OrthancPlugins::CallHttpClient2(const void* parameters) | 2281 void OrthancPlugins::CallHttpClient2(const void* parameters) |
2227 { | 2282 { |
2228 const _OrthancPluginCallHttpClient2& p = *reinterpret_cast<const _OrthancPluginCallHttpClient2*>(parameters); | 2283 const _OrthancPluginCallHttpClient2& p = *reinterpret_cast<const _OrthancPluginCallHttpClient2*>(parameters); |
2229 | 2284 |
2285 if (p.httpStatus == NULL) | |
2286 { | |
2287 throw OrthancException(ErrorCode_NullPointer); | |
2288 } | |
2289 | |
2230 HttpClient client; | 2290 HttpClient client; |
2231 | 2291 |
2232 if (p.method == OrthancPluginHttpMethod_Post || | 2292 if (p.method == OrthancPluginHttpMethod_Post || |
2233 p.method == OrthancPluginHttpMethod_Put) | 2293 p.method == OrthancPluginHttpMethod_Put) |
2234 { | 2294 { |
2235 client.GetBody().assign(p.body, p.bodySize); | 2295 client.GetBody().assign(p.body, p.bodySize); |
2236 } | 2296 } |
2237 | 2297 |
2238 RunHttpClient(client, p); | 2298 SetupHttpClient(client, p); |
2239 } | 2299 ExecuteHttpClientWithoutStream(*p.httpStatus, p.answerBody, p.answerHeaders, client); |
2240 | 2300 } |
2241 | 2301 |
2242 void OrthancPlugins::HttpClientChunkedBody(const void* parameters) | 2302 |
2243 { | 2303 void OrthancPlugins::StreamingHttpClient(const void* parameters) |
2244 const _OrthancPluginHttpClientChunkedBody& p = | 2304 { |
2245 *reinterpret_cast<const _OrthancPluginHttpClientChunkedBody*>(parameters); | 2305 const _OrthancPluginStreamingHttpClient& p = |
2306 *reinterpret_cast<const _OrthancPluginStreamingHttpClient*>(parameters); | |
2307 | |
2308 if (p.httpStatus == NULL) | |
2309 { | |
2310 throw OrthancException(ErrorCode_NullPointer); | |
2311 } | |
2312 | |
2313 HttpClient client; | |
2314 | |
2315 { | |
2316 _OrthancPluginCallHttpClient2 converted; | |
2317 memset(&converted, 0, sizeof(converted)); | |
2318 | |
2319 converted.answerBody = NULL; | |
2320 converted.answerHeaders = NULL; | |
2321 converted.httpStatus = NULL; | |
2322 converted.method = p.method; | |
2323 converted.url = p.url; | |
2324 converted.headersCount = p.headersCount; | |
2325 converted.headersKeys = p.headersKeys; | |
2326 converted.headersValues = p.headersValues; | |
2327 converted.body = NULL; | |
2328 converted.bodySize = 0; | |
2329 converted.username = p.username; | |
2330 converted.password = p.password; | |
2331 converted.timeout = p.timeout; | |
2332 converted.certificateFile = p.certificateFile; | |
2333 converted.certificateKeyFile = p.certificateKeyFile; | |
2334 converted.certificateKeyPassword = p.certificateKeyPassword; | |
2335 converted.pkcs11 = p.pkcs11; | |
2336 | |
2337 SetupHttpClient(client, converted); | |
2338 } | |
2246 | 2339 |
2247 if (p.method != OrthancPluginHttpMethod_Post && | 2340 StreamingHttpRequest body(p, pimpl_->dictionary_); |
2248 p.method != OrthancPluginHttpMethod_Put) | |
2249 { | |
2250 throw OrthancException(ErrorCode_ParameterOutOfRange, | |
2251 "This plugin service is only allowed for PUT and POST HTTP requests"); | |
2252 } | |
2253 | |
2254 HttpRequestBody body(p, pimpl_->dictionary_); | |
2255 | |
2256 HttpClient client; | |
2257 client.SetBody(body); | 2341 client.SetBody(body); |
2258 | 2342 |
2259 _OrthancPluginCallHttpClient2 converted; | 2343 StreamingHttpAnswer answer(p, pimpl_->dictionary_); |
2260 memset(&converted, 0, sizeof(converted)); | 2344 |
2261 | 2345 bool success = client.Apply(answer); |
2262 converted.answerBody = p.answerBody; | 2346 |
2263 converted.answerHeaders = p.answerHeaders; | 2347 *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus()); |
2264 converted.httpStatus = p.httpStatus; | 2348 |
2265 converted.method = p.method; | 2349 if (!success) |
2266 converted.url = p.url; | 2350 { |
2267 converted.headersCount = p.headersCount; | 2351 HttpClient::ThrowException(client.GetLastStatus()); |
2268 converted.headersKeys = p.headersKeys; | 2352 } |
2269 converted.headersValues = p.headersValues; | |
2270 converted.body = NULL; | |
2271 converted.bodySize = 0; | |
2272 converted.username = p.username; | |
2273 converted.password = p.password; | |
2274 converted.timeout = p.timeout; | |
2275 converted.certificateFile = p.certificateFile; | |
2276 converted.certificateKeyFile = p.certificateKeyFile; | |
2277 converted.certificateKeyPassword = p.certificateKeyPassword; | |
2278 converted.pkcs11 = p.pkcs11; | |
2279 | |
2280 RunHttpClient(client, converted); | |
2281 } | 2353 } |
2282 | 2354 |
2283 | 2355 |
2284 void OrthancPlugins::CallPeerApi(const void* parameters) | 2356 void OrthancPlugins::CallPeerApi(const void* parameters) |
2285 { | 2357 { |
2957 | 3029 |
2958 case _OrthancPluginService_CallHttpClient2: | 3030 case _OrthancPluginService_CallHttpClient2: |
2959 CallHttpClient2(parameters); | 3031 CallHttpClient2(parameters); |
2960 return true; | 3032 return true; |
2961 | 3033 |
2962 case _OrthancPluginService_HttpClientChunkedBody: | 3034 case _OrthancPluginService_StreamingHttpClient: |
2963 HttpClientChunkedBody(parameters); | 3035 StreamingHttpClient(parameters); |
2964 return true; | 3036 return true; |
2965 | 3037 |
2966 case _OrthancPluginService_ConvertPixelFormat: | 3038 case _OrthancPluginService_ConvertPixelFormat: |
2967 ConvertPixelFormat(parameters); | 3039 ConvertPixelFormat(parameters); |
2968 return true; | 3040 return true; |