import { NextFunction , Request , RequestHandler , Response } from "express" ; import uuid from "uuid/v4" ; import logger from "../../common/Logger" ; import { IP_REGEX } from "../../common/utils/constants" ; import { NO_ACCOUNT_HOST_NAMES } from "../../common/utils/constants" ; import BlobStorageContext from "../context/BlobStorageContext" ; import StorageErrorFactory from "../errors/StorageErrorFactory" ; import { DEFAULT_CONTEXT_PATH , HeaderConstants , SECONDARY_SUFFIX , ValidAPIVersions , VERSION } from "../utils/constants" ; import { checkApiVersion , validateContainerName } from "../utils/utils" ; export default function createStorageBlobContextMiddleware ( skipApiVersionCheck ? , disableProductStyleUrl ? ) { return ( req , res , next ) => { return blobStorageContextMiddleware ( req , res , next , skipApiVersionCheck , disableProductStyleUrl ) ; } ; } export param { Request } req param { Response } res param { NextFunction } next export function blobStorageContextMiddleware ( req , res , next , skipApiVersionCheck ? , disableProductStyleUrl ? ) { res . setHeader ( HeaderConstants . SERVER , `Azurite-Blob/${ VERSION }` ) ; const requestID = uuid ( ) ; if ( ! skipApiVersionCheck ) { const apiVersion = req . header ( HeaderConstants . X_MS_VERSION ) ; if ( apiVersion !== undefined ) { checkApiVersion ( apiVersion , ValidAPIVersions , requestID ) ; } } const blobContext = new BlobStorageContext ( res . locals , DEFAULT_CONTEXT_PATH ) ; blobContext . startTime = new Date ( ) ; blobContext . xMsRequestID = requestID ; logger . info ( `BlobStorageContextMiddleware: RequestMethod=${ req . method } RequestURL=${ req . protocol }://${ req . hostname }${ req . url } RequestHeaders:${ JSON . stringify ( req . headers ) } ClientIP=${ req . ip } Protocol=${ req . protocol } HTTPVersion=${ req . httpVersion }` , requestID ) ; const [ account , container , blob , isSecondary ] = extractStoragePartsFromPath ( req . hostname , req . path , disableProductStyleUrl ) ; blobContext . account = account ; blobContext . container = container ; blobContext . blob = blob ; blobContext . isSecondary = isSecondary ; blobContext . dispatchPattern = container ? blob ? `/container/blob` : `/container` : "/" ; blobContext . authenticationPath = req . path ; if ( isSecondary ) { const pos = blobContext . authenticationPath . search ( SECONDARY_SUFFIX ) ; blobContext . authenticationPath = blobContext . authenticationPath . substr ( 0 , pos ) + blobContext . authenticationPath . substr ( pos + SECONDARY_SUFFIX . length ) ; } if ( ! account ) { const handlerError = StorageErrorFactory . getInvalidQueryParameterValue ( requestID ) ; logger . error ( `BlobStorageContextMiddleware: BlobStorageContextMiddleware: ${ handlerError . message }` , requestID ) ; return next ( handlerError ) ; } if ( container && ! container . startsWith ( "$" ) ) { validateContainerName ( requestID , container ) ; } logger . info ( `BlobStorageContextMiddleware: Account=${ account } Container=${ container } Blob=${ blob }` , requestID ) ; next ( ) ; } param { string } path returns { ( [ string | undefined , string | undefined , string | undefined , boolean | undefined ] ) } export function extractStoragePartsFromPath ( hostname , path , disableProductStyleUrl ? ) { let account ; let container ; let blob ; let isSecondary = false ; const decodedPath = decodeURIComponent ( path ) ; const normalizedPath = decodedPath . startsWith ( "/" ) ? decodedPath . substr ( 1 ) : decodedPath ; const parts = normalizedPath . split ( "/" ) ; let urlPartIndex = 0 ; const isIPAddress = IP_REGEX . test ( hostname ) ; const isNoAccountHostName = NO_ACCOUNT_HOST_NAMES . has ( hostname . toLowerCase ( ) ) ; const firstDotIndex = hostname . indexOf ( "." ) ; if ( ! disableProductStyleUrl && ! isIPAddress && ! isNoAccountHostName && firstDotIndex > 0 ) { account = hostname . substring ( 0 , firstDotIndex ) ; } else { account = parts [ urlPartIndex ++ ] ; } container = parts [ urlPartIndex ++ ] ; blob = parts . slice ( urlPartIndex ++ ) . join ( "/" ) . replace ( /\\/g , "/" ) ; if ( account . endsWith ( SECONDARY_SUFFIX ) ) { account = account . substr ( 0 , account . length - SECONDARY_SUFFIX . length ) ; isSecondary = true ; } return [ account , container , blob , isSecondary ] ; }