HTTPCORE-708: H2 stream multiplexer incorrectly handles multiple fram… · apache/httpcomponents-core@64c4c7b (original) (raw)

``

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

`+

package org.apache.hc.core5.http2.impl.nio;

`

``

29

+

``

30

`+

import java.io.IOException;

`

``

31

`+

import java.nio.ByteBuffer;

`

``

32

+

``

33

`+

import org.apache.hc.core5.http.config.CharCodingConfig;

`

``

34

`+

import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;

`

``

35

`+

import org.apache.hc.core5.http.nio.AsyncPushConsumer;

`

``

36

`+

import org.apache.hc.core5.http.nio.HandlerFactory;

`

``

37

`+

import org.apache.hc.core5.http.nio.command.ExecutableCommand;

`

``

38

`+

import org.apache.hc.core5.http.protocol.HttpProcessor;

`

``

39

`+

import org.apache.hc.core5.http2.H2ConnectionException;

`

``

40

`+

import org.apache.hc.core5.http2.WritableByteChannelMock;

`

``

41

`+

import org.apache.hc.core5.http2.config.H2Config;

`

``

42

`+

import org.apache.hc.core5.http2.frame.DefaultFrameFactory;

`

``

43

`+

import org.apache.hc.core5.http2.frame.FrameConsts;

`

``

44

`+

import org.apache.hc.core5.http2.frame.FrameFactory;

`

``

45

`+

import org.apache.hc.core5.http2.frame.FrameType;

`

``

46

`+

import org.apache.hc.core5.http2.frame.RawFrame;

`

``

47

`+

import org.apache.hc.core5.http2.frame.StreamIdGenerator;

`

``

48

`+

import org.apache.hc.core5.reactor.ProtocolIOSession;

`

``

49

`+

import org.junit.Assert;

`

``

50

`+

import org.junit.Before;

`

``

51

`+

import org.junit.Test;

`

``

52

`+

import org.junit.function.ThrowingRunnable;

`

``

53

`+

import org.mockito.Mock;

`

``

54

`+

import org.mockito.Mockito;

`

``

55

`+

import org.mockito.MockitoAnnotations;

`

``

56

+

``

57

`+

public class TestAbstractH2StreamMultiplexer {

`

``

58

+

``

59

`+

@Mock

`

``

60

`+

ProtocolIOSession protocolIOSession;

`

``

61

`+

@Mock

`

``

62

`+

HttpProcessor httpProcessor;

`

``

63

`+

@Mock

`

``

64

`+

H2StreamListener h2StreamListener;

`

``

65

+

``

66

`+

@Before

`

``

67

`+

public void prepareMocks() {

`

``

68

`+

MockitoAnnotations.initMocks(this);

`

``

69

`+

}

`

``

70

+

``

71

`+

static class H2StreamMultiplexerImpl extends AbstractH2StreamMultiplexer {

`

``

72

+

``

73

`+

public H2StreamMultiplexerImpl(

`

``

74

`+

final ProtocolIOSession ioSession,

`

``

75

`+

final FrameFactory frameFactory,

`

``

76

`+

final StreamIdGenerator idGenerator,

`

``

77

`+

final HttpProcessor httpProcessor,

`

``

78

`+

final CharCodingConfig charCodingConfig,

`

``

79

`+

final H2Config h2Config,

`

``

80

`+

final H2StreamListener streamListener) {

`

``

81

`+

super(ioSession, frameFactory, idGenerator, httpProcessor, charCodingConfig, h2Config, streamListener);

`

``

82

`+

}

`

``

83

+

``

84

`+

@Override

`

``

85

`+

void acceptHeaderFrame() throws H2ConnectionException {

`

``

86

`+

}

`

``

87

+

``

88

`+

@Override

`

``

89

`+

void acceptPushRequest() throws H2ConnectionException {

`

``

90

`+

}

`

``

91

+

``

92

`+

@Override

`

``

93

`+

void acceptPushFrame() throws H2ConnectionException {

`

``

94

`+

}

`

``

95

+

``

96

`+

@Override

`

``

97

`+

H2StreamHandler createRemotelyInitiatedStream(

`

``

98

`+

final H2StreamChannel channel,

`

``

99

`+

final HttpProcessor httpProcessor,

`

``

100

`+

final BasicHttpConnectionMetrics connMetrics,

`

``

101

`+

final HandlerFactory pushHandlerFactory) throws IOException {

`

``

102

`+

return null;

`

``

103

`+

}

`

``

104

+

``

105

`+

@Override

`

``

106

`+

H2StreamHandler createLocallyInitiatedStream(

`

``

107

`+

final ExecutableCommand command,

`

``

108

`+

final H2StreamChannel channel,

`

``

109

`+

final HttpProcessor httpProcessor,

`

``

110

`+

final BasicHttpConnectionMetrics connMetrics) throws IOException {

`

``

111

`+

return null;

`

``

112

`+

}

`

``

113

`+

}

`

``

114

+

``

115

`+

@Test

`

``

116

`+

public void testInputOneFrame() throws Exception {

`

``

117

`+

final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024);

`

``

118

`+

final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024);

`

``

119

+

``

120

`+

final byte[] data = new byte[FrameConsts.MIN_FRAME_SIZE];

`

``

121

`+

for (int i = 0; i < FrameConsts.MIN_FRAME_SIZE; i++) {

`

``

122

`+

data[i] = (byte)(i % 16);

`

``

123

`+

}

`

``

124

+

``

125

`+

final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(data));

`

``

126

`+

outbuffer.write(frame, writableChannel);

`

``

127

`+

final byte[] bytes = writableChannel.toByteArray();

`

``

128

+

``

129

`+

final AbstractH2StreamMultiplexer streamMultiplexer = new H2StreamMultiplexerImpl(

`

``

130

`+

protocolIOSession,

`

``

131

`+

DefaultFrameFactory.INSTANCE,

`

``

132

`+

StreamIdGenerator.ODD,

`

``

133

`+

httpProcessor,

`

``

134

`+

CharCodingConfig.DEFAULT,

`

``

135

`+

H2Config.custom()

`

``

136

`+

.setMaxFrameSize(FrameConsts.MIN_FRAME_SIZE)

`

``

137

`+

.build(),

`

``

138

`+

h2StreamListener);

`

``

139

+

``

140

`+

Assert.assertThrows(H2ConnectionException.class, new ThrowingRunnable() {

`

``

141

`+

@Override

`

``

142

`+

public void run() throws Throwable {

`

``

143

`+

streamMultiplexer.onInput(ByteBuffer.wrap(bytes));

`

``

144

`+

}

`

``

145

`+

});

`

``

146

+

``

147

`+

Mockito.verify(h2StreamListener).onFrameInput(

`

``

148

`+

Mockito.same(streamMultiplexer),

`

``

149

`+

Mockito.eq(1),

`

``

150

`+

Mockito.any());

`

``

151

+

``

152

`+

Mockito.reset(h2StreamListener);

`

``

153

+

``

154

`+

Assert.assertThrows(H2ConnectionException.class, new ThrowingRunnable() {

`

``

155

`+

@Override

`

``

156

`+

public void run() throws Throwable {

`

``

157

`+

int pos = 0;

`

``

158

`+

int remaining = bytes.length;

`

``

159

`+

while (remaining > 0) {

`

``

160

`+

final int chunk = Math.min(2048, remaining);

`

``

161

`+

streamMultiplexer.onInput(ByteBuffer.wrap(bytes, pos, chunk));

`

``

162

`+

pos += chunk;

`

``

163

`+

remaining -= chunk;

`

``

164

`+

}

`

``

165

`+

}

`

``

166

`+

});

`

``

167

+

``

168

`+

Mockito.verify(h2StreamListener).onFrameInput(

`

``

169

`+

Mockito.same(streamMultiplexer),

`

``

170

`+

Mockito.eq(1),

`

``

171

`+

Mockito.any());

`

``

172

`+

}

`

``

173

+

``

174

`+

@Test

`

``

175

`+

public void testInputMultipleFrames() throws Exception {

`

``

176

`+

final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024);

`

``

177

`+

final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024);

`

``

178

+

``

179

`+

final byte[] data = new byte[FrameConsts.MIN_FRAME_SIZE];

`

``

180

`+

for (int i = 0; i < FrameConsts.MIN_FRAME_SIZE; i++) {

`

``

181

`+

data[i] = (byte) (i % 16);

`

``

182

`+

}

`

``

183

+

``

184

`+

final RawFrame frame1 = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(data));

`

``

185

`+

outbuffer.write(frame1, writableChannel);

`

``

186

`+

final RawFrame frame2 = new RawFrame(FrameType.DATA.getValue(), 0, 1, ByteBuffer.wrap(data));

`

``

187

`+

outbuffer.write(frame2, writableChannel);

`

``

188

`+

final byte[] bytes = writableChannel.toByteArray();

`

``

189

+

``

190

`+

final AbstractH2StreamMultiplexer streamMultiplexer = new H2StreamMultiplexerImpl(

`

``

191

`+

protocolIOSession,

`

``

192

`+

DefaultFrameFactory.INSTANCE,

`

``

193

`+

StreamIdGenerator.ODD,

`

``

194

`+

httpProcessor,

`

``

195

`+

CharCodingConfig.DEFAULT,

`

``

196

`+

H2Config.custom()

`

``

197

`+

.setMaxFrameSize(FrameConsts.MIN_FRAME_SIZE)

`

``

198

`+

.build(),

`

``

199

`+

h2StreamListener);

`

``

200

+

``

201

`+

Assert.assertThrows(H2ConnectionException.class, new ThrowingRunnable() {

`

``

202

`+

@Override

`

``

203

`+

public void run() throws Throwable {

`

``

204

`+

streamMultiplexer.onInput(ByteBuffer.wrap(bytes));

`

``

205

`+

}

`

``

206

`+

});

`

``

207

`+

Mockito.verify(h2StreamListener).onFrameInput(

`

``

208

`+

Mockito.same(streamMultiplexer),

`

``

209

`+

Mockito.eq(1),

`

``

210

`+

Mockito.any());

`

``

211

+

``

212

`+

Mockito.reset(h2StreamListener);

`

``

213

+

``

214

`+

Assert.assertThrows(H2ConnectionException.class, new ThrowingRunnable() {

`

``

215

`+

@Override

`

``

216

`+

public void run() throws Throwable {

`

``

217

`+

int pos = 0;

`

``

218

`+

int remaining = bytes.length;

`

``

219

`+

while (remaining > 0) {

`

``

220

`+

final int chunk = Math.min(4096, remaining);

`

``

221

`+

streamMultiplexer.onInput(ByteBuffer.wrap(bytes, pos, chunk));

`

``

222

`+

pos += chunk;

`

``

223

`+

remaining -= chunk;

`

``

224

`+

}

`

``

225

`+

}

`

``

226

`+

});

`

``

227

`+

Mockito.verify(h2StreamListener).onFrameInput(

`

``

228

`+

Mockito.same(streamMultiplexer),

`

``

229

`+

Mockito.eq(1),

`

``

230

`+

Mockito.any());

`

``

231

`+

}

`

``

232

+

``

233

`+

}

`

``

234

+