All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.questdb.cutlass.http.ChunkedContentParser Maven / Gradle / Ivy

There is a newer version: 8.3.2
Show newest version
/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2024 QuestDB
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

package io.questdb.cutlass.http;

import io.questdb.network.PeerDisconnectedException;
import io.questdb.network.PeerIsSlowToReadException;
import io.questdb.network.ServerDisconnectException;
import io.questdb.std.Mutable;
import io.questdb.std.Unsafe;

public class ChunkedContentParser implements Mutable {
    private long chunkSize = -1;

    @Override
    public void clear() {
        chunkSize = -1;
    }

    public long handleRecv(long lo, long hi, HttpMultipartContentListener listener)
            throws PeerIsSlowToReadException, ServerDisconnectException, PeerDisconnectedException {

        while (lo < hi) {
            if (chunkSize == 0 || chunkSize == -1) {
                lo = parseChunkLength(lo, hi, chunkSize == -1);
                if (lo > -1) {
                    if (chunkSize == 0) {
                        // Should be all done, left to see trailing \r\n
                        return waitTrailingEol(lo, hi);
                    }
                }
            } else if (chunkSize == -2) {
                return waitTrailingEol(lo, hi);
            }

            if (lo < 0) {
                return lo;
            }

            long n = Math.min(hi - lo, chunkSize);
            listener.onChunk(lo, lo + n);
            chunkSize -= n;
            lo += n;
        }
        return hi;
    }

    private static boolean isEol(long lo, long hi) {
        return hi - lo > 1 && Unsafe.getUnsafe().getByte(lo) == '\r'
                && Unsafe.getUnsafe().getByte(lo + 1) == '\n';
    }

    private long parseChunkLength(long lo, long hi, boolean skipEol) {
        long startLo = lo;
        long chunkSize = 0;

        if (!skipEol) {
            if (!isEol(lo, hi)) {
                // Chunk size must start with \r\n
                return -startLo;
            }
            lo += 2;
        }

        while (lo < hi) {
            byte b = Unsafe.getUnsafe().getByte(lo++);
            if (b >= '0' && b <= '9') {
                chunkSize = chunkSize * 16 + (b - '0');
            } else if (b >= 'a' && b <= 'f') {
                chunkSize = chunkSize * 16 + (b - 'a' + 10);
            } else if (b >= 'A' && b <= 'F') {
                chunkSize = chunkSize * 16 + (b - 'A' + 10);
            } else if (b == '\r' && hi - lo < 1) {
                // Not enough buffer to parse \r\n
                break;
            } else if (isEol(lo - 1, hi) && lo > startLo + 1) {
                // parsed OK
                this.chunkSize = chunkSize;
                return lo + 1;
            } else {
                // Invalid characters in chunk size
                this.chunkSize = -2;
                return Long.MIN_VALUE;
            }
        }
        return -startLo;
    }

    private long waitTrailingEol(long lo, long hi) {
        if (hi - lo == 2) {
            if (isEol(lo, hi)) {
                return Long.MAX_VALUE;
            }
            return Long.MIN_VALUE;
        } else if (hi - lo <= 1) {
            // Missing \r\n
            chunkSize = -2;
            return -lo;
        }
        // Protocol violation
        return Long.MIN_VALUE;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy