Skip to content

Commit b5280f8

Browse files
authored
Merge pull request #567 from rmunn/feature/http2
Fix a few endianness bugs on HTTP/2 branch
2 parents 8120ab3 + f512dd5 commit b5280f8

File tree

1 file changed

+44
-43
lines changed

1 file changed

+44
-43
lines changed

src/Suave/Http2.fs

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Http2 =
44

55
let connectionPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
66

7-
type ErrorCode =
7+
type ErrorCode =
88
| NoError
99
| ProtocolError
1010
| InternalError
@@ -69,14 +69,14 @@ module Http2 =
6969
let setPadded x = set x 3
7070
let setPriority x = set x 5
7171

72-
type FrameHeader = {
73-
// the length field allows payloads of up to 224224 bytes (~16MB) per frame
72+
type FrameHeader = {
73+
// the length field allows payloads of up to 2^24 bytes (~16MB) per frame
7474
length : int32;
7575
``type`` : byte;
7676
flags : byte;
7777
streamIdentifier : int32 }
7878

79-
type Settings =
79+
type Settings =
8080
{ headerTableSize : int32
8181
; enablePush : bool
8282
; maxConcurrentStreams : int32 option
@@ -121,9 +121,10 @@ module Http2 =
121121
Array.Reverse b
122122
b
123123

124+
/// Get 31-bit int from 4-byte array (which MUST be in network order)
124125
let get31Bit (data: byte []) =
125-
data.[3] <- data.[3] &&& 128uy
126-
BitConverter.ToInt32(data, 0)
126+
data.[0] <- data.[0] &&& 127uy
127+
BitConverter.ToInt32(checkEndianness data, 0)
127128

128129
let get24BitBytes (value : Int32) =
129130
let bytes = BitConverter.GetBytes value
@@ -143,8 +144,8 @@ module Http2 =
143144
let frameStreamIdData = Array.zeroCreate<byte> 4
144145
Array.Copy (bytes, 5, frameStreamIdData, 0, 4)
145146

146-
// turn of most significant bit
147-
let streamIdentifier = get31Bit (checkEndianness frameStreamIdData)
147+
// turn off most significant bit
148+
let streamIdentifier = get31Bit frameStreamIdData
148149

149150
{ length = frameLength;
150151
``type`` = frameType;
@@ -163,11 +164,9 @@ module Http2 =
163164
bytes.[3] <- header.``type``
164165
bytes.[4] <- header.flags
165166

166-
let encodedStreamIdentifier = BitConverter.GetBytes header.streamIdentifier
167+
let encodedStreamIdentifier = BitConverter.GetBytes header.streamIdentifier |> checkEndianness
167168

168-
encodedStreamIdentifier.[3] <- encodedStreamIdentifier.[3] &&& 128uy
169-
170-
let encodedStreamIdentifier = checkEndianness encodedStreamIdentifier
169+
encodedStreamIdentifier.[0] <- encodedStreamIdentifier.[0] &&& 127uy
171170

172171
bytes.[5] <- encodedStreamIdentifier.[0]
173172
bytes.[6] <- encodedStreamIdentifier.[1]
@@ -181,7 +180,7 @@ module Http2 =
181180
return parseFrameHeader bytes
182181
}
183182

184-
type Priority =
183+
type Priority =
185184
{ exclusive : bool
186185
; streamIdentifier : int32
187186
; weight: byte
@@ -216,7 +215,7 @@ module Http2 =
216215

217216
let parseData (header: FrameHeader) (payload: byte[]) =
218217
assert(header.``type`` = 0uy)
219-
// If a DATA frame is received whose stream identifier field is 0x0,
218+
// If a DATA frame is received whose stream identifier field is 0x0,
220219
// the recipient MUST respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
221220
let isEndStream = (header.flags &&& 0x1uy) = 0x1uy
222221
let data = removePadding header payload
@@ -228,7 +227,7 @@ module Http2 =
228227
if priority then
229228
let dependecyData = Array.zeroCreate 4
230229
Array.Copy(payload, 0, dependecyData, 0, 4)
231-
let dependency = get31Bit (checkEndianness dependecyData)
230+
let dependency = get31Bit dependecyData
232231
let weight = payload.[4]
233232

234233
Some ({ exclusive = true; streamIdentifier = dependency;weight = weight})
@@ -284,7 +283,7 @@ module Http2 =
284283
let data = removePadding header payload
285284
let frameStreamIdData = Array.zeroCreate<byte> 4
286285
Array.Copy (data, 0, frameStreamIdData, 0, 4)
287-
let streamIdentifier = get31Bit (checkEndianness frameStreamIdData)
286+
let streamIdentifier = get31Bit frameStreamIdData
288287
let headerBlockFragment = Array.zeroCreate (data.Length - 5)
289288
Array.Copy(data,headerBlockFragment,data.Length - 5)
290289
PushPromise (streamIdentifier,headerBlockFragment)
@@ -299,7 +298,7 @@ module Http2 =
299298
assert(header.``type`` = 7uy)
300299
let idData = Array.zeroCreate<byte> 4
301300
Array.Copy (payload, 0, idData, 0, 4)
302-
let streamIdentifier = get31Bit (checkEndianness idData)
301+
let streamIdentifier = get31Bit idData
303302
Array.Copy (payload, 4, idData, 0, 4)
304303
let errorCode = BitConverter.ToUInt32 (checkEndianness idData, 0)
305304
GoAway (streamIdentifier,toErrorCode (int errorCode), Array.sub payload 8 (payload.Length - 8))
@@ -308,14 +307,14 @@ module Http2 =
308307
assert(header.``type`` = 8uy)
309308
let idData = Array.zeroCreate<byte> 4
310309
Array.Copy (payload, 0, idData, 0, 4)
311-
let windowSizeIncrement = get31Bit (checkEndianness idData)
310+
let windowSizeIncrement = get31Bit idData
312311
WindowUpdate windowSizeIncrement
313312

314313
let parseContinuation (header: FrameHeader) (payload: byte[]) =
315314
assert(header.``type`` = 8uy)
316315
Continuation payload
317316

318-
let payloadDecoders : PayloadDecoder [] =
317+
let payloadDecoders : PayloadDecoder [] =
319318
[| parseData; parseHeaders; parsePriority; parseRstStream; parseSettings; parsePushPromise; parsePing;
320319
parseGoAway; parseWindowUpdate; parseContinuation |]
321320

@@ -331,15 +330,17 @@ module Http2 =
331330
do! t.write(ByteSegment(bytes,0,bytes.Length))
332331
}
333332

333+
/// Poke a 16-bit int into a byte array in network byte order (big-endian)
334334
let poke16 (arr : byte array) i w =
335-
arr.[i] <- byte w
336-
arr.[i + 1] <- byte (w >>> 8)
335+
arr.[i] <- byte (w >>> 8)
336+
arr.[i+ 1] <- byte w
337337

338+
/// Poke a 32-bit int into a byte array in network byte order (big-endian)
338339
let poke32 (arr : byte array) i w =
339-
arr.[i] <- byte w
340-
arr.[i + 1] <- byte (w >>> 8)
341-
arr.[i + 2] <- byte (w >>> 0x10)
342-
arr.[i + 3] <- byte (w >>> 0x18)
340+
arr.[i] <- byte (w >>> 0x18)
341+
arr.[i + 1] <- byte (w >>> 0x10)
342+
arr.[i + 2] <- byte (w >>> 8)
343+
arr.[i + 3] <- byte w
343344

344345
let encodePriority priority =
345346
let arr = Array.zeroCreate<byte> 5
@@ -353,16 +354,16 @@ module Http2 =
353354
| Data (bytes, flag) ->
354355
{ length = bytes.Length; ``type`` = 0uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier} ,
355356
[ bytes ]
356-
| Headers(None, bytes) ->
357+
| Headers(None, bytes) ->
357358
{ length = bytes.Length; ``type`` = 1uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier} ,
358359
[ bytes ]
359-
| Headers(Some priority, bytes) ->
360+
| Headers(Some priority, bytes) ->
360361
{ length = bytes.Length + 5; ``type`` = 1uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier} ,
361362
[ encodePriority priority ; bytes ]
362-
| Priority None ->
363+
| Priority None ->
363364
{ length = 0; ``type`` = 2uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier} ,
364365
[[||]]
365-
| Priority (Some priority) ->
366+
| Priority (Some priority) ->
366367
{ length = 5; ``type`` = 2uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier} ,
367368
[encodePriority priority]
368369
| RstStream errorCode ->
@@ -372,7 +373,7 @@ module Http2 =
372373
[ b4 ]
373374
| Settings (flag,settings) ->
374375
{ length = 36; ``type`` = 4uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier } ,
375-
[
376+
[
376377
for settingIdentifier in [ 1 .. 6 ] do
377378
let arr = Array.zeroCreate<byte> 6
378379
poke16 arr 0 settingIdentifier
@@ -402,27 +403,27 @@ module Http2 =
402403
failwith "invalid setting identifier"
403404
yield arr
404405
]
405-
| PushPromise (i,bytes) ->
406+
| PushPromise (i,bytes) ->
406407
let b4 = Array.zeroCreate 4
407408
poke32 b4 0 i
408409
{ length = bytes.Length + 4; ``type`` = 5uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier } ,
409-
[ checkEndianness b4 ; bytes ]
410+
[ b4 ; bytes ]
410411
| Ping (flag,bytes) ->
411412
{ length = bytes.Length; ``type`` = 6uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier } ,
412-
[ bytes]
413-
| GoAway (i,errorCode,bytes) ->
413+
[ bytes]
414+
| GoAway (i,errorCode,bytes) ->
414415
let a4 = Array.zeroCreate 4
415416
let b4 = Array.zeroCreate 4
416417
poke32 a4 0 i
417418
poke32 b4 0 (fromErrorCode errorCode)
418419
{ length = bytes.Length + 8; ``type`` = 7uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier } ,
419-
[ checkEndianness a4; b4; bytes]
420-
| WindowUpdate w ->
420+
[ a4; b4; bytes]
421+
| WindowUpdate w ->
421422
let b4 = Array.zeroCreate 4
422423
poke32 b4 0 w
423424
{ length = 4; ``type`` = 8uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier } ,
424425
[ b4 ]
425-
| Continuation bytes ->
426+
| Continuation bytes ->
426427
{ length = bytes.Length; ``type`` = 9uy ; flags = encodeInfo.flags; streamIdentifier = encodeInfo.streamIdentifier } ,
427428
[ bytes ]
428429

@@ -448,7 +449,7 @@ module Http2 =
448449

449450
open System.Collections.Generic
450451

451-
let newDynamicRevIndex _ =
452+
let newDynamicRevIndex _ =
452453
Array.map (fun _ -> new Dictionary<HeaderValue,HIndex>()) [| minTokenIx .. maxStaticTokenIndex |]
453454

454455
let newOtherRevIndex _ = new Dictionary<KeyValue,HIndex>()
@@ -463,7 +464,7 @@ module Http2 =
463464
let buf = Array.zeroCreate huftmpsiz
464465
let decoder = decode buf huftmpsiz
465466
newDynamicTable maxsiz (DecodeInfo(decoder,maxsiz))
466-
467+
467468
open Suave.Utils
468469
open System.Threading
469470

@@ -480,10 +481,10 @@ module Http2 =
480481
member val encodeDynamicTable = newDynamicTableForEncoding defaultDynamicTableSize
481482
member val decodeDynamicTable = newDynamicTableForDecoding defaultDynamicTableSize 4096
482483

483-
member x.read() : SocketOp<Frame> =
484+
member x.read() : SocketOp<Frame> =
484485
readFrame facade
485486

486-
member x.write(encInfo,p:FramePayload) : SocketOp<unit> =
487+
member x.write(encInfo,p:FramePayload) : SocketOp<unit> =
487488
writeFrame encInfo p (facade.GetCurrentContext().connection.transport)
488489

489490
member x.writeResponseToFrame (response: HttpResult) = async {
@@ -502,14 +503,14 @@ module Http2 =
502503
member x.send (r:HttpRequest) =
503504
Async.Start (procQueue.AsyncAdd <| Request r)
504505

505-
member x.stop() =
506+
member x.stop() =
506507
Async.Start (procQueue.AsyncAdd <| Stop)
507508
closeEvent.WaitOne() |> ignore
508509

509510
member x.get() =
510511
procQueue.AsyncGet()
511512

512-
member x.writeLoop (ctxOuter : HttpContext) (webPart: WebPart) =
513+
member x.writeLoop (ctxOuter : HttpContext) (webPart: WebPart) =
513514
async {
514515
// send an empty SETTINGS frame
515516
let encInfo = { flags = 1uy (*ack*); streamIdentifier = 0; padding = None}

0 commit comments

Comments
 (0)