@@ -49,6 +49,7 @@ public class HTTP2Stream {
4949 private volatile Thread thread ;
5050 private volatile boolean streamOpen = true ;
5151 private volatile boolean halfClosed = false ;
52+ private volatile boolean streamOutClosed = false ;
5253 private volatile AtomicBoolean handlingRequest = new AtomicBoolean (false );
5354
5455 private long dataInSize = 0 ;
@@ -218,16 +219,26 @@ private void performRequest(boolean halfClosed) throws IOException, HTTP2Excepti
218219 }
219220 });
220221 }
221- public void writeResponseHeaders () throws IOException {
222- if (headersSent .compareAndSet (false ,true )) {
222+ /**
223+ * @param closeStream if true the output stream is closed, and any attempts
224+ * to write data to the stream will fail. This is an optimization that
225+ * allows the CLOSE_STREAM bit to be set in the Headers frame, reducing the
226+ * packet count.
227+ */
228+ public void writeResponseHeaders (boolean closeStream ) throws IOException {
229+ if (headersSent .compareAndSet (false , true )) {
223230 connection .lock ();
224231 try {
225- HPackContext .writeHeaderFrame (responseHeaders ,connection .outputStream ,streamId );
232+ HPackContext .writeHeaderFrame (responseHeaders , connection .outputStream , streamId , closeStream );
233+ if (closeStream ) {
234+ streamOutClosed = true ;
235+ }
226236 } finally {
227237 connection .unlock ();
228238 }
229239 }
230240 }
241+
231242 public InetSocketAddress getLocalAddress () {
232243 return connection .getLocalAddress ();
233244 }
@@ -267,7 +278,10 @@ public void write(byte[] b, int off, int len) throws IOException {
267278 connection .stats .pauses .incrementAndGet ();
268279 LockSupport .parkNanos (TimeUnit .MILLISECONDS .toNanos (1 ));
269280 }
270- writeResponseHeaders ();
281+ writeResponseHeaders (false );
282+ if (streamOutClosed ) {
283+ throw new IOException ("output stream was closed during headers send" );
284+ }
271285 while (len >0 ) {
272286 int _len = Math .min (Math .min (len ,max_frame_size ),(int )Math .min (connection .sendWindow .get (),sendWindow .get ()));
273287 if (_len <=0 ) {
@@ -322,19 +336,25 @@ public void close() throws IOException {
322336 }
323337 return ;
324338 }
325- writeResponseHeaders ();
339+ writeResponseHeaders (false );
326340 connection .lock ();
327341 boolean lastRequest = connection .requestsInProgress .decrementAndGet () == 0 ;
328342 try {
329- FrameHeader .writeTo (connection .outputStream , 0 , FrameType .DATA , END_STREAM , streamId );
330- connection .stats .framesSent .incrementAndGet ();
343+ if (!streamOutClosed ) {
344+ FrameHeader .writeTo (connection .outputStream , 0 , FrameType .DATA , END_STREAM , streamId );
345+ connection .stats .framesSent .incrementAndGet ();
346+ }
331347 if (lastRequest ) {
332348 connection .outputStream .flush ();
333349 connection .stats .flushes .incrementAndGet ();
334350 }
335351 } finally {
336352 connection .unlock ();
337353 }
354+ // same as http1, read all incoming frames when closing the output stream.
355+ // TODO review this, as the http2 stream is bidirectional and the spec may allow the server to continue to process inbound frames
356+ // after closing the outbound stream - similar to a http2 client
357+ dataIn .readAllBytes ();
338358 } finally {
339359 connection .stats .activeStreams .decrementAndGet ();
340360 closed =true ;
@@ -355,6 +375,7 @@ public DataIn() {
355375 }
356376
357377 void enqueue (byte [] data ) {
378+ if (closed ) return ;
358379 queue .add (data );
359380 LockSupport .unpark (reader );
360381 }
0 commit comments