azurite
Version:
An open source Azure Storage API compatible server
154 lines • 7.61 kB
JavaScript
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
;