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;