v / vlib / net / http / chunked / dechunk.v
76 lines · 69 sloc · 1.41 KB · c51d30bf5309653c6b573ec815268e69a78ea8cc
Raw
1module chunked
2
3import strings
4// See: https://en.wikipedia.org/wiki/Chunked_transfer_encoding
5// /////////////////////////////////////////////////////////////
6// The chunk size is transferred as a hexadecimal number
7// followed by \r\n as a line separator,
8// followed by a chunk of data of the given size.
9// The end is marked with a chunk with size 0.
10
11struct ChunkScanner {
12mut:
13 pos int
14 text string
15}
16
17fn (mut s ChunkScanner) read_chunk_size() u32 {
18 mut n := u32(0)
19 for {
20 if s.pos >= s.text.len {
21 break
22 }
23 c := s.text[s.pos]
24 if !c.is_hex_digit() {
25 break
26 }
27 n = n << 4
28 n += u32(unhex(c))
29 s.pos++
30 }
31 return n
32}
33
34fn unhex(c u8) u8 {
35 if `0` <= c && c <= `9` {
36 return c - `0`
37 } else if `a` <= c && c <= `f` {
38 return c - `a` + 10
39 } else if `A` <= c && c <= `F` {
40 return c - `A` + 10
41 }
42 return 0
43}
44
45fn (mut s ChunkScanner) skip_crlf() {
46 s.pos += 2
47}
48
49fn (mut s ChunkScanner) read_chunk(chunksize u32) !string {
50 startpos := s.pos
51 s.pos += int(chunksize)
52 if s.pos > s.text.len {
53 return error('invalid chunksize')
54 }
55 return s.text[startpos..s.pos]
56}
57
58pub fn decode(text string) !string {
59 mut sb := strings.new_builder(100)
60 mut cscanner := ChunkScanner{
61 pos: 0
62 text: text
63 }
64 for {
65 csize := cscanner.read_chunk_size()
66 if 0 == csize {
67 break
68 }
69 cscanner.skip_crlf()
70 ch := cscanner.read_chunk(csize)!
71 sb.write_string(ch)
72 cscanner.skip_crlf()
73 }
74 cscanner.skip_crlf()
75 return sb.str()
76}
77