Skip to content

Commit d91d57f

Browse files
committed
tls tests update
1 parent ba9de8f commit d91d57f

1 file changed

Lines changed: 90 additions & 17 deletions

File tree

lang/tests/tls/src/server_direction_extra_tests.ch

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,6 @@ func sdv_send_raw(ssl : *mut SSLContext, resp : string) : int {
2424
return ssl_write(ssl, resp.data() as *u8, resp.size() as i32)
2525
}
2626

27-
// Best-effort drain of remaining request bytes (bounded by the socket recv
28-
// timeout). Used after a header-only read so late body records never linger.
29-
func sdv_drain_leftover(ssl : *mut SSLContext) {
30-
unsafe var tmp : [4096]u8
31-
while(true) {
32-
var n = ssl_read(ssl, &raw mut tmp[0], 4096)
33-
if(n <= 0) { break }
34-
}
35-
}
36-
3727
// Extract the request target (path[?query]) from the request line.
3828
func sdv_request_target(buf : *u8, consumed : size_t, out : *mut u8, out_cap : size_t) : size_t {
3929
var sp1 : size_t = 0
@@ -257,8 +247,10 @@ func sdv_loop_head(env : &mut TestEnv, ssl_mem : *mut SSLContext) : bool {
257247
}
258248

259249
// CONFORMANCE probe: python uploads a chunked request body. RFC 9112 §6
260-
// requires servers to decode Transfer-Encoding: chunked; the body must come
261-
// back echoed. A short/empty echo means chunked requests are unsupported.
250+
// requires servers to decode Transfer-Encoding: chunked. The Chemical server
251+
// here dechunks the received records itself and echoes exactly the decoded
252+
// bytes; python compares against the original payload, so any transport or
253+
// framing corruption (or undecodable chunks) fails the roundtrip.
262254
func sdv_loop_chunkup(env : &mut TestEnv, ssl_mem : *mut SSLContext) : bool {
263255
const BUF_CAP : size_t = 16384u
264256
unsafe var req_buf : [BUF_CAP]u8
@@ -272,12 +264,93 @@ func sdv_loop_chunkup(env : &mut TestEnv, ssl_mem : *mut SSLContext) : bool {
272264
&raw mut filled, &raw mut consumed)
273265
if(rret != 0) { env.error("chunkup: header read failed"); return false }
274266

275-
// Drain whatever body followed (chunked records) until quiet.
276-
sdv_drain_leftover(ssl_mem)
267+
// Accumulate body bytes until quiet.
268+
unsafe var raw_body : [4096]u8
269+
var raw_len : size_t = 0
270+
while(raw_len < 4096u) {
271+
var n = ssl_read(ssl_mem, (&raw mut raw_body[0]) + raw_len, (4096u - raw_len) as i32)
272+
if(n <= 0) { break }
273+
raw_len += (n as usize)
274+
}
275+
276+
// Dechunk per RFC 9112 §7.1: hex-size [;ext] CRLF data CRLF ... 0 CRLF trailers.
277+
unsafe var decoded : [2048]u8
278+
var dlen : size_t = 0
279+
var pos : size_t = 0
280+
var done = false
281+
while(pos < raw_len) {
282+
// Find the chunk-size line terminator.
283+
var le = pos
284+
while(le + 1 < raw_len && !(raw_body[le] == 13u8 && raw_body[le + 1u] == 10u8)) { le += 1 }
285+
if(le + 1 >= raw_len) { break }
286+
287+
// Parse hex size up to an optional ';' extension separator.
288+
var sz : usize = 0
289+
var hi = pos
290+
var any_hex = false
291+
while(hi < le) {
292+
var c = raw_body[hi]
293+
var v : int = -1
294+
if(c >= 48u8 && c <= 57u8) {
295+
v = (c - 48u8) as int
296+
} else if(c >= 97u8 && c <= 102u8) {
297+
v = (c - 87u8) as int
298+
} else if(c >= 65u8 && c <= 70u8) {
299+
v = (c - 55u8) as int
300+
}
301+
if(v < 0) { break }
302+
sz = sz * 16u + (v as usize)
303+
any_hex = true
304+
hi += 1
305+
}
306+
if(!any_hex) { break }
307+
308+
pos = le + 2u
309+
if(sz == 0u) { done = true; break }
310+
311+
var avail = raw_len - pos
312+
var take = sz
313+
if(take > avail) { take = avail }
314+
var k : usize = 0
315+
while(k < take && dlen < 2048u) {
316+
decoded[dlen] = raw_body[pos + k]
317+
dlen += 1
318+
k += 1
319+
}
320+
pos += take
321+
// Skip the data-terminating CRLF.
322+
if(pos + 1u < raw_len && raw_body[pos] == 13u8 && raw_body[pos + 1u] == 10u8) {
323+
pos += 2u
324+
}
325+
}
326+
327+
// Verify the decoded payload matches what python sent.
328+
var want = "chunk-one-chunk-two!"
329+
var match = done && dlen == 20u
330+
if(match) {
331+
var wi : size_t = 0
332+
while(wi < dlen) {
333+
if(decoded[wi] != (want[wi] as u8)) { match = false; break }
334+
wi += 1
335+
}
336+
}
337+
if(!match) {
338+
env.error("[CONFORMANCE] chunked request body not decoded correctly")
339+
var m = string("decoded=")
340+
m.append_uinteger(dlen as ubigint)
341+
m.append_view(" complete=")
342+
var ds = string("false")
343+
if(done) { ds = string("true") }
344+
m.append_view(ds.to_view())
345+
env.error(m.data())
346+
}
277347

278-
// Echo what a compliant decoder would have produced.
279-
var resp = string("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 20\r\nConnection: close\r\n\r\n")
280-
resp.append_view(string_view("chunk-one-chunk-two!"))
348+
// Echo exactly what we decoded — python validates against the original.
349+
var resp = string("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: ")
350+
resp.append_uinteger(dlen as ubigint)
351+
resp.append_view("\r\nConnection: close\r\n\r\n")
352+
var di2 : size_t = 0
353+
while(di2 < dlen) { resp.append(decoded[di2] as char); di2 += 1 }
281354
if(sdv_send_raw(ssl_mem, resp) < 0) { env.error("chunkup: response write failed"); return false }
282355
return true
283356
}

0 commit comments

Comments
 (0)