package com.github.dockerjava.core.command;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.StreamType;
import javax.annotation.CheckForNull;
/**
* Breaks the input into frame. Similar to how a buffered reader would readLies.
* <p/>
* See: {@link }http://docs.docker.com/v1.6/reference/api/docker_remote_api_v1.13/#attach-to-a-container}
*/
public class FrameReader implements AutoCloseable {
private static final int HEADER_SIZE = 8;
private final byte[] rawBuffer = new byte[1000];
private final InputStream inputStream;
private Boolean rawStreamDetected = false;
public FrameReader(InputStream inputStream) {
this.inputStream = inputStream;
}
private static StreamType streamType(byte streamType) {
switch (streamType) {
case 0:
return StreamType.STDIN;
case 1:
return StreamType.STDOUT;
case 2:
return StreamType.STDERR;
default:
return StreamType.RAW;
}
}
/**
* @return A frame, or null if no more frames.
*/
@CheckForNull
public Frame readFrame() throws IOException {
if (rawStreamDetected) {
int read = inputStream.read(rawBuffer);
if (read == -1) {
return null;
}
return new Frame(StreamType.RAW, Arrays.copyOf(rawBuffer, read));
} else {
byte[] header = new byte[HEADER_SIZE];
int actualHeaderSize = 0;
do {
int headerCount = inputStream.read(header, actualHeaderSize, HEADER_SIZE - actualHeaderSize);
if (headerCount == -1) {
return null;
}
actualHeaderSize += headerCount;
} while (actualHeaderSize < HEADER_SIZE);
// HexDump.dump(header, 0, System.err, 0);
StreamType streamType = streamType(header[0]);
if (streamType.equals(StreamType.RAW)) {
rawStreamDetected = true;
return new Frame(StreamType.RAW, Arrays.copyOf(header, HEADER_SIZE));
}
int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8)
+ (header[7] & 0xff);
byte[] payload = new byte[payloadSize];
int actualPayloadSize = 0;
do {
int count = inputStream.read(payload, actualPayloadSize, payloadSize - actualPayloadSize);
if (count == -1) {
if (actualPayloadSize != payloadSize) {
throw new IOException(String.format("payload must be %d bytes long, but was %d", payloadSize,
actualPayloadSize));
}
break;
}
actualPayloadSize += count;
} while (actualPayloadSize < payloadSize);
return new Frame(streamType, payload);
}
}
@Override
public void close() throws IOException {
inputStream.close();
}
}