mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
std.http.Client: fix handling of \r\n before next chunk size
This commit is contained in:
parent
24b4e643f4
commit
3806091a10
@ -1,7 +1,3 @@
|
||||
//! This API is a barely-touched, barely-functional http client, just the
|
||||
//! absolute minimum thing I needed in order to test `std.crypto.tls`. Bear
|
||||
//! with me and I promise the API will become useful and streamlined.
|
||||
//!
|
||||
//! TODO: send connection: keep-alive and LRU cache a configurable number of
|
||||
//! open connections to skip DNS and TLS handshake for subsequent requests.
|
||||
|
||||
@ -178,6 +174,8 @@ pub const Request = struct {
|
||||
seen_rnr,
|
||||
finished,
|
||||
/// Begin transfer-encoding: chunked parsing states.
|
||||
chunk_size_prefix_r,
|
||||
chunk_size_prefix_n,
|
||||
chunk_size,
|
||||
chunk_r,
|
||||
chunk_data,
|
||||
@ -382,6 +380,8 @@ pub const Request = struct {
|
||||
continue :state;
|
||||
},
|
||||
},
|
||||
.chunk_size_prefix_r => unreachable,
|
||||
.chunk_size_prefix_n => unreachable,
|
||||
.chunk_size => unreachable,
|
||||
.chunk_r => unreachable,
|
||||
.chunk_data => unreachable,
|
||||
@ -449,18 +449,6 @@ pub const Request = struct {
|
||||
try expectEqual(@as(u10, 999), parseInt3("999".*));
|
||||
}
|
||||
|
||||
inline fn int16(array: *const [2]u8) u16 {
|
||||
return @bitCast(u16, array.*);
|
||||
}
|
||||
|
||||
inline fn int32(array: *const [4]u8) u32 {
|
||||
return @bitCast(u32, array.*);
|
||||
}
|
||||
|
||||
inline fn int64(array: *const [8]u8) u64 {
|
||||
return @bitCast(u64, array.*);
|
||||
}
|
||||
|
||||
test "find headers end basic" {
|
||||
var buffer: [1]u8 = undefined;
|
||||
var r = Response.initStatic(&buffer);
|
||||
@ -480,6 +468,29 @@ pub const Request = struct {
|
||||
"\r\ncontent";
|
||||
try testing.expectEqual(@as(usize, 131), r.findHeadersEnd(example));
|
||||
}
|
||||
|
||||
test "find headers end bug" {
|
||||
var buffer: [1]u8 = undefined;
|
||||
var r = Response.initStatic(&buffer);
|
||||
const trail = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
|
||||
const example =
|
||||
"HTTP/1.1 200 OK\r\n" ++
|
||||
"Access-Control-Allow-Origin: https://render.githubusercontent.com\r\n" ++
|
||||
"content-disposition: attachment; filename=zig-0.10.0.tar.gz\r\n" ++
|
||||
"Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox\r\n" ++
|
||||
"Content-Type: application/x-gzip\r\n" ++
|
||||
"ETag: \"bfae0af6b01c7c0d89eb667cb5f0e65265968aeebda2689177e6b26acd3155ca\"\r\n" ++
|
||||
"Strict-Transport-Security: max-age=31536000\r\n" ++
|
||||
"Vary: Authorization,Accept-Encoding,Origin\r\n" ++
|
||||
"X-Content-Type-Options: nosniff\r\n" ++
|
||||
"X-Frame-Options: deny\r\n" ++
|
||||
"X-XSS-Protection: 1; mode=block\r\n" ++
|
||||
"Date: Fri, 06 Jan 2023 22:26:22 GMT\r\n" ++
|
||||
"Transfer-Encoding: chunked\r\n" ++
|
||||
"X-GitHub-Request-Id: 89C6:17E9:A7C9E:124B51:63B8A00E\r\n" ++
|
||||
"connection: close\r\n\r\n" ++ trail;
|
||||
try testing.expectEqual(@as(usize, example.len - trail.len), r.findHeadersEnd(example));
|
||||
}
|
||||
};
|
||||
|
||||
pub const Headers = struct {
|
||||
@ -536,8 +547,7 @@ pub const Request = struct {
|
||||
/// This one can return 0 without meaning EOF.
|
||||
/// TODO change to readvAdvanced
|
||||
pub fn readAdvanced(req: *Request, buffer: []u8) !usize {
|
||||
const amt = try req.connection.read(buffer);
|
||||
var in = buffer[0..amt];
|
||||
var in = buffer[0..try req.connection.read(buffer)];
|
||||
var out_index: usize = 0;
|
||||
while (true) {
|
||||
switch (req.response.state) {
|
||||
@ -571,7 +581,8 @@ pub const Request = struct {
|
||||
req.deinit();
|
||||
req.* = new_req;
|
||||
assert(out_index == 0);
|
||||
return readAdvanced(req, buffer);
|
||||
in = buffer[0..try req.connection.read(buffer)];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (req.response.headers.transfer_encoding) |transfer_encoding| {
|
||||
@ -598,8 +609,50 @@ pub const Request = struct {
|
||||
return 0;
|
||||
},
|
||||
.finished => {
|
||||
mem.copy(u8, buffer[out_index..], in);
|
||||
return out_index + in.len;
|
||||
if (in.ptr == buffer.ptr) {
|
||||
return in.len;
|
||||
} else {
|
||||
mem.copy(u8, buffer[out_index..], in);
|
||||
return out_index + in.len;
|
||||
}
|
||||
},
|
||||
.chunk_size_prefix_r => switch (in.len) {
|
||||
0 => return out_index,
|
||||
1 => switch (in[0]) {
|
||||
'\r' => {
|
||||
req.response.state = .chunk_size_prefix_n;
|
||||
return out_index;
|
||||
},
|
||||
else => {
|
||||
req.response.state = .invalid;
|
||||
return error.HttpHeadersInvalid;
|
||||
},
|
||||
},
|
||||
else => switch (int16(in[0..2])) {
|
||||
int16("\r\n") => {
|
||||
in = in[2..];
|
||||
req.response.state = .chunk_size;
|
||||
continue;
|
||||
},
|
||||
else => {
|
||||
req.response.state = .invalid;
|
||||
return error.HttpHeadersInvalid;
|
||||
},
|
||||
},
|
||||
},
|
||||
.chunk_size_prefix_n => switch (in.len) {
|
||||
0 => return out_index,
|
||||
else => switch (in[0]) {
|
||||
'\n' => {
|
||||
in = in[1..];
|
||||
req.response.state = .chunk_size;
|
||||
continue;
|
||||
},
|
||||
else => {
|
||||
req.response.state = .invalid;
|
||||
return error.HttpHeadersInvalid;
|
||||
},
|
||||
},
|
||||
},
|
||||
.chunk_size, .chunk_r => {
|
||||
const i = req.response.findChunkedLen(in);
|
||||
@ -619,20 +672,38 @@ pub const Request = struct {
|
||||
},
|
||||
.chunk_data => {
|
||||
const sub_amt = @min(req.response.next_chunk_length, in.len);
|
||||
req.response.next_chunk_length -= sub_amt;
|
||||
if (req.response.next_chunk_length > 0) {
|
||||
if (in.ptr == buffer.ptr) {
|
||||
return sub_amt;
|
||||
} else {
|
||||
mem.copy(u8, buffer[out_index..], in[0..sub_amt]);
|
||||
out_index += sub_amt;
|
||||
return out_index;
|
||||
}
|
||||
}
|
||||
mem.copy(u8, buffer[out_index..], in[0..sub_amt]);
|
||||
out_index += sub_amt;
|
||||
req.response.next_chunk_length -= sub_amt;
|
||||
if (req.response.next_chunk_length == 0) {
|
||||
req.response.state = .chunk_size;
|
||||
in = in[sub_amt..];
|
||||
continue;
|
||||
}
|
||||
return out_index;
|
||||
req.response.state = .chunk_size_prefix_r;
|
||||
in = in[sub_amt..];
|
||||
continue;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fn int16(array: *const [2]u8) u16 {
|
||||
return @bitCast(u16, array.*);
|
||||
}
|
||||
|
||||
inline fn int32(array: *const [4]u8) u32 {
|
||||
return @bitCast(u32, array.*);
|
||||
}
|
||||
|
||||
inline fn int64(array: *const [8]u8) u64 {
|
||||
return @bitCast(u64, array.*);
|
||||
}
|
||||
|
||||
test {
|
||||
_ = Response;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user