/*
 * Copyright 2014-2015 Andrew Gaul <andrew@gaul.org>
 *
 * 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 org.gaul.s3proxy;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

import com.google.common.io.ByteStreams;


/**
 * Parse an AWS v4 signature chunked stream.  Reference:
 * https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
 */
final class ChunkedInputStream extends FilterInputStream {
    private byte[] chunk;
    private int currentIndex;
    private int currentLength;
    private String currentSignature;

    ChunkedInputStream(InputStream is) {
        super(is);
    }

    @Override
    public int read() throws IOException {
        while (currentIndex == currentLength) {
            String line = readLine(in);
            if (line.equals("")) {
                return -1;
            }
            String[] parts = line.split(";", 2);
            currentLength = Integer.parseInt(parts[0], 16);
            currentSignature = parts[1];
            chunk = new byte[currentLength];
            currentIndex = 0;
            ByteStreams.readFully(in, chunk);
            // TODO: check currentSignature
            if (currentLength == 0) {
                return -1;
            }
            readLine(in);
        }
        return chunk[currentIndex++];
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int i;
        for (i = 0; i < len; ++i) {
            int ch = read();
            if (ch == -1) {
                break;
            }
            b[off + i] = (byte) ch;
        }
        if (i == 0) {
            return -1;
        }
        return i;
    }

    /**
     * Read a \r\n terminated line from an InputStream.
     *
     * @return line without the newline or empty String if InputStream is empty
     */
    private static String readLine(InputStream is) throws IOException {
        StringBuilder builder = new StringBuilder();
        while (true) {
            int ch = is.read();
            if (ch == '\r') {
                ch = is.read();
                if (ch == '\n') {
                    break;
                } else {
                    throw new IOException("unexpected char after \\r: " + ch);
                }
            } else if (ch == -1) {
                if (builder.length() > 0) {
                    throw new IOException("unexpected end of stream");
                }
                break;
            }
            builder.append((char) ch);
        }
        return builder.toString();
    }
}

@@ -1844,16 +1844,23 @@
 Index++%5D
+ & 0xFF
 ;%0A    %7D%0A

Mask high bits in ChunkedInputStream.read