const nlNlBuffer = Buffer.from([13, 10, 13, 10]);
const nlBuffer = Buffer.from([13, 10]);
/**
 * Parser for HTTP body data
 */
export class BodyParser {
    logger;
    responseInfo;
    rawBody;
    constructor(logger, responseInfo) {
        this.logger = logger;
        this.responseInfo = responseInfo;
    }
    /**
     * Process body with content-length encoding
     * @param data Buffer containing body data
     * @param onComplete Callback when body is complete
     */
    processContentLength(data, onComplete) {
        if (typeof this.responseInfo.contentLength === 'undefined') {
            throw new Error('The content-length header of the response is missing.');
        }
        if (!this.responseInfo.body) {
            this.responseInfo.body = data;
            if (data.length >= this.responseInfo.contentLength) {
                const body = this.responseInfo.body.slice(0, this.responseInfo.contentLength);
                this.rawBody = body;
                onComplete(body);
                return;
            }
            return;
        }
        const sum = this.responseInfo.body.length + data.length;
        this.responseInfo.body = Buffer.concat([this.responseInfo.body, data], sum);
        if (this.responseInfo.body.length >= this.responseInfo.contentLength) {
            const body = this.responseInfo.body.slice(0, this.responseInfo.contentLength);
            this.rawBody = body;
            onComplete(body);
            return;
        }
    }
    /**
     * Process body with chunked transfer encoding
     * @param data Buffer containing chunked body data
     * @param onComplete Callback when body is complete
     */
    processChunked(data, onComplete) {
        if (!data) {
            return;
        }
        if (this.responseInfo.chunk) {
            data = Buffer.concat([this.responseInfo.chunk, data], this.responseInfo.chunk.length + data.length);
            this.responseInfo.chunk = undefined;
        }
        while (true) {
            if (this.responseInfo.chunkSize === 0 && data.indexOf(nlNlBuffer) === 0) {
                onComplete(this.rawBody || Buffer.alloc(0));
                return;
            }
            if (!this.responseInfo.chunkSize) {
                const tmp = this.readChunkSize(data);
                if (!tmp) {
                    return;
                }
                data = tmp;
                if (!this.responseInfo.chunkSize && this.responseInfo.chunkSize !== 0) {
                    // It may happen that node's buffer cuts the data
                    // just before the chunk size.
                    // It should proceed it in next portion of the data.
                    this.responseInfo.chunk = data;
                    return;
                }
                if (!this.responseInfo.chunkSize) {
                    onComplete(this.rawBody || Buffer.alloc(0));
                    return;
                }
            }
            const size = Math.min(this.responseInfo.chunkSize, data.length);
            const sliced = data.slice(0, size);
            if (!this.rawBody) {
                this.rawBody = sliced;
            }
            else {
                const sum = size + this.rawBody.length;
                this.rawBody = Buffer.concat([this.rawBody, sliced], sum);
            }
            this.responseInfo.chunkSize -= size;
            if (data.length === 0) {
                return;
            }
            data = data.slice(size + 2); // + CR
            if (data.length === 0) {
                return;
            }
        }
    }
    /**
     * Read chunk size from chunked transfer encoding
     * @param array Buffer containing chunk data
     * @returns Remaining data after reading chunk size
     */
    readChunkSize(array) {
        let index = array.indexOf(nlBuffer);
        if (index === -1) {
            // not found in this portion of data.
            return array;
        }
        if (index === 0) {
            // Node's buffer cuts CRLF after the end of chunk data, without last CLCR,
            // here's to fix it.
            // It can be either new line from the last chunk or end of
            // the message where
            // the rest of the array is [13, 10, 48, 13, 10, 13, 10]
            if (array.indexOf(nlNlBuffer) === 0) {
                this.responseInfo.chunkSize = 0;
                return Buffer.alloc(0);
            }
            array = array.subarray(index + 2);
            index = array.indexOf(nlBuffer);
        }
        const chunkSize = parseInt(array.slice(0, index).toString(), 16);
        if (Number.isNaN(chunkSize)) {
            this.responseInfo.chunkSize = undefined;
            return array.slice(index + 2);
        }
        this.responseInfo.chunkSize = chunkSize;
        return array.slice(index + 2);
    }
    /**
     * Get the raw body buffer
     */
    getRawBody() {
        return this.rawBody;
    }
    /**
     * Reset the parser state
     */
    reset() {
        this.rawBody = undefined;
        this.responseInfo.body = undefined;
        this.responseInfo.chunk = undefined;
        this.responseInfo.chunkSize = undefined;
    }
}
//# sourceMappingURL=BodyParser.js.map