Skip to content

Commit

Permalink
success
Browse files Browse the repository at this point in the history
  • Loading branch information
dtudury committed Feb 22, 2019
1 parent e74e914 commit 832b299
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 122 deletions.
74 changes: 48 additions & 26 deletions lib/H2LSession.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
const Emitter = require('./utils/Emitter')
const H2LStream = require('./utils/H2LStream')
const { frameToHttp, httpToFrame } = require('./utils/codecs')
const { DATA, HEADERS, PING } = require('./constants')
const { encodeRequest, decodeRequest } = require('./utils/codecs')
const { FRAME, H2LSTREAM } = require('./constants')
const ui8aHelpers = require('./utils/ui8aHelpers')

/**
* Muxes and demuxes
*/
class H2LSession extends Emitter {
/**
* @param {Number} nextStreamId - default is 1 for client, passing 2 would be for server
Expand All @@ -11,49 +15,67 @@ class H2LSession extends Emitter {
super([FRAME, H2LSTREAM])
this.nextStreamId = nextStreamId
this.frameBuffer = ui8aHelpers.alloc(0) // partial frame holder
this.h2LStream = new H2LStream(this, 0, this._writeHttp.bind(this))
this.h2LStream = new H2LStream(this, 0)
this.h2LStreams = [this.h2LStream]
}
/**
* @param {Number} streamId
* @param {Number} type - frame type from constants
* @param {Uint8Array} payload
* @param {Boolean} endsStream
* @param {Boolean} endsHeaders
* @param {Number} padLength
* @param {Number} priority
* @param {Number} streamDependency
* @param {Boolean} isExclusive
*/
_writeHttp (streamId, type, payload, endsStream, endsHeaders, padLength, priority, streamDependency, isExclusive) {
this.emit(FRAME, httpToFrame(streamId, type, payload, endsStream, endsHeaders, padLength, priority, streamDependency, isExclusive))
}

/**
* Emit any new http messages. Emit new streams when necessary.
* @param {Uint8Array} frame - partial and/or multiple encoded http messages
*/
writeFrame (frame) {
this.frameBuffer = ui8aHelpers.concat([this.frameBuffer, frame])
const { type, flags, streamId, payload, priority, bytesRead } = frameToHttp(this.frameBuffer)
if (!bytesRead) {
const request = decodeRequest(this.frameBuffer)
// const { type, flags, streamId, payload, priority, bytesRead } = frameToHttp(this.frameBuffer)
if (!request.bytesRead) {
return // we don't have one whole frame yet
}
if (!this.h2LStreams[streamId]) {
this.h2LStreams[streamId] = new H2LStream(this, streamId, this._writeHttp.bind(this))
this.emit(H2LSTREAM, this.h2LStreams[streamId])
if (!this.h2LStreams[request.streamId]) {
this.h2LStreams[request.streamId] = new H2LStream(this, request.streamId)
this.emit(H2LSTREAM, this.h2LStreams[request.streamId])
}
const h2LStream = this.h2LStreams[streamId]
h2LStream.emit(type, payload, flags, priority)
this.frameBuffer = this.frameBuffer.slice(bytesRead)
const h2LStream = this.h2LStreams[request.streamId]
h2LStream.emit(request.type, request)
this.frameBuffer = this.frameBuffer.slice(request.bytesRead)
if (this.frameBuffer.length) {
this.writeFrame(ui8aHelpers.alloc(0))
}
}

/**
* @returns {H2LStream}
*/
request () {
const streamId = this.nextStreamId
this.nextStreamId += 2 // even stays even (server), odd stays odd (client)
this.h2LStreams[streamId] = new H2LStream(this, streamId, this._writeHttp.bind(this))
this.h2LStreams[streamId] = new H2LStream(this, streamId)
return this.h2LStreams[streamId]
}
}

/**
* Gateway for reading from and writing to virtual streams
*/
class H2LStream extends Emitter {
/**
* Create a gateway for reading from and writing to a virtual stream
* @param {H2LSession} h2LSession
* @param {Number} streamId
*/
constructor (h2LSession, streamId) {
super([DATA, HEADERS, PING])
this.h2LSession = h2LSession
this.streamId = streamId
}

/**
* Encode http-like request and mux it into the output stream
* @param {import('./utils/codecs/http2/request').Request} request
*/
writeRequest (request) {
request.streamId = this.streamId
this.h2LSession.emit(FRAME, encodeRequest(request))
}
}

module.exports = H2LSession
19 changes: 0 additions & 19 deletions lib/utils/H2LStream.js

This file was deleted.

8 changes: 4 additions & 4 deletions lib/utils/codecs/http2/flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ function decodeFlags (ui8) {

function encodeFlags (flags) {
let ui8 = 0
if (flags.endStream || flags.isAck) { ui8 |= END_STREAM } // same bit
if (flags.endHeaders) { ui8 |= END_HEADERS }
if (flags.isPadded) { ui8 |= PADDED }
if (flags.isPriority) { ui8 |= PRIORITY }
if (flags && (flags.endStream || flags.isAck)) { ui8 |= END_STREAM } // same bit
if (flags && flags.endHeaders) { ui8 |= END_HEADERS }
if (flags && flags.isPadded) { ui8 |= PADDED }
if (flags && flags.isPriority) { ui8 |= PRIORITY }
return ui8
}

Expand Down
11 changes: 0 additions & 11 deletions lib/utils/codecs/http2/frameToHttp.js

This file was deleted.

25 changes: 0 additions & 25 deletions lib/utils/codecs/http2/httpToFrame.js

This file was deleted.

2 changes: 0 additions & 2 deletions lib/utils/codecs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,3 @@ exports.encodeHeader = encodeHeader
// HTTP2 transcoders
exports.decodeRequest = decodeRequest
exports.encodeRequest = encodeRequest
exports.frameToHttp = require('./http2/frameToHttp')
exports.httpToFrame = require('./http2/httpToFrame')
69 changes: 34 additions & 35 deletions test/Http2Lite.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ describe('H2LSession', () => {
server.writeFrame(frame)
})
server.on(H2LSTREAM, h2LStream => {
h2LStream.on(HEADERS, headers => {
h2LStream.on(HEADERS, request => {
const oldHeaders = serverHeadersByStream[h2LStream.streamId] || {}
serverHeadersByStream[h2LStream.streamId] = Object.assign(oldHeaders, headers)
serverHeadersByStream[h2LStream.streamId] = Object.assign(oldHeaders, request.payload)
})
h2LStream.on(DATA, data => {
h2LStream.on(DATA, request => {
const oldData = serverDataByStream[h2LStream.streamId] || ui8aHelpers.alloc(0)
serverDataByStream[h2LStream.streamId] = ui8aHelpers.concat([oldData, data])
serverDataByStream[h2LStream.streamId] = ui8aHelpers.concat([oldData, request.payload])
})
})
describe('#writeData()', () => {
const clientReq = client.request()
const part1 = new Uint8Array([1, 2, 3, 4])
it('should send data from end to end', () => {
clientReq.writeData(part1)
clientReq.writeRequest({ type: DATA, payload: part1 })
expect(part1).to.deep.equal(serverDataByStream[clientReq.streamId])
})
const part2 = new Uint8Array([5, 6, 7, 8])
it('should append data when more is sent', () => {
clientReq.writeData(part2)
clientReq.writeRequest({ type: DATA, payload: part2 })
expect(ui8aHelpers.concat([part1, part2])).to.deep.equal(serverDataByStream[clientReq.streamId])
})
})
Expand All @@ -47,7 +47,7 @@ describe('H2LSession', () => {
const client = new H2LSession(1)
const clientReq = client.request()
it('should throw when too large of a payload is written', () => {
expect(() => clientReq.writeData(ui8aHelpers.allocUnsafe(0x1000000))).to.throw()
expect(() => clientReq.writeRequest({ type: DATA, payload: ui8aHelpers.allocUnsafe(0x1000000) })).to.throw()
})
})
describe('partial and multiple frames', () => {
Expand All @@ -59,17 +59,17 @@ describe('H2LSession', () => {
clientOutgoingFrame = ui8aHelpers.concat([clientOutgoingFrame, frame])
})
server.on(H2LSTREAM, h2LStream => {
h2LStream.on(DATA, (data, flags) => {
h2LStream.on(DATA, request => {
const oldData = serverDataByStream[h2LStream.streamId] || ui8aHelpers.alloc(0)
serverDataByStream[h2LStream.streamId] = ui8aHelpers.concat([oldData, data])
serverDataByStream[h2LStream.streamId] = ui8aHelpers.concat([oldData, request.payload])
})
})
const clientReq = client.request()
clientReq.writeData(new Uint8Array([1]), false, 5)
clientReq.writeData(new Uint8Array([2]))
clientReq.writeData(new Uint8Array([3]))
clientReq.writeData(new Uint8Array([4]))
clientReq.writeData(new Uint8Array([5]), true)
clientReq.writeRequest({ type: DATA, payload: new Uint8Array([1]), flags: { isPadded: true }, padLength: 5 })
clientReq.writeRequest({ type: DATA, payload: new Uint8Array([2]) })
clientReq.writeRequest({ type: DATA, payload: new Uint8Array([3]) })
clientReq.writeRequest({ type: DATA, payload: new Uint8Array([4]) })
clientReq.writeRequest({ type: DATA, payload: new Uint8Array([5]), flags: { endStream: true } })
const barelyStarted = 10
const halfway = Math.round(clientOutgoingFrame.length / 2)
it('should understand nothing without the header', () => {
Expand All @@ -95,19 +95,19 @@ describe('H2LSession', () => {
server.writeFrame(frame)
})
server.on(H2LSTREAM, h2LStream => {
h2LStream.on(HEADERS, (headers, flags) => {
receivedHeaders.push(headers)
receivedFlags.push(flags)
h2LStream.on(HEADERS, request => {
receivedHeaders.push(request.payload)
receivedFlags.push(request.flags)
})
})
const clientReq = client.request()
const headers = new Uint8Array([1, 2, 3, 4])
clientReq.writeHeaders(headers)
clientReq.writeRequest({ type: HEADERS, payload: headers })
it('should default to no padding', () => {
expect(receivedFlags[0].isPadded).to.equal(false)
})
const padLength = 5
clientReq.writeHeaders(headers, false, true, padLength, 0, 0, false)
clientReq.writeRequest({ type: HEADERS, payload: headers, flags: { endHeaders: true, isPadded: true }, padLength })
it('should increase the framesize by the padLength + 1 when padLength is set', () => {
expect(sentFrames[1].length - sentFrames[0].length).to.equal(padLength + 1)
})
Expand All @@ -128,14 +128,14 @@ describe('H2LSession', () => {
server.writeFrame(frame)
})
server.on(H2LSTREAM, h2LStream => {
h2LStream.on(DATA, (data, flags) => {
receivedDatas.push(data)
receivedFlags.push(flags)
h2LStream.on(DATA, request => {
receivedDatas.push(request.payload)
receivedFlags.push(request.flags)
})
h2LStream.on(HEADERS, (headers, flags, priority) => {
receivedHeaders.push(headers)
receivedFlags.push(flags)
receivedPriorities.push(priority)
h2LStream.on(HEADERS, request => {
receivedHeaders.push(request.payload)
receivedFlags.push(request.flags)
receivedPriorities.push(request.priority)
})
})

Expand All @@ -144,18 +144,17 @@ describe('H2LSession', () => {
const sentPriority = 20
const sentStreamDependency = 100
const sentIsExclusive = true
clientReq.writeHeaders(headers, false, false, 0, sentPriority, sentStreamDependency, sentIsExclusive)
clientReq.writeRequest({ type: HEADERS, payload: headers, flags: { isPriority: true }, priority: { priority: sentPriority, streamDependency: sentStreamDependency, isExclusive: sentIsExclusive } })
it('should send the prioritization data', () => {
const { priority, streamDependency, isExclusive } = receivedPriorities[0]
expect(sentPriority).to.equal(priority)
expect(sentStreamDependency).to.equal(streamDependency)
expect(sentIsExclusive).to.equal(isExclusive)
})
const sentIsExclusive2 = false
clientReq.writeHeaders(headers, false, false, 0, sentPriority, sentStreamDependency, sentIsExclusive2)
clientReq.writeRequest({ type: HEADERS, payload: headers, flags: { isPriority: true }, priority: { streamDependency: sentStreamDependency, isExclusive: sentIsExclusive2 } })
it('should send the prioritization data (not exclusive)', () => {
const { priority, streamDependency, isExclusive } = receivedPriorities[1]
expect(sentPriority).to.equal(priority)
const { streamDependency, isExclusive } = receivedPriorities[1]
expect(sentStreamDependency).to.equal(streamDependency)
expect(sentIsExclusive2).to.equal(isExclusive)
})
Expand All @@ -173,16 +172,16 @@ describe('H2LSession', () => {
sentFrames.push(frame)
server.writeFrame(frame)
})
server.h2LStream.on(PING, (data, flags) => {
receivedPings.push(data)
receivedFlags.push(flags)
server.h2LStream.on(PING, request => {
receivedPings.push(request.payload)
receivedFlags.push(request.flags)
})
const payloads = [
new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
new Uint8Array([8, 7, 6, 5, 4, 3, 2, 1])
]
client.h2LStream.writePing(payloads[0])
client.h2LStream.writePing(payloads[1], true)
client.h2LStream.writeRequest({ type: PING, payload: payloads[0] })
client.h2LStream.writeRequest({ type: PING, payload: payloads[1], flags: { isAck: true } })
it('should send pings with the right ACK flag', () => {
expect(receivedFlags[0].isAck).to.equal(false)
expect(receivedFlags[1].isAck).to.equal(true)
Expand Down

0 comments on commit 832b299

Please sign in to comment.