UNPKG

5.91 kBJavaScriptView Raw
1'use strict'
2
3var assert = require('assert')
4var thing = require('handle-thing')
5var httpDeceiver = require('http-deceiver')
6var util = require('util')
7
8function Handle (options, stream, socket) {
9 var state = {}
10 this._spdyState = state
11
12 state.options = options || {}
13
14 state.stream = stream
15 state.socket = null
16 state.rawSocket = socket || stream.connection.socket
17 state.deceiver = null
18 state.ending = false
19
20 var self = this
21 thing.call(this, stream, {
22 getPeerName: function () {
23 return self._getPeerName()
24 },
25 close: function (callback) {
26 return self._closeCallback(callback)
27 }
28 })
29
30 if (!state.stream) {
31 this.on('stream', function (stream) {
32 state.stream = stream
33 })
34 }
35}
36util.inherits(Handle, thing)
37module.exports = Handle
38
39Handle.create = function create (options, stream, socket) {
40 return new Handle(options, stream, socket)
41}
42
43Handle.prototype._getPeerName = function _getPeerName () {
44 var state = this._spdyState
45
46 if (state.rawSocket._getpeername) {
47 return state.rawSocket._getpeername()
48 }
49
50 return null
51}
52
53Handle.prototype._closeCallback = function _closeCallback (callback) {
54 var state = this._spdyState
55 var stream = state.stream
56
57 if (state.ending) {
58 // The .end() method of the stream may be called by us or by the
59 // .shutdown() method in our super-class. If the latter has already been
60 // called, then calling the .end() method below will have no effect, with
61 // the result that the callback will never get executed, leading to an ever
62 // so subtle memory leak.
63 if (stream._writableState.finished) {
64 // NOTE: it is important to call `setImmediate` instead of `nextTick`,
65 // since this is how regular `handle.close()` works in node.js core.
66 //
67 // Using `nextTick` will lead to `net.Socket` emitting `close` before
68 // `end` on UV_EOF. This results in aborted request without `end` event.
69 setImmediate(callback)
70 } else if (stream._writableState.ending) {
71 stream.once('finish', function () {
72 callback(null)
73 })
74 } else {
75 stream.end(callback)
76 }
77 } else {
78 stream.abort(callback)
79 }
80
81 // Only a single end is allowed
82 state.ending = false
83}
84
85Handle.prototype.getStream = function getStream (callback) {
86 var state = this._spdyState
87
88 if (!callback) {
89 assert(state.stream)
90 return state.stream
91 }
92
93 if (state.stream) {
94 process.nextTick(function () {
95 callback(state.stream)
96 })
97 return
98 }
99
100 this.on('stream', callback)
101}
102
103Handle.prototype.assignSocket = function assignSocket (socket, options) {
104 var state = this._spdyState
105
106 state.socket = socket
107 state.deceiver = httpDeceiver.create(socket, options)
108
109 function onStreamError (err) {
110 state.socket.emit('error', err)
111 }
112
113 this.getStream(function (stream) {
114 stream.on('error', onStreamError)
115 })
116}
117
118Handle.prototype.assignClientRequest = function assignClientRequest (req) {
119 var state = this._spdyState
120 var oldEnd = req.end
121 var oldSend = req._send
122
123 // Catch the headers before request will be sent
124 var self = this
125
126 // For old nodes
127 if (thing.mode !== 'modern') {
128 req.end = function end () {
129 this.end = oldEnd
130
131 this._send('')
132
133 return this.end.apply(this, arguments)
134 }
135 }
136
137 req._send = function send (data) {
138 this._headerSent = true
139
140 // for v0.10 and below, otherwise it will set `hot = false` and include
141 // headers in first write
142 this._header = 'ignore me'
143
144 // To prevent exception
145 this.connection = state.socket
146
147 // It is very important to leave this here, otherwise it will be executed
148 // on a next tick, after `_send` will perform write
149 self.getStream(function (stream) {
150 if (!stream.connection._isGoaway(stream.id)) {
151 stream.send()
152 }
153 })
154
155 // We are ready to create stream
156 self.emit('needStream')
157
158 // Ensure that the connection is still ok to use
159 if (state.stream && state.stream.connection._isGoaway(state.stream.id)) {
160 return
161 }
162
163 req._send = oldSend
164
165 // Ignore empty writes
166 if (req.method === 'GET' && data.length === 0) {
167 return
168 }
169
170 return req._send.apply(this, arguments)
171 }
172
173 // No chunked encoding
174 req.useChunkedEncodingByDefault = false
175
176 req.on('finish', function () {
177 req.socket.end()
178 })
179}
180
181Handle.prototype.assignRequest = function assignRequest (req) {
182 // Emit trailing headers
183 this.getStream(function (stream) {
184 stream.on('headers', function (headers) {
185 req.emit('trailers', headers)
186 })
187 })
188}
189
190Handle.prototype.assignResponse = function assignResponse (res) {
191 var self = this
192
193 res.addTrailers = function addTrailers (headers) {
194 self.getStream(function (stream) {
195 stream.sendHeaders(headers)
196 })
197 }
198}
199
200Handle.prototype._transformHeaders = function _transformHeaders (kind, headers) {
201 var state = this._spdyState
202
203 var res = {}
204 var keys = Object.keys(headers)
205
206 if (kind === 'request' && state.options['x-forwarded-for']) {
207 var xforwarded = state.stream.connection.getXForwardedFor()
208 if (xforwarded !== null) {
209 res['x-forwarded-for'] = xforwarded
210 }
211 }
212
213 for (var i = 0; i < keys.length; i++) {
214 var key = keys[i]
215 var value = headers[key]
216
217 if (key === ':authority') {
218 res.host = value
219 }
220 if (/^:/.test(key)) {
221 continue
222 }
223
224 res[key] = value
225 }
226 return res
227}
228
229Handle.prototype.emitRequest = function emitRequest () {
230 var state = this._spdyState
231 var stream = state.stream
232
233 state.deceiver.emitRequest({
234 method: stream.method,
235 path: stream.path,
236 headers: this._transformHeaders('request', stream.headers)
237 })
238}
239
240Handle.prototype.emitResponse = function emitResponse (status, headers) {
241 var state = this._spdyState
242
243 state.deceiver.emitResponse({
244 status: status,
245 headers: this._transformHeaders('response', headers)
246 })
247}