Skip to content

Commit 09d7de9

Browse files
Improve plain api, add decoder for chunked encoding
1 parent bb07dc1 commit 09d7de9

File tree

1 file changed

+46
-8
lines changed

1 file changed

+46
-8
lines changed

libraries/common/io/requests.effekt

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ namespace jsWeb {
318318
namespace plain {
319319
def constructRequest{ body: => Unit / RequestBuilder }: Unit / emit[Byte] = {
320320
def crlf() = {
321-
do emit('\n'.toInt.toByte)
322321
do emit('\r'.toInt.toByte)
322+
do emit('\n'.toInt.toByte)
323323
}
324324

325325
with encodeUTF8
@@ -397,11 +397,49 @@ namespace plain {
397397
val k = collectString { readWhile { c => c.isHTTPTokenChar() } }
398398
readIf(':')
399399
skipWhile { c => c == ' ' || c == '\t' }
400-
val v = collectString { readWhile { c => c != '\n' } }
400+
val v = collectString { readWhile { c => c != '\r' } }
401401
skipWhile { c => c == ' ' || c == '\t' }
402402
(k, v)
403403
}
404404

405+
/// Decode a chunked transfer coding as per RFC 2616.
406+
/// Emits the contents as bytes, and headers from the trailer.
407+
def unchunk(): Int / { emit[Byte], emit[(String, String)], read[Byte], Exception[WrongFormat] } = {
408+
with decodeUTF8
409+
with returning::scanner[Char, Int]
410+
var length = 0
411+
loop { {l} =>
412+
// chunk header
413+
val size = returning::expect("chunk header"){
414+
val size = collectString{ readWhile{ c => c.isHexDigit } }.toInt(16)
415+
val extensions = collectString{ readWhile{ c => c != '\r' } } // ignored
416+
if(size == 0){ l.break() }
417+
readIf('\r')
418+
readIf('\n')
419+
size
420+
}
421+
// chunk body
422+
length = length + size
423+
repeat(size){
424+
try {
425+
do emit(do read[Byte]())
426+
} with stop { () => do raise(WrongFormat(), "Premature end of chunk in chunked encoding") }
427+
}
428+
}
429+
// trailer headers
430+
expect("Trailers or end of response"){
431+
while(do peek[Char]() != '\r') {
432+
do emit(parseHeaderLine())
433+
readIf('\r')
434+
readIf('\n')
435+
}
436+
readIf('\r')
437+
readIf('\n')
438+
}
439+
// return total length
440+
length
441+
}
442+
405443
def parseResponse[R]{ body: {ResponseReader} => R }: R / { read[Byte], Exception[WrongFormat] } = {
406444
with decodeUTF8
407445
with returning::scanner[Char, R]
@@ -414,18 +452,18 @@ namespace plain {
414452
statusCode = readInteger()
415453
if (statusCode > 599 || statusCode < 100) { do raise(WrongFormat(), "Invalid status code") }
416454
readIf(' ')
417-
val reason = collectString{ readWhile{ c => c != '\n' } }
418-
readIf('\n')
455+
val reason = collectString{ readWhile{ c => c != '\r' } }
419456
readIf('\r')
457+
readIf('\n')
420458
headers = collectList {
421-
while(do peek[Char]() != '\n') {
459+
while(do peek[Char]() != '\r') {
422460
do emit(parseHeaderLine())
423-
readIf('\n')
424461
readIf('\r')
462+
readIf('\n')
425463
}
426464
}
427-
readIf('\n')
428465
readIf('\r')
466+
readIf('\n')
429467
}
430468

431469
try body{res} with res: ResponseReader {
@@ -633,8 +671,8 @@ namespace example {
633671
})
634672
with source[Byte]{
635673
def crlf() = {
636-
do emit('\n'.toInt.toByte)
637674
do emit('\r'.toInt.toByte)
675+
do emit('\n'.toInt.toByte)
638676
}
639677
with encodeUTF8;
640678
"HTTP/1.1 200 OK".each;

0 commit comments

Comments
 (0)