Skip to content

Http2Stream emit connection related 'error' after receiving all data of stream. #29929

Open
@sogaani

Description

@sogaani

The following conditions cause an error event on http2stream, even though http2stream received all data and RST_STREAM from a client.

  1. http2stream is not destroyed(does not consume all data)
  2. goaway frame is not received
  3. socket error happens
  4. http2stream received all data and RST_STREAM

Socket error without goaway frame causes a http2session.destroy(err).

function socketOnError(error) {
const session = this[kSession];
if (session !== undefined) {
// We can ignore ECONNRESET after GOAWAY was received as there's nothing
// we can do and the other side is fully within its rights to do so.
if (error.code === 'ECONNRESET' && session[kState].goawayCode !== null)
return session.destroy();
debugSessionObj(this, 'socket error [%s]', error.message);
session.destroy(error);
}
}

http2session.destroy(err) propagate error to http2stream.destroy(err).

destroy(error = NGHTTP2_NO_ERROR, code) {
if (this.destroyed)
return;
debugSessionObj(this, 'destroying');
if (typeof error === 'number') {
code = error;
error =
code !== NGHTTP2_NO_ERROR ?
new ERR_HTTP2_SESSION_ERROR(code) : undefined;
}
if (code === undefined && error != null)
code = NGHTTP2_INTERNAL_ERROR;
const state = this[kState];
state.flags |= SESSION_FLAGS_DESTROYED;
state.destroyCode = code;
// Clear timeout and remove timeout listeners
this.setTimeout(0);
this.removeAllListeners('timeout');
// Destroy any pending and open streams
const cancel = new ERR_HTTP2_STREAM_CANCEL(error);
state.pendingStreams.forEach((stream) => stream.destroy(cancel));
state.streams.forEach((stream) => stream.destroy(error));

I thought that http2stream got RST_STREAM means that there is no error about processing data. So, I thought if http2stream is closed by RST_STREAM, connection error should not cause http2stream error.

The codes to reproduce.

'use strict';

const common = require('../common');
const fixtures = require('../common/fixtures');
const http2 = require('http2');
const fs = require('fs');
const net = require('net');

const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const loc = fixtures.path('person-large.jpg');

const server = http2.createServer();
let session_;
server.on('session', (session)=>{
  session_ = session;
});
server.on('stream', common.mustCall((stream) => {
  let sum = 0;
  stream.pause();
  const slowRead = ()=>{
    setTimeout(()=>{
      const data = stream.read(stream._readableState.highWaterMark/10);
      sum += data ? data.length: 0;
      console.log('read:' + sum + ' soc:' + socket.bytesWritten + ' closed:' + stream.closed + ' destroyed:' + stream.destroyed);
      if(stream.closed){ // Got RST_STREAM and stream was closed but all data isn't processed.
        socket.destroy(); // destroy connection without goaway frame.
        try{
          session_.ping(()=>{}); // activate read.
        }catch(err){
          console.log(err);
        }
      }
      slowRead();
    }, 10)
  };
  slowRead();
  stream.respond();
  stream.end();
  stream.on('error', (err)=>{
    // Stream allready closed, but error event happens.
    console.log(err);
  });
}));

let socket;
server.listen(0, common.mustCall(() => {
  const options = {
    createConnection: (authority, options) => {
      socket = net.connect(server.address().port, 'localhost');
      return socket;
    }
  }
  const client = http2.connect(`http://localhost:${server.address().port}`, options);
  const req = client.request({ ':method': 'POST' });

  req.on('response', common.mustCall());
  req.resume();
  const str = fs.createReadStream(loc);
  str.on('end', common.mustCall());
  str.pipe(req);
}));

Metadata

Metadata

Assignees

No one assigned

    Labels

    http2Issues or PRs related to the http2 subsystem.streamIssues and PRs related to the stream subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions