@ -214,7 +214,7 @@ function onSessionHeaders(id, cat, flags, headers) {
}
// Called to determine if there are trailers to be sent at the end of a
// Stream. The 'fetchTrailers' event is emitt ed and passed a holder object.
// Stream. The 'getTrailers' callback is invok ed and passed a holder object.
// The trailers to return are set on that object by the handler. Once the
// event handler returns, those are sent off for processing. Note that this
// is a necessarily synchronous operation. We need to know immediately if
@ -229,9 +229,11 @@ function onSessionTrailers(id) {
assert ( stream !== undefined ,
'Internal HTTP/2 Failure. Stream does not exist. Please ' +
'report this as a bug in Node.js' ) ;
const getTrailers = stream [ kState ] . getTrailers ;
if ( typeof getTrailers !== 'function' )
return [ ] ;
const trailers = Object . create ( null ) ;
stream . emit ( 'fetchTrailers' , trailers ) ;
getTrailers . call ( stream , trailers ) ;
const headersList = mapToHeaders ( trailers , assertValidPseudoHeaderTrailer ) ;
if ( ! Array . isArray ( headersList ) ) {
process . nextTick ( ( ) => stream . emit ( 'error' , headersList ) ) ;
@ -407,8 +409,7 @@ function requestOnConnect(headers, options) {
const session = this [ kSession ] ;
debug ( ` [ ${ sessionName ( session [ kType ] ) } ] connected.. initializing request ` ) ;
const streams = session [ kState ] . streams ;
// ret will be either the reserved stream ID (if positive)
// or an error code (if negative)
validatePriorityOptions ( options ) ;
const handle = session [ kHandle ] ;
@ -418,11 +419,20 @@ function requestOnConnect(headers, options) {
return ;
}
let getTrailers = false ;
if ( typeof options . getTrailers === 'function' ) {
getTrailers = true ;
this [ kState ] . getTrailers = options . getTrailers ;
}
// ret will be either the reserved stream ID (if positive)
// or an error code (if negative)
const ret = handle . submitRequest ( headersList ,
! ! options . endStream ,
options . parent | 0 ,
options . weight | 0 ,
! ! options . exclusive ) ;
! ! options . exclusive ,
getTrailers ) ;
// In an error condition, one of three possible response codes will be
// possible:
@ -1095,7 +1105,15 @@ class ClientHttp2Session extends Http2Session {
options . endStream ) ;
}
if ( options . getTrailers !== undefined &&
typeof options . getTrailers !== 'function' ) {
throw new errors . TypeError ( 'ERR_INVALID_OPT_VALUE' ,
'getTrailers' ,
options . getTrailers ) ;
}
const stream = new ClientHttp2Stream ( this , undefined , { } ) ;
const onConnect = requestOnConnect . bind ( stream , headers , options ) ;
// Close the writable side of the stream if options.endStream is set.
@ -1535,7 +1553,8 @@ function processHeaders(headers) {
return headers ;
}
function processRespondWithFD ( fd , headers , offset = 0 , length = - 1 ) {
function processRespondWithFD ( fd , headers , offset = 0 , length = - 1 ,
getTrailers = false ) {
const session = this [ kSession ] ;
const state = this [ kState ] ;
state . headersSent = true ;
@ -1545,7 +1564,7 @@ function processRespondWithFD(fd, headers, offset = 0, length = -1) {
const handle = session [ kHandle ] ;
const ret =
handle . submitFile ( this [ kID ] , fd , headers , offset , length ) ;
handle . submitFile ( this [ kID ] , fd , headers , offset , length , getTrailers ) ;
let err ;
switch ( ret ) {
case NGHTTP2_ERR_NOMEM :
@ -1560,7 +1579,7 @@ function processRespondWithFD(fd, headers, offset = 0, length = -1) {
}
}
function doSendFD ( session , options , fd , headers , err , stat ) {
function doSendFD ( session , options , fd , headers , getTrailers , err , stat ) {
if ( this . destroyed || session . destroyed ) {
abort ( this ) ;
return ;
@ -1588,10 +1607,11 @@ function doSendFD(session, options, fd, headers, err, stat) {
processRespondWithFD . call ( this , fd , headersList ,
statOptions . offset ,
statOptions . length ) ;
statOptions . length ,
getTrailers ) ;
}
function doSendFileFD ( session , options , fd , headers , err , stat ) {
function doSendFileFD ( session , options , fd , headers , getTrailers , err , stat ) {
if ( this . destroyed || session . destroyed ) {
abort ( this ) ;
return ;
@ -1633,10 +1653,11 @@ function doSendFileFD(session, options, fd, headers, err, stat) {
processRespondWithFD . call ( this , fd , headersList ,
options . offset ,
options . length ) ;
options . length ,
getTrailers ) ;
}
function afterOpen ( session , options , headers , err , fd ) {
function afterOpen ( session , options , headers , getTrailers , err , fd ) {
const state = this [ kState ] ;
if ( this . destroyed || session . destroyed ) {
abort ( this ) ;
@ -1648,7 +1669,8 @@ function afterOpen(session, options, headers, err, fd) {
}
state . fd = fd ;
fs . fstat ( fd , doSendFileFD . bind ( this , session , options , fd , headers ) ) ;
fs . fstat ( fd ,
doSendFileFD . bind ( this , session , options , fd , headers , getTrailers ) ) ;
}
@ -1783,6 +1805,17 @@ class ServerHttp2Stream extends Http2Stream {
options = Object . assign ( Object . create ( null ) , options ) ;
options . endStream = ! ! options . endStream ;
let getTrailers = false ;
if ( options . getTrailers !== undefined ) {
if ( typeof options . getTrailers !== 'function' ) {
throw new errors . TypeError ( 'ERR_INVALID_OPT_VALUE' ,
'getTrailers' ,
options . getTrailers ) ;
}
getTrailers = true ;
state . getTrailers = options . getTrailers ;
}
headers = processHeaders ( headers ) ;
const statusCode = headers [ HTTP2_HEADER_STATUS ] |= 0 ;
@ -1809,7 +1842,10 @@ class ServerHttp2Stream extends Http2Stream {
const handle = session [ kHandle ] ;
const ret =
handle . submitResponse ( this [ kID ] , headersList , options . endStream ) ;
handle . submitResponse ( this [ kID ] ,
headersList ,
options . endStream ,
getTrailers ) ;
let err ;
switch ( ret ) {
case NGHTTP2_ERR_NOMEM :
@ -1862,6 +1898,17 @@ class ServerHttp2Stream extends Http2Stream {
options . statCheck ) ;
}
let getTrailers = false ;
if ( options . getTrailers !== undefined ) {
if ( typeof options . getTrailers !== 'function' ) {
throw new errors . TypeError ( 'ERR_INVALID_OPT_VALUE' ,
'getTrailers' ,
options . getTrailers ) ;
}
getTrailers = true ;
state . getTrailers = options . getTrailers ;
}
if ( typeof fd !== 'number' )
throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' ,
'fd' , 'number' ) ;
@ -1876,7 +1923,8 @@ class ServerHttp2Stream extends Http2Stream {
}
if ( options . statCheck !== undefined ) {
fs . fstat ( fd , doSendFD . bind ( this , session , options , fd , headers ) ) ;
fs . fstat ( fd ,
doSendFD . bind ( this , session , options , fd , headers , getTrailers ) ) ;
return ;
}
@ -1888,7 +1936,8 @@ class ServerHttp2Stream extends Http2Stream {
processRespondWithFD . call ( this , fd , headersList ,
options . offset ,
options . length ) ;
options . length ,
getTrailers ) ;
}
// Initiate a file response on this Http2Stream. The path is passed to
@ -1930,6 +1979,17 @@ class ServerHttp2Stream extends Http2Stream {
options . statCheck ) ;
}
let getTrailers = false ;
if ( options . getTrailers !== undefined ) {
if ( typeof options . getTrailers !== 'function' ) {
throw new errors . TypeError ( 'ERR_INVALID_OPT_VALUE' ,
'getTrailers' ,
options . getTrailers ) ;
}
getTrailers = true ;
state . getTrailers = options . getTrailers ;
}
headers = processHeaders ( headers ) ;
const statusCode = headers [ HTTP2_HEADER_STATUS ] |= 0 ;
// Payload/DATA frames are not permitted in these cases
@ -1939,7 +1999,8 @@ class ServerHttp2Stream extends Http2Stream {
throw new errors . Error ( 'ERR_HTTP2_PAYLOAD_FORBIDDEN' , statusCode ) ;
}
fs . open ( path , 'r' , afterOpen . bind ( this , session , options , headers ) ) ;
fs . open ( path , 'r' ,
afterOpen . bind ( this , session , options , headers , getTrailers ) ) ;
}
// Sends a block of informational headers. In theory, the HTTP/2 spec