88# those terms.
99
1010import
11- unittest2, chronos/ unittest2/ asynctests,
11+ unittest2,
12+ chronos/ unittest2/ asynctests,
1213 ../ json_rpc/ [rpcserver, rpcclient, jsonmarshal],
13- ./ private/ helpers
14+ ./ private/ helpers,
15+ stew/ byteutils
1416
1517const TestsCount = 100
1618const bigChunkSize = 4 * 8192
@@ -19,14 +21,14 @@ suite "JSON-RPC/http":
1921 setup:
2022 var httpsrv = newRpcHttpServer ([" 127.0.0.1:0" ])
2123 # Create RPC on server
22- httpsrv.rpc (" myProc" ) do (input: string , data: array [0 .. 3 , int ]):
24+ httpsrv.rpc (" myProc" ) do (input: string , data: array [0 .. 3 , int ]):
2325 result = % (" Hello " & input & " data: " & $ data)
2426 httpsrv.rpc (" noParamsProc" ) do ():
2527 result = % (" Hello world" )
2628
2729 httpsrv.rpc (" bigchunkMethod" ) do () -> seq [byte ]:
2830 result = newSeq [byte ](bigChunkSize)
29- for i in 0 ..< result .len:
31+ for i in 0 ..< result .len:
3032 result [i] = byte (i mod 255 )
3133
3234 httpsrv.setMaxChunkSize (8192 )
@@ -47,10 +49,11 @@ suite "JSON-RPC/http":
4749
4850 asyncTest " Continuous RPC calls (" & $ TestsCount & " messages)" :
4951 var client = newRpcHttpClient ()
50- for i in 0 ..< TestsCount :
52+ for i in 0 ..< TestsCount :
5153 await client.connect (" http://" & serverAddress)
5254 var r = await client.call (" myProc" , % [% " abc" , % [1 , 2 , 3 , i]])
53- check: r.string == " \" Hello abc data: [1, 2, 3, " & $ i & " ]\" "
55+ check:
56+ r.string == " \" Hello abc data: [1, 2, 3, " & $ i & " ]\" "
5457 await client.close ()
5558
5659 asyncTest " Invalid RPC calls" :
@@ -88,3 +91,67 @@ suite "JSON-RPC/http":
8891
8992 check:
9093 notif
94+
95+ asyncTest " Chunked encoding" :
96+ # The server doesn't use chunked encoding but the client might encounter
97+ # such servers - test that it works as expected
98+ let host = " 127.0.0.1"
99+ var server = createStreamServer (initTAddress (host & " :0" ), {ReuseAddr })
100+ defer :
101+ await server.closeWait ()
102+
103+ let port = server.localAddress ().port
104+
105+ proc simpleServerResponder () {.async .} =
106+ try :
107+ let conn = await server.accept ()
108+
109+ # 1. Consume the incoming headers but ignore the body hoping it's small
110+ # enough
111+ var headerBuf: seq [byte ] = newSeq [byte ](8192 )
112+ discard
113+ await conn.readUntil (addr headerBuf[0 ], headerBuf.len, toBytes (" \r\n\r\n " ))
114+
115+ # 2. Send HTTP response with Chunked Transfer Encoding
116+ var respHeader = (
117+ " HTTP/1.1 200 OK\r\n Content-Type: application/json\r\n " &
118+ " Transfer-Encoding: chunked\r\n\r\n "
119+ ).toBytes ()
120+ discard await conn.write (respHeader)
121+
122+ # JSON Response broken into two chunks
123+ let fullMsg = """ {"jsonrpc":"2.0","result":{"s":"part1"},"id":1} """
124+ let mid = fullMsg.len div 2
125+ let part1 = fullMsg[0 ..< mid]
126+ let part2 = fullMsg[mid ..^ 1 ]
127+
128+ # Chunk 1: size in hex + \r\n
129+ discard await conn.write (
130+ (toHex (part1.len).strip (trailing = false , chars = {'0' }) & " \r\n " ).toBytes ()
131+ )
132+ discard await conn.write (part1.toBytes ())
133+
134+ # Chunk 2: size in hex + \r\n
135+ discard await conn.write (
136+ (" \r\n " & toHex (part2.len).strip (trailing = false , chars = {'0' }) & " \r\n " ).toBytes ()
137+ )
138+ discard await conn.write (part2.toBytes ())
139+
140+ # Final empty chunk to end the stream (0\r\n\r\n)
141+ discard await conn.write (" \r\n 0\r\n\r\n " .toBytes ())
142+ await conn.closeWait ()
143+ except CatchableError as exc:
144+ raiseAssert exc.msg
145+
146+ var st = simpleServerResponder ()
147+
148+ var client = newRpcHttpClient ()
149+
150+ try :
151+ await client.connect (host, port, false )
152+
153+ let res = await client.call (" anyMethod" , %* {" param" : " value" })
154+ check res == JsonString """ {"s":"part1"} """
155+ finally :
156+ await client.close ()
157+ await st.cancelAndWait ()
0 commit comments