UNPKG

azurite

Version:

An open source Azure Storage API compatible server

154 lines 7.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const utils_1 = require("../../common/utils/utils"); const utils_2 = require("../../common/utils/utils"); const BlobStorageContext_1 = tslib_1.__importDefault(require("../context/BlobStorageContext")); const NotImplementedError_1 = tslib_1.__importDefault(require("../errors/NotImplementedError")); const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory")); const Models = tslib_1.__importStar(require("../generated/artifacts/models")); const constants_1 = require("../utils/constants"); const utils_3 = require("../utils/utils"); const BaseHandler_1 = tslib_1.__importDefault(require("./BaseHandler")); class AppendBlobHandler extends BaseHandler_1.default { async create(contentLength, options, context) { const blobCtx = new BlobStorageContext_1.default(context); const accountName = blobCtx.account; const containerName = blobCtx.container; const blobName = blobCtx.blob; const date = blobCtx.startTime; const etag = (0, utils_2.newEtag)(); if (contentLength !== 0 && !this.loose) { throw StorageErrorFactory_1.default.getInvalidOperation(blobCtx.contextId, "Content-Length must be 0 for Create Append Blob request."); } options.blobHTTPHeaders = options.blobHTTPHeaders || {}; const contentType = options.blobHTTPHeaders.blobContentType || context.request.getHeader("content-type") || "application/octet-stream"; // Preserve metadata key case const metadata = (0, utils_1.convertRawHeadersToMetadata)(blobCtx.request.getRawHeaders(), context.contextId); const blob = { deleted: false, metadata, accountName, containerName, name: blobName, properties: { creationTime: date, lastModified: date, etag, contentLength: 0, contentType, contentEncoding: options.blobHTTPHeaders.blobContentEncoding, contentLanguage: options.blobHTTPHeaders.blobContentLanguage, contentMD5: options.blobHTTPHeaders.blobContentMD5, contentDisposition: options.blobHTTPHeaders.blobContentDisposition, cacheControl: options.blobHTTPHeaders.blobCacheControl, blobType: Models.BlobType.AppendBlob, leaseStatus: Models.LeaseStatusType.Unlocked, leaseState: Models.LeaseStateType.Available, serverEncrypted: true }, snapshot: "", isCommitted: true, committedBlocksInOrder: [], blobTags: options.blobTagsString === undefined ? undefined : (0, utils_3.getTagsFromString)(options.blobTagsString, context.contextId), }; await this.metadataStore.createBlob(context, blob, options.leaseAccessConditions, options.modifiedAccessConditions); const response = { statusCode: 201, eTag: etag, lastModified: blob.properties.lastModified, contentMD5: blob.properties.contentMD5, requestId: context.contextId, version: constants_1.BLOB_API_VERSION, date, isServerEncrypted: true, clientRequestId: options.requestId }; return response; } async appendBlock(body, contentLength, options, context) { const blobCtx = new BlobStorageContext_1.default(context); const accountName = blobCtx.account; const containerName = blobCtx.container; const blobName = blobCtx.blob; const date = blobCtx.startTime; if (contentLength > constants_1.MAX_APPEND_BLOB_BLOCK_SIZE) { throw StorageErrorFactory_1.default.getRequestEntityTooLarge(blobCtx.contextId); } if (contentLength === 0) { throw StorageErrorFactory_1.default.getInvalidHeaderValue(blobCtx.contextId, { HeaderName: constants_1.HeaderConstants.CONTENT_LENGTH, HeaderValue: "0" }); } // TODO: Optimize with cache const blob = await this.metadataStore.downloadBlob(blobCtx, accountName, containerName, blobName, undefined); if (blob.properties.blobType !== Models.BlobType.AppendBlob) { throw StorageErrorFactory_1.default.getBlobInvalidBlobType(blobCtx.contextId); } const committedBlockCount = (blob.committedBlocksInOrder || []).length; if (committedBlockCount >= constants_1.MAX_APPEND_BLOB_BLOCK_COUNT) { throw StorageErrorFactory_1.default.getBlockCountExceedsLimit(blobCtx.contextId); } // Persist content const extent = await this.extentStore.appendExtent(body, blobCtx.contextId); if (extent.count !== contentLength) { throw StorageErrorFactory_1.default.getInvalidOperation(blobCtx.contextId, `The size of the request body ${extent.count} mismatches the content-length ${contentLength}.`); } // MD5 const contentMD5 = blobCtx.request.getHeader(constants_1.HeaderConstants.CONTENT_MD5); let contentMD5Buffer; let contentMD5String; if (contentMD5 !== undefined) { contentMD5Buffer = typeof contentMD5 === "string" ? Buffer.from(contentMD5, "base64") : contentMD5; contentMD5String = typeof contentMD5 === "string" ? contentMD5 : contentMD5Buffer.toString("base64"); const stream = await this.extentStore.readExtent(extent, blobCtx.contextId); const calculatedContentMD5Buffer = await (0, utils_2.getMD5FromStream)(stream); const calculatedContentMD5String = Buffer.from(calculatedContentMD5Buffer).toString("base64"); if (contentMD5String !== calculatedContentMD5String) { throw StorageErrorFactory_1.default.getMd5Mismatch(context.contextId, contentMD5String, calculatedContentMD5String); } } const originOffset = blob.properties.contentLength; const properties = await this.metadataStore.appendBlock(blobCtx, { accountName, containerName, blobName, isCommitted: true, name: "", // No block ID for append block size: extent.count, persistency: extent }, options.leaseAccessConditions, options.modifiedAccessConditions, options.appendPositionAccessConditions); const response = { statusCode: 201, requestId: context.contextId, eTag: properties.etag, lastModified: properties.lastModified, contentMD5: contentMD5Buffer, xMsContentCrc64: undefined, clientRequestId: options.requestId, version: constants_1.BLOB_API_VERSION, date, blobAppendOffset: `${originOffset}`, blobCommittedBlockCount: committedBlockCount + 1, isServerEncrypted: true }; return response; } appendBlockFromUrl(sourceUrl, contentLength, options, context) { throw new NotImplementedError_1.default(context.contextId); } async seal(options, context) { throw new NotImplementedError_1.default(context.contextId); } } exports.default = AppendBlobHandler; //# sourceMappingURL=AppendBlobHandler.js.map