forked from docker-java/docker-java
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathFramedResponseStreamHandler.java
More file actions
153 lines (109 loc) · 3.96 KB
/
Copy pathFramedResponseStreamHandler.java
File metadata and controls
153 lines (109 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package com.github.dockerjava.netty.handler;
import java.util.Arrays;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.StreamType;
/**
* Handler that decodes a docker-raw-stream as described here:
*
* https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#attach-to-a-container
*
* It drives the {@link ResultCallback#onNext(Object)} method of the passed {@link ResultCallback}.
*
* @author Marcus Linke
*/
public class FramedResponseStreamHandler extends SimpleChannelInboundHandler<ByteBuf> {
private static final int HEADER_SIZE = 8;
private final ByteBuf rawBuffer = Unpooled.buffer(1000);
private byte[] header = new byte[HEADER_SIZE];
private int headerCnt = 0;
private byte[] payload = new byte[0];
private int payloadCnt = 0;
private ResultCallback<Frame> resultCallback;
private StreamType streamType = null;
public FramedResponseStreamHandler(ResultCallback<Frame> resultCallback) {
this.resultCallback = resultCallback;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
rawBuffer.writeBytes(msg, 0, msg.readableBytes());
Frame frame = null;
do {
frame = decode();
if (frame != null) {
resultCallback.onNext(frame);
}
} while (frame != null);
}
private int read(byte[] buf, int offset, int length) {
length = Math.min(rawBuffer.readableBytes(), length);
rawBuffer.readBytes(buf, offset, length);
rawBuffer.discardReadBytes();
return length;
}
private Frame decode() {
if (headerCnt < HEADER_SIZE) {
int headerCount = read(header, headerCnt, HEADER_SIZE - headerCnt);
if (headerCount == 0) {
return null;
}
headerCnt += headerCount;
streamType = streamType(header[0]);
if (streamType.equals(StreamType.RAW)) {
return new Frame(streamType, Arrays.copyOf(header, headerCount));
}
if (headerCnt < HEADER_SIZE) {
return null;
}
}
if (streamType.equals(StreamType.RAW)) {
if (payloadCnt == 0) {
payload = new byte[rawBuffer.readableBytes()];
}
int count = read(payload, payloadCnt, rawBuffer.readableBytes());
if (count == 0) {
return null;
}
payloadCnt = 0;
return new Frame(StreamType.RAW, payload);
} else {
int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8)
+ (header[7] & 0xff);
if (payloadCnt == 0) {
payload = new byte[payloadSize];
}
int count = read(payload, payloadCnt, payloadSize - payloadCnt);
if (count == 0) {
return null;
}
payloadCnt += count;
if (payloadCnt < payloadSize) {
return null;
}
headerCnt = 0;
payloadCnt = 0;
return new Frame(streamType, payload);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
resultCallback.onError(cause);
ctx.close();
}
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;
}
}
}