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 TableStorageContext from "../context/TableStorageContext" ; import { DEFAULT_TABLE_CONTEXT_PATH , HeaderConstants , ValidAPIVersions , VERSION , SECONDARY_SUFFIX } from "../utils/constants" ; import { checkApiVersion , validateTableName } from "../utils/utils" ; export default function createTableStorageContextMiddleware ( skipApiVersionCheck ? , disableProductStyleUrl ? ) { return ( req , res , next ) => { return tableStorageContextMiddleware ( req , res , next , skipApiVersionCheck , disableProductStyleUrl ) ; } ; } export param { Request } req param { Response } res param { NextFunction } next export function tableStorageContextMiddleware ( req , res , next , skipApiVersionCheck ? , disableProductStyleUrl ? ) { res . setHeader ( HeaderConstants . SERVER , `Azurite-Table/${ VERSION }` ) ; const requestID = uuid ( ) ; const tableContext = new TableStorageContext ( res . locals , DEFAULT_TABLE_CONTEXT_PATH ) ; tableContext . accept = req . headers . accept ; tableContext . startTime = new Date ( ) ; tableContext . xMsRequestID = requestID ; if ( ! skipApiVersionCheck ) { const apiVersion = req . header ( HeaderConstants . X_MS_VERSION ) ; if ( apiVersion !== undefined ) { checkApiVersion ( apiVersion , ValidAPIVersions , tableContext ) ; } } logger . info ( `TableStorageContextMiddleware: 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 ) ; let [ account , tableSection , isSecondary ] = extractStoragePartsFromPath ( req . hostname , req . path , disableProductStyleUrl ) ; tableContext . isSecondary = isSecondary ; const isGet = req . method . toUpperCase ( ) === "GET" ; if ( tableSection === undefined || tableSection === "" ) { tableContext . tableName = undefined ; } else if ( tableSection === "Tables" || tableSection === "Tables()" ) { tableSection = "Tables" ; tableContext . tableName = undefined ; } else if ( tableSection . startsWith ( "Tables('" ) && tableSection . endsWith ( "')" ) ) { tableContext . tableName = tableSection . substring ( 8 , tableSection . length - 2 ) ; if ( isGet ) { tableSection = `${ tableContext . tableName }()` ; } } else if ( ! tableSection . includes ( "(" ) && ! tableSection . includes ( ")" ) ) { tableContext . tableName = tableSection ; if ( isGet ) { tableSection = `${ tableContext . tableName }()` ; } } else if ( tableSection . includes ( "(" ) && tableSection . includes ( ")" ) && tableSection . includes ( "PartitionKey='" ) && tableSection . includes ( "RowKey='" ) ) { tableContext . tableName = tableSection . substring ( 0 , tableSection . indexOf ( "(" ) ) ; const firstQuoteIndex = tableSection . indexOf ( "'" ) ; const secondQuoteIndex = tableSection . indexOf ( "'" , firstQuoteIndex + 1 ) ; const thridQuoteIndex = tableSection . indexOf ( "'" , secondQuoteIndex + 1 ) ; const fourthQuoteIndex = tableSection . indexOf ( "'" , thridQuoteIndex + 1 ) ; tableContext . partitionKey = tableSection . substring ( firstQuoteIndex + 1 , secondQuoteIndex ) ; tableContext . rowKey = tableSection . substring ( thridQuoteIndex + 1 , fourthQuoteIndex ) ; tableSection = `${ tableContext . tableName }(PartitionKey='PLACEHOLDER',RowKey='PLACEHOLDER')` ; } else if ( tableSection . includes ( "(" ) && tableSection . includes ( ")" ) && tableSection . indexOf ( ")" ) - tableSection . indexOf ( "(" ) === 1 ) { tableContext . tableName = tableSection . substr ( 0 , tableSection . indexOf ( "(" ) ) ; } else { logger . error ( `tableStorageContextMiddleware: Cannot extract table name from URL path=${ req . path }` , requestID ) ; return next ( new Error ( `tableStorageContextMiddleware: Cannot extract table name from URL path=${ req . path }` ) ) ; } tableContext . account = account ; tableContext . authenticationPath = req . path ; if ( isSecondary ) { const pos = tableContext . authenticationPath . search ( SECONDARY_SUFFIX ) ; tableContext . authenticationPath = tableContext . authenticationPath . substr ( 0 , pos ) + tableContext . authenticationPath . substr ( pos + SECONDARY_SUFFIX . length ) ; } tableContext . dispatchPattern = tableSection !== undefined ? `/${ tableSection }` : "/" ; logger . debug ( `tableStorageContextMiddleware: Dispatch pattern string: ${ tableContext . dispatchPattern }` , requestID ) ; if ( tableContext . tableName && ! tableContext . tableName . startsWith ( "$" ) ) { validateTableName ( tableContext , tableContext . tableName ) ; } logger . info ( `tableStorageContextMiddleware: Account=${ account } tableName=${ tableContext . tableName }` , requestID ) ; next ( ) ; } param { string } path returns { ( [ string | undefined , string | undefined , string | undefined , boolean | undefined ] ) } export function extractStoragePartsFromPath ( hostname , path , disableProductStyleUrl ? ) { let account ; let table ; 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 ++ ] ; } table = parts [ urlPartIndex ++ ] ; if ( account . endsWith ( SECONDARY_SUFFIX ) ) { account = account . substr ( 0 , account . length - SECONDARY_SUFFIX . length ) ; isSecondary = true ; } return [ account , table , isSecondary ] ; }