From 50d264087628f158b06d974bbacc368f51578613 Mon Sep 17 00:00:00 2001 From: Salim Kanoun Date: Mon, 5 Jun 2023 22:32:29 +0200 Subject: [PATCH] accept both accept header and transfer syntax uid --- .../cornerstone/src/initWADOImageLoader.js | 43 ++++---------- .../default/src/DicomWebDataSource/index.js | 50 ++++------------ .../core/src/utils/generateAcceptHeader.ts | 58 +++++++++++++++++++ platform/core/src/utils/index.js | 3 + .../docs/configuration/configurationFiles.md | 1 + 5 files changed, 84 insertions(+), 71 deletions(-) create mode 100644 platform/core/src/utils/generateAcceptHeader.ts diff --git a/extensions/cornerstone/src/initWADOImageLoader.js b/extensions/cornerstone/src/initWADOImageLoader.js index 89f5e2c03..8b2aec1cc 100644 --- a/extensions/cornerstone/src/initWADOImageLoader.js +++ b/extensions/cornerstone/src/initWADOImageLoader.js @@ -5,7 +5,7 @@ import dicomImageLoader, { webWorkerManager, } from '@cornerstonejs/dicom-image-loader'; import dicomParser from 'dicom-parser'; -import { errorHandler } from '@ohif/core'; +import { errorHandler, utils } from '@ohif/core'; const { registerVolumeLoader } = volumeLoader; @@ -55,14 +55,19 @@ export default function initWADOImageLoader( // we should set this flag to false. convertFloatPixelDataToInt: false, }, - beforeSend: function (xhr) { + beforeSend: function(xhr) { //TODO should be removed in the future and request emitted by DicomWebDataSource - const sourceConfig = extensionManager.getActiveDataSource()?.[0].getConfig() ?? {} + const sourceConfig = + extensionManager.getActiveDataSource()?.[0].getConfig() ?? {}; const headers = userAuthenticationService.getAuthorizationHeader(); - const acceptHeader = generateAcceptHeader(sourceConfig.acceptHeader, sourceConfig.omitQuotationForMultipartRequest) + const acceptHeader = utils.generateAcceptHeader( + sourceConfig.acceptHeader, + sourceConfig.requestTransferSyntaxUID, + sourceConfig.omitQuotationForMultipartRequest + ); const xhrRequestHeaders = { - Accept: acceptHeader + Accept: acceptHeader, }; if (headers) { @@ -79,34 +84,6 @@ export default function initWADOImageLoader( initWebWorkers(appConfig); } -const generateAcceptHeader = (configAcceptHeader = [], omitQuotationForMultipartRequest = false) => { - let acceptHeader = [] - if (configAcceptHeader.length === 0) { - acceptHeader.push('multipart/related; type=application/octet-stream') - } else { - acceptHeader = configAcceptHeader - } - - if (!omitQuotationForMultipartRequest) { - //need to add quotation for each mime type of each accept entry - acceptHeader = acceptHeader.map(accept => { - let mimes = accept.split("; ") - let newMimes = mimes.map(mime => { - if (mime.startsWith('type=')) { - const quotedParam = 'type=' + '"' + mime.substring(5, mime.length) + '"' - return quotedParam - } else { - return mime - } - }) - return newMimes.join(';') - }) - } - - return acceptHeader - -} - export function destroy() { // Note: we don't want to call .terminate on the webWorkerManager since // that resets the config diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js index 5c31ec042..c0f05b4d2 100644 --- a/extensions/default/src/DicomWebDataSource/index.js +++ b/extensions/default/src/DicomWebDataSource/index.js @@ -61,17 +61,22 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) { staticWado, singlepart, omitQuotationForMultipartRequest, - acceptHeader + acceptHeader, + requestTransferSyntaxUID, } = dicomWebConfig; const dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig)); //Generate accept header depending on config params - let formattedAcceptHeader = generateAcceptHeader(acceptHeader, omitQuotationForMultipartRequest) + let formattedAcceptHeader = utils.generateAcceptHeader( + acceptHeader, + requestTransferSyntaxUID, + omitQuotationForMultipartRequest + ); - const authHeaders = userAuthenticationService.getAuthorizationHeader() + const authHeaders = userAuthenticationService.getAuthorizationHeader(); - const xhrRequestHeaders = {} + const xhrRequestHeaders = {}; if (authHeaders && authHeaders.Authorization) { xhrRequestHeaders.Authorization = authHeaders.Authorization; @@ -91,7 +96,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) { singlepart, headers: { ...xhrRequestHeaders, - Accept: formattedAcceptHeader + Accept: formattedAcceptHeader, }, errorInterceptor: errorHandler.getHTTPErrorHandler(), }; @@ -125,8 +130,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) { query: { studies: { mapParams: mapParams.bind(), - search: async function (origParams) { - + search: async function(origParams) { const { studyInstanceUid, seriesInstanceUid, ...mappedParams } = mapParams(origParams, { supportsFuzzyMatching, @@ -146,7 +150,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) { }, series: { // mapParams: mapParams.bind(), - search: async function (studyInstanceUid) { + search: async function(studyInstanceUid) { const results = await seriesInStudy( qidoDicomWebClient, studyInstanceUid @@ -202,7 +206,6 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) { sortFunction, madeInClient = false, } = {}) => { - if (!StudyInstanceUID) { throw new Error( 'Unable to query for SeriesMetadata without StudyInstanceUID' @@ -232,7 +235,6 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) { store: { dicom: async (dataset, request) => { - if (dataset instanceof ArrayBuffer) { const options = { datasets: [dataset], @@ -518,32 +520,4 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) { return IWebApiDataSource.create(implementation); } -const generateAcceptHeader = (configAcceptHeader = [], omitQuotationForMultipartRequest = false) => { - let acceptHeader = [] - if (configAcceptHeader.length === 0) { - acceptHeader.push('multipart/related; type=application/octet-stream') - } else { - acceptHeader = configAcceptHeader - } - - if (!omitQuotationForMultipartRequest) { - //need to add quotation for each mime type of each accept entry - acceptHeader = acceptHeader.map(accept => { - let mimes = accept.split("; ") - let newMimes = mimes.map(mime => { - if (mime.startsWith('type=')) { - const quotedParam = 'type=' + '"' + mime.substring(5, mime.length) + '"' - return quotedParam - } else { - return mime - } - }) - return newMimes.join(';') - }) - } - - return acceptHeader - -} - export { createDicomWebApi }; diff --git a/platform/core/src/utils/generateAcceptHeader.ts b/platform/core/src/utils/generateAcceptHeader.ts new file mode 100644 index 000000000..7de060726 --- /dev/null +++ b/platform/core/src/utils/generateAcceptHeader.ts @@ -0,0 +1,58 @@ +const generateAcceptHeader = ( + configAcceptHeader = [], + requestTransferSyntaxUID = null, + omitQuotationForMultipartRequest = false +): string[] => { + //if acceptedHeader is passed by config use it as it. + if (configAcceptHeader.length > 0) { + return configAcceptHeader; + } + + let acceptHeader = ['multipart/related']; + if (requestTransferSyntaxUID && typeForTS[requestTransferSyntaxUID]) { + const type = typeForTS[requestTransferSyntaxUID]; + acceptHeader.push('type=' + type); + acceptHeader.push('transfer-syntax=' + requestTransferSyntaxUID); + } else { + acceptHeader.push('type=application/octet-stream'); + } + + if (!omitQuotationForMultipartRequest) { + //need to add quotation for each mime type of each accept entry + acceptHeader = acceptHeader.map(mime => { + if (mime.startsWith('type=')) { + const quotedParam = 'type="' + mime.substring(5, mime.length) + '"'; + return quotedParam; + } + if (mime.startsWith('transfer-syntax=')) { + const quotedParam = + 'transfer-syntax="' + mime.substring(16, mime.length) + '"'; + return quotedParam; + } else { + return mime; + } + }); + } + + return [acceptHeader.join('; ')]; +}; + +const typeForTS = { + '*': 'application/octet-stream', + '1.2.840.10008.1.2.1': 'application/octet-stream', + '1.2.840.10008.1.2': 'application/octet-stream', + '1.2.840.10008.1.2.2': 'application/octet-stream', + '1.2.840.10008.1.2.4.70': 'image/jpeg', + '1.2.840.10008.1.2.4.50': 'image/jpeg', + '1.2.840.10008.1.2.4.51': 'image/dicom+jpeg', + '1.2.840.10008.1.2.4.57': 'image/jpeg', + '1.2.840.10008.1.2.5': 'image/dicom-rle', + '1.2.840.10008.1.2.4.80': 'image/jls', + '1.2.840.10008.1.2.4.81': 'image/jls', + '1.2.840.10008.1.2.4.90': 'image/jp2', + '1.2.840.10008.1.2.4.91': 'image/jp2', + '1.2.840.10008.1.2.4.92': 'image/jpx', + '1.2.840.10008.1.2.4.93': 'image/jpx', +}; + +export default generateAcceptHeader; diff --git a/platform/core/src/utils/index.js b/platform/core/src/utils/index.js index 06e7537f3..4b801d054 100644 --- a/platform/core/src/utils/index.js +++ b/platform/core/src/utils/index.js @@ -13,6 +13,7 @@ import Queue from './Queue'; import isDicomUid from './isDicomUid'; import formatDate from './formatDate'; import formatPN from './formatPN'; +import generateAcceptHeader from './generateAcceptHeader'; import resolveObjectPath from './resolveObjectPath'; import hierarchicalListUtils from './hierarchicalListUtils'; import progressTrackingUtils from './progressTrackingUtils'; @@ -74,6 +75,7 @@ const utils = { subscribeToNextViewportGridChange, splitComma, getSplitParam, + generateAcceptHeader, }; export { @@ -105,6 +107,7 @@ export { downloadCSVReport, splitComma, getSplitParam, + generateAcceptHeader, }; export default utils; diff --git a/platform/docs/docs/configuration/configurationFiles.md b/platform/docs/docs/configuration/configurationFiles.md index 21239030c..5ce871583 100644 --- a/platform/docs/docs/configuration/configurationFiles.md +++ b/platform/docs/docs/configuration/configurationFiles.md @@ -113,6 +113,7 @@ Here are a list of some options available: decoding. Defaults to minimum of `navigator.hardwareConcurrency` and what is specified by `maxNumberOfWebWorkers`. Some windows machines require smaller values. - `acceptHeader` : accept header to request specific dicom transfer syntax ex : [ 'multipart/related; type=image/jls; q=1', 'multipart/related; type=application/octet-stream; q=0.1' ] +- `requestTransferSyntaxUID` : Request a specific Tansfer syntax from dicom web server ex: 1.2.840.10008.1.2.4.80 (applyed only if acceptHeader is not set) - `omitQuotationForMultipartRequest`: Some servers (e.g., .NET) require the `multipart/related` request to be sent without quotation marks. Defaults to `false`. If your server doesn't require this, then setting this flag to `true` might improve performance (by removing the need for preflight requests). Also note that if auth headers are used, a preflight request is required. - `maxNumRequests`: The maximum number of requests to allow in parallel. It is an object with keys of `interaction`, `thumbnail`, and `prefetch`. You can specify a specific number for each type.