http-2 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,302 @@
1
+ module HTTP2
2
+
3
+ # Performs encoding, decoding, and validation of binary HTTP 2.0 frames.
4
+ #
5
+ class Framer
6
+ include Error
7
+
8
+ # Maximum frame size (65535 bytes)
9
+ MAX_PAYLOAD_SIZE = 2**16-1
10
+
11
+ # Maximum stream ID (2^31)
12
+ MAX_STREAM_ID = 0x7fffffff
13
+
14
+ # Maximum window increment value (2^31)
15
+ MAX_WINDOWINC = 0x7fffffff
16
+
17
+ # HTTP 2.0 frame type mapping as defined by the spec
18
+ FRAME_TYPES = {
19
+ data: 0x0,
20
+ headers: 0x1,
21
+ priority: 0x2,
22
+ rst_stream: 0x3,
23
+ settings: 0x4,
24
+ push_promise: 0x5,
25
+ ping: 0x6,
26
+ goaway: 0x7,
27
+ window_update: 0x9,
28
+ continuation: 0xa
29
+ }
30
+
31
+ # Per frame flags as defined by the spec
32
+ FRAME_FLAGS = {
33
+ data: {
34
+ end_stream: 0, reserved: 1
35
+ },
36
+ headers: {
37
+ end_stream: 0, reserved: 1,
38
+ end_headers: 2, priority: 3
39
+ },
40
+ priority: {},
41
+ rst_stream: {},
42
+ settings: {},
43
+ push_promise: { end_push_promise: 0 },
44
+ ping: { pong: 0 },
45
+ goaway: {},
46
+ window_update:{},
47
+ continuation: {
48
+ end_stream: 0, end_headers: 1
49
+ }
50
+ }
51
+
52
+ # Default settings as defined by the spec
53
+ DEFINED_SETTINGS = {
54
+ settings_max_concurrent_streams: 4,
55
+ settings_initial_window_size: 7,
56
+ settings_flow_control_options: 10
57
+ }
58
+
59
+ # Default error types as defined by the spec
60
+ DEFINED_ERRORS = {
61
+ no_error: 0,
62
+ protocol_error: 1,
63
+ internal_error: 2,
64
+ flow_control_error: 3,
65
+ stream_closed: 5,
66
+ frame_too_large: 6,
67
+ refused_stream: 7,
68
+ cancel: 8,
69
+ compression_error: 9
70
+ }
71
+
72
+ RBIT = 0x7fffffff
73
+ RBYTE = 0x0fffffff
74
+ HEADERPACK = "SCCL"
75
+ UINT32 = "L"
76
+
77
+ private_constant :RBIT, :RBYTE, :HEADERPACK, :UINT32
78
+
79
+ # Generates common 8-byte frame header.
80
+ # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-04#section-4.1
81
+ #
82
+ # @param frame [Hash]
83
+ # @return [String]
84
+ def commonHeader(frame)
85
+ header = []
86
+
87
+ if !FRAME_TYPES[frame[:type]]
88
+ raise CompressionError.new("Invalid frame type (#{frame[:type]})")
89
+ end
90
+
91
+ if frame[:length] > MAX_PAYLOAD_SIZE
92
+ raise CompressionError.new("Frame size is too large: #{frame[:length]}")
93
+ end
94
+
95
+ if frame[:stream] > MAX_STREAM_ID
96
+ raise CompressionError.new("Stream ID (#{frame[:stream]}) is too large")
97
+ end
98
+
99
+ if frame[:type] == :window_update && frame[:increment] > MAX_WINDOWINC
100
+ raise CompressionError.new("Window increment (#{frame[:increment]}) is too large")
101
+ end
102
+
103
+ header << frame[:length]
104
+ header << FRAME_TYPES[frame[:type]]
105
+ header << frame[:flags].reduce(0) do |acc, f|
106
+ position = FRAME_FLAGS[frame[:type]][f]
107
+ if !position
108
+ raise CompressionError.new("Invalid frame flag (#{f}) for #{frame[:type]}")
109
+ end
110
+
111
+ acc |= (1 << position)
112
+ acc
113
+ end
114
+
115
+ header << frame[:stream]
116
+ header.pack(HEADERPACK) # 16,8,8,32
117
+ end
118
+
119
+ # Decodes common 8-byte header.
120
+ #
121
+ # @param buf [String]
122
+ def readCommonHeader(buf)
123
+ frame = {}
124
+ frame[:length], type, flags, stream = buf.slice(0,8).unpack(HEADERPACK)
125
+
126
+ frame[:type], _ = FRAME_TYPES.select { |t,pos| type == pos }.first
127
+ frame[:flags] = FRAME_FLAGS[frame[:type]].reduce([]) do |acc, (name, pos)|
128
+ acc << name if (flags & (1 << pos)) > 0
129
+ acc
130
+ end
131
+
132
+ frame[:stream] = stream & RBIT
133
+ frame
134
+ end
135
+
136
+ # Generates encoded HTTP 2.0 frame.
137
+ # - http://tools.ietf.org/html/draft-ietf-httpbis-http2
138
+ #
139
+ # @param frame [Hash]
140
+ def generate(frame)
141
+ bytes = ''
142
+ length = 0
143
+
144
+ frame[:flags] ||= []
145
+ frame[:stream] ||= 0
146
+
147
+ case frame[:type]
148
+ when :data
149
+ bytes += frame[:payload]
150
+ length += frame[:payload].bytesize
151
+
152
+ when :headers
153
+ if frame[:priority]
154
+ frame[:flags] += [:priority] if !frame[:flags].include? :priority
155
+ end
156
+
157
+ if frame[:flags].include? :priority
158
+ bytes += [frame[:priority] & RBIT].pack(UINT32)
159
+ length += 4
160
+ end
161
+
162
+ bytes += frame[:payload]
163
+ length += frame[:payload].bytesize
164
+
165
+ when :priority
166
+ bytes += [frame[:priority] & RBIT].pack(UINT32)
167
+ length += 4
168
+
169
+ when :rst_stream
170
+ bytes += pack_error frame[:error]
171
+ length += 4
172
+
173
+ when :settings
174
+ if frame[:stream] != 0
175
+ raise CompressionError.new("Invalid stream ID (#{frame[:stream]})")
176
+ end
177
+
178
+ frame[:payload].each do |(k,v)|
179
+ if !k.is_a? Integer
180
+ k = DEFINED_SETTINGS[k]
181
+
182
+ if k.nil?
183
+ raise CompressionError.new("Unknown settings ID for #{k}")
184
+ end
185
+ end
186
+
187
+ bytes += [k & RBYTE].pack(UINT32)
188
+ bytes += [v].pack(UINT32)
189
+ length += 8
190
+ end
191
+
192
+ when :push_promise
193
+ bytes += [frame[:promise_stream] & RBIT].pack(UINT32)
194
+ bytes += frame[:payload]
195
+ length += 4 + frame[:payload].bytesize
196
+
197
+ when :ping
198
+ if frame[:payload].bytesize != 8
199
+ raise CompressionError.new("Invalid payload size \
200
+ (#{frame[:payload].size} != 8 bytes)")
201
+ end
202
+ bytes += frame[:payload]
203
+ length += 8
204
+
205
+ when :goaway
206
+ bytes += [frame[:last_stream] & RBIT].pack(UINT32)
207
+ bytes += pack_error frame[:error]
208
+ length += 8
209
+
210
+ if frame[:payload]
211
+ bytes += frame[:payload]
212
+ length += frame[:payload].bytesize
213
+ end
214
+
215
+ when :window_update
216
+ bytes += [frame[:increment] & RBIT].pack(UINT32)
217
+ length += 4
218
+
219
+ when :continuation
220
+ bytes += frame[:payload]
221
+ length += frame[:payload].bytesize
222
+ end
223
+
224
+ frame[:length] = length
225
+ commonHeader(frame) + bytes
226
+ end
227
+
228
+ # Decodes complete HTTP 2.0 frame from provided buffer. If the buffer
229
+ # does not contain enough data, no further work is performed.
230
+ #
231
+ # @param buf [String]
232
+ def parse(buf)
233
+ return nil if buf.size < 8
234
+ frame = readCommonHeader(buf)
235
+ return nil if buf.size < 8 + frame[:length]
236
+
237
+ buf.read(8)
238
+ payload = buf.read(frame[:length])
239
+
240
+ case frame[:type]
241
+ when :data
242
+ frame[:payload] = payload.read(frame[:length])
243
+ when :headers
244
+ if frame[:flags].include? :priority
245
+ frame[:priority] = payload.read(4).unpack(UINT32).first & RBIT
246
+ end
247
+ frame[:payload] = payload.read(frame[:length])
248
+ when :priority
249
+ frame[:priority] = payload.read(4).unpack(UINT32).first & RBIT
250
+ when :rst_stream
251
+ frame[:error] = unpack_error payload.read(4).unpack(UINT32).first
252
+
253
+ when :settings
254
+ frame[:payload] = {}
255
+ (frame[:length] / 8).times do
256
+ id = payload.read(4).unpack(UINT32).first & RBYTE
257
+ val = payload.read(4).unpack(UINT32).first
258
+
259
+ name, _ = DEFINED_SETTINGS.select { |name, v| v == id }.first
260
+ frame[:payload][name || id] = val
261
+ end
262
+ when :push_promise
263
+ frame[:promise_stream] = payload.read(4).unpack(UINT32).first & RBIT
264
+ frame[:payload] = payload.read(frame[:length])
265
+ when :ping
266
+ frame[:payload] = payload.read(frame[:length])
267
+ when :goaway
268
+ frame[:last_stream] = payload.read(4).unpack(UINT32).first & RBIT
269
+ frame[:error] = unpack_error payload.read(4).unpack(UINT32).first
270
+
271
+ size = frame[:length] - 8
272
+ frame[:payload] = payload.read(size) if size > 0
273
+ when :window_update
274
+ frame[:increment] = payload.read(4).unpack(UINT32).first & RBIT
275
+ when :continuation
276
+ frame[:payload] = payload.read(frame[:length])
277
+ end
278
+
279
+ frame
280
+ end
281
+
282
+ private
283
+
284
+ def pack_error(e)
285
+ if !e.is_a? Integer
286
+ e = DEFINED_ERRORS[e]
287
+
288
+ if e.nil?
289
+ raise CompressionError.new("Unknown error ID for #{e}")
290
+ end
291
+ end
292
+
293
+ [e].pack(UINT32)
294
+ end
295
+
296
+ def unpack_error(e)
297
+ name, _ = DEFINED_ERRORS.select { |name, v| v == e }.first
298
+ name || error
299
+ end
300
+
301
+ end
302
+ end