azurite
Version:
An open source Azure Storage API compatible server
167 lines • 8.38 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractStoragePartsFromPath = exports.tableStorageContextMiddleware = void 0;
const tslib_1 = require("tslib");
const v4_1 = tslib_1.__importDefault(require("uuid/v4"));
const Logger_1 = tslib_1.__importDefault(require("../../common/Logger"));
const constants_1 = require("../../common/utils/constants");
const constants_2 = require("../../common/utils/constants");
const TableStorageContext_1 = tslib_1.__importDefault(require("../context/TableStorageContext"));
const constants_3 = require("../utils/constants");
const utils_1 = require("../utils/utils");
function createTableStorageContextMiddleware(skipApiVersionCheck, disableProductStyleUrl) {
return (req, res, next) => {
return tableStorageContextMiddleware(req, res, next, skipApiVersionCheck, disableProductStyleUrl);
};
}
exports.default = createTableStorageContextMiddleware;
/**
* A middleware extract related table service context.
*
* @export
* @param {Request} req An express compatible Request object
* @param {Response} res An express compatible Response object
* @param {NextFunction} next An express middleware next callback
*/
function tableStorageContextMiddleware(req, res, next, skipApiVersionCheck, disableProductStyleUrl) {
// Set server header in every Azurite response
res.setHeader(constants_3.HeaderConstants.SERVER, `Azurite-Table/${constants_3.VERSION}`);
const requestID = (0, v4_1.default)();
const tableContext = new TableStorageContext_1.default(res.locals, constants_3.DEFAULT_TABLE_CONTEXT_PATH);
tableContext.accept = req.headers.accept;
tableContext.startTime = new Date();
tableContext.xMsRequestID = requestID;
if (!skipApiVersionCheck) {
const apiVersion = req.header(constants_3.HeaderConstants.X_MS_VERSION);
if (apiVersion !== undefined) {
(0, utils_1.checkApiVersion)(apiVersion, constants_3.ValidAPIVersions, tableContext);
}
}
Logger_1.default.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);
// tslint:disable-next-line: prefer-const
let [account, tableSection, isSecondary] = extractStoragePartsFromPath(req.hostname, req.path, disableProductStyleUrl);
tableContext.isSecondary = isSecondary;
const isGet = req.method.toUpperCase() === "GET";
// Candidate tableSection
// undefined - Set Table Service Properties
// Tables - Create Tables, Query Tables
// Tables() - Create Tables, Query Tables
// Tables('mytable') - Delete Tables, Query Entities
// mytable - Get/Set Table ACL, Insert Entity, Query Entities
// mytable(PartitionKey='<partition-key>',RowKey='<row-key>') -
// Query Entities, Update Entity, Merge Entity, Delete Entity
// mytable() - Query Entities
// TODO: Not allowed create Table with Tables as name
if (tableSection === undefined || tableSection === "") {
// Service level operation
tableContext.tableName = undefined;
}
else if (tableSection === "Tables" || tableSection === "Tables()") {
// Table name in request body
tableSection = "Tables";
tableContext.tableName = undefined;
}
else if (tableSection.startsWith("Tables('") &&
tableSection.endsWith("')")) {
// Tables('mytable')
tableContext.tableName = tableSection.substring(8, tableSection.length - 2);
// Workaround for query entity
if (isGet) {
tableSection = `${tableContext.tableName}()`;
}
}
else if (!tableSection.includes("(") && !tableSection.includes(")")) {
// mytable
tableContext.tableName = tableSection;
// Workaround for query entity
if (isGet) {
tableSection = `${tableContext.tableName}()`;
}
}
else if (tableSection.includes("(") &&
tableSection.includes(")") &&
tableSection.includes("PartitionKey='") &&
tableSection.includes("RowKey='")) {
// mytable(PartitionKey='<partition-key>',RowKey='<row-key>')
tableContext.tableName = tableSection.substring(0, tableSection.indexOf("("));
const regex = /'([^']|'')*'/g;
const matches = tableSection?.match(regex);
tableContext.partitionKey = matches ? matches[0].replace(/^'|'$/g, '').replace(/''/g, "'") : undefined;
tableContext.rowKey = matches ? matches[1].replace(/^'|'$/g, '').replace(/''/g, "'") : undefined;
tableSection = `${tableContext.tableName}(PartitionKey='PLACEHOLDER',RowKey='PLACEHOLDER')`;
}
else if (tableSection.includes("(") &&
tableSection.includes(")") &&
tableSection.indexOf(")") - tableSection.indexOf("(") === 1) {
// mytable()
tableContext.tableName = tableSection.substr(0, tableSection.indexOf("("));
}
else {
Logger_1.default.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(constants_3.SECONDARY_SUFFIX);
if (pos !== -1) {
tableContext.authenticationPath =
tableContext.authenticationPath.substr(0, pos) +
tableContext.authenticationPath.substr(pos + constants_3.SECONDARY_SUFFIX.length);
}
}
// Emulator's URL pattern is like http://hostname[:port]/account/table
// (or, alternatively, http[s]://account.localhost[:port]/table/)
// Create a router to exclude account name from req.path, as url path in swagger doesn't include account
// Exclude account name from req.path for dispatchMiddleware
tableContext.dispatchPattern =
tableSection !== undefined ? `/${tableSection}` : "/";
Logger_1.default.debug(`tableStorageContextMiddleware: Dispatch pattern string: ${tableContext.dispatchPattern}`, requestID);
// validate table name, when table name has value (not undefined or empty string)
// skip check for system table
if (tableContext.tableName && !tableContext.tableName.startsWith("$")) {
(0, utils_1.validateTableName)(tableContext, tableContext.tableName);
}
Logger_1.default.info(`tableStorageContextMiddleware: Account=${account} tableName=${tableContext.tableName}`, requestID);
next();
}
exports.tableStorageContextMiddleware = tableStorageContextMiddleware;
/**
* Extract storage account, table, and messages from URL path.
*
* @param {string} path
* @returns {([string | undefined, string | undefined, string | undefined, boolean | undefined])}
*/
function extractStoragePartsFromPath(hostname, path, disableProductStyleUrl) {
let account;
let table;
let isSecondary = false;
const decodedPath = decodeURIComponent(path);
const normalizedPath = decodedPath.startsWith("/")
? decodedPath.substr(1)
: decodedPath; // Remove starting "/"
const parts = normalizedPath.split("/");
let urlPartIndex = 0;
const isIPAddress = constants_1.IP_REGEX.test(hostname);
const isNoAccountHostName = constants_2.NO_ACCOUNT_HOST_NAMES.has(hostname.toLowerCase());
const firstDotIndex = hostname.indexOf(".");
// If hostname is not an IP address or a known host name, and has a dot inside,
// we assume user wants to access emulator with a production-like URL.
if (!disableProductStyleUrl &&
!isIPAddress &&
!isNoAccountHostName &&
firstDotIndex > 0) {
account = hostname.substring(0, firstDotIndex);
}
else {
account = parts[urlPartIndex++];
}
table = parts[urlPartIndex++];
if (account.endsWith(constants_3.SECONDARY_SUFFIX)) {
account = account.substr(0, account.length - constants_3.SECONDARY_SUFFIX.length);
isSecondary = true;
}
return [account, table, isSecondary];
}
exports.extractStoragePartsFromPath = extractStoragePartsFromPath;
//# sourceMappingURL=tableStorageContext.middleware.js.map
;