mirror of
https://github.com/ziglang/zig.git
synced 2026-01-02 03:25:01 +00:00
Revert "std.http: remove 'done' flag"
This reverts commit 42be972a72c86b32ad8403d082ab42763c6facec. Using a bit to distinguish between headers and trailers is fine. It was just named and documented poorly.
This commit is contained in:
parent
3d61890d24
commit
d574875f00
@ -649,7 +649,7 @@ pub const Request = struct {
|
||||
/// Frees all resources associated with the request.
|
||||
pub fn deinit(req: *Request) void {
|
||||
if (req.connection) |connection| {
|
||||
if (req.response.parser.state != .complete) {
|
||||
if (!req.response.parser.done) {
|
||||
// If the response wasn't fully read, then we need to close the connection.
|
||||
connection.closing = true;
|
||||
}
|
||||
@ -664,7 +664,7 @@ pub const Request = struct {
|
||||
// or keep those which will be used.
|
||||
// This needs to be kept in sync with deinit and request.
|
||||
fn redirect(req: *Request, uri: Uri) !void {
|
||||
assert(req.response.parser.state == .complete);
|
||||
assert(req.response.parser.done);
|
||||
|
||||
req.client.connection_pool.release(req.client.allocator, req.connection.?);
|
||||
req.connection = null;
|
||||
@ -823,12 +823,12 @@ pub const Request = struct {
|
||||
}
|
||||
|
||||
fn transferRead(req: *Request, buf: []u8) TransferReadError!usize {
|
||||
if (req.response.parser.state == .complete) return 0;
|
||||
if (req.response.parser.done) return 0;
|
||||
|
||||
var index: usize = 0;
|
||||
while (index == 0) {
|
||||
const amt = try req.response.parser.read(req.connection.?, buf[index..], req.response.skip);
|
||||
if (amt == 0 and req.response.parser.state == .complete) break;
|
||||
if (amt == 0 and req.response.parser.done) break;
|
||||
index += amt;
|
||||
}
|
||||
|
||||
@ -873,7 +873,7 @@ pub const Request = struct {
|
||||
if (req.response.status == .@"continue") {
|
||||
// We're done parsing the continue response; reset to prepare
|
||||
// for the real response.
|
||||
req.response.parser.state = .complete;
|
||||
req.response.parser.done = true;
|
||||
req.response.parser.reset();
|
||||
|
||||
if (req.handle_continue)
|
||||
@ -885,7 +885,7 @@ pub const Request = struct {
|
||||
// we're switching protocols, so this connection is no longer doing http
|
||||
if (req.method == .CONNECT and req.response.status.class() == .success) {
|
||||
connection.closing = false;
|
||||
req.response.parser.state = .complete;
|
||||
req.response.parser.done = true;
|
||||
return; // the connection is not HTTP past this point
|
||||
}
|
||||
|
||||
@ -899,7 +899,7 @@ pub const Request = struct {
|
||||
if (req.method == .HEAD or req.response.status.class() == .informational or
|
||||
req.response.status == .no_content or req.response.status == .not_modified)
|
||||
{
|
||||
req.response.parser.state = .complete;
|
||||
req.response.parser.done = true;
|
||||
return; // The response is empty; no further setup or redirection is necessary.
|
||||
}
|
||||
|
||||
@ -914,7 +914,7 @@ pub const Request = struct {
|
||||
} else if (req.response.content_length) |cl| {
|
||||
req.response.parser.next_chunk_length = cl;
|
||||
|
||||
if (cl == 0) req.response.parser.state = .complete;
|
||||
if (cl == 0) req.response.parser.done = true;
|
||||
} else {
|
||||
// read until the connection is closed
|
||||
req.response.parser.next_chunk_length = std.math.maxInt(u64);
|
||||
@ -973,7 +973,7 @@ pub const Request = struct {
|
||||
try req.send(.{});
|
||||
} else {
|
||||
req.response.skip = false;
|
||||
if (req.response.parser.state != .complete) {
|
||||
if (!req.response.parser.done) {
|
||||
switch (req.response.transfer_compression) {
|
||||
.identity => req.response.compression = .none,
|
||||
.compress, .@"x-compress" => return error.CompressionUnsupported,
|
||||
|
||||
@ -352,7 +352,7 @@ pub const Response = struct {
|
||||
return .reset;
|
||||
}
|
||||
|
||||
if (res.request.parser.state != .complete) {
|
||||
if (!res.request.parser.done) {
|
||||
// If the response wasn't fully read, then we need to close the connection.
|
||||
res.connection.closing = true;
|
||||
return .closing;
|
||||
@ -447,12 +447,12 @@ pub const Response = struct {
|
||||
}
|
||||
|
||||
fn transferRead(res: *Response, buf: []u8) TransferReadError!usize {
|
||||
if (res.request.parser.state == .complete) return 0;
|
||||
if (res.request.parser.done) return 0;
|
||||
|
||||
var index: usize = 0;
|
||||
while (index == 0) {
|
||||
const amt = try res.request.parser.read(&res.connection, buf[index..], false);
|
||||
if (amt == 0 and res.request.parser.state == .complete) break;
|
||||
if (amt == 0 and res.request.parser.done) break;
|
||||
index += amt;
|
||||
}
|
||||
|
||||
@ -502,9 +502,9 @@ pub const Response = struct {
|
||||
if (res.request.content_length) |len| {
|
||||
res.request.parser.next_chunk_length = len;
|
||||
|
||||
if (len == 0) res.request.parser.state = .complete;
|
||||
if (len == 0) res.request.parser.done = true;
|
||||
} else {
|
||||
res.request.parser.state = .complete;
|
||||
res.request.parser.done = true;
|
||||
}
|
||||
},
|
||||
.chunked => {
|
||||
@ -513,7 +513,7 @@ pub const Response = struct {
|
||||
},
|
||||
}
|
||||
|
||||
if (res.request.parser.state != .complete) {
|
||||
if (!res.request.parser.done) {
|
||||
switch (res.request.transfer_compression) {
|
||||
.identity => res.request.compression = .none,
|
||||
.compress, .@"x-compress" => return error.CompressionUnsupported,
|
||||
|
||||
@ -7,76 +7,64 @@ const assert = std.debug.assert;
|
||||
const use_vectors = builtin.zig_backend != .stage2_x86_64;
|
||||
|
||||
pub const State = enum {
|
||||
/// Begin header parsing states.
|
||||
invalid,
|
||||
|
||||
// Begin header and trailer parsing states.
|
||||
|
||||
start,
|
||||
seen_n,
|
||||
seen_r,
|
||||
seen_rn,
|
||||
seen_rnr,
|
||||
headers_end,
|
||||
/// Begin transfer-encoding: chunked parsing states.
|
||||
finished,
|
||||
|
||||
// Begin transfer-encoding: chunked parsing states.
|
||||
|
||||
chunk_head_size,
|
||||
chunk_head_ext,
|
||||
chunk_head_r,
|
||||
chunk_data,
|
||||
chunk_data_suffix,
|
||||
chunk_data_suffix_r,
|
||||
/// When the parser has finished parsing a complete message. A message is
|
||||
/// only complete after the entire body has been read and any trailing
|
||||
/// headers have been parsed.
|
||||
complete,
|
||||
|
||||
/// Returns true if the parser is in a content state (ie. not waiting for more headers).
|
||||
pub fn isContent(self: State) bool {
|
||||
return switch (self) {
|
||||
.invalid,
|
||||
.start,
|
||||
.seen_n,
|
||||
.seen_r,
|
||||
.seen_rn,
|
||||
.seen_rnr,
|
||||
=> false,
|
||||
|
||||
.headers_end,
|
||||
.chunk_head_size,
|
||||
.chunk_head_ext,
|
||||
.chunk_head_r,
|
||||
.chunk_data,
|
||||
.chunk_data_suffix,
|
||||
.chunk_data_suffix_r,
|
||||
.complete,
|
||||
=> true,
|
||||
.invalid, .start, .seen_n, .seen_r, .seen_rn, .seen_rnr => false,
|
||||
.finished, .chunk_head_size, .chunk_head_ext, .chunk_head_r, .chunk_data, .chunk_data_suffix, .chunk_data_suffix_r => true,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const HeadersParser = struct {
|
||||
state: State,
|
||||
state: State = .start,
|
||||
/// A fixed buffer of len `max_header_bytes`.
|
||||
/// Pointers into this buffer are not stable until after a message is complete.
|
||||
header_bytes_buffer: []u8,
|
||||
header_bytes_len: u32,
|
||||
next_chunk_length: u64,
|
||||
/// `false`: headers. `true`: trailers.
|
||||
done: bool,
|
||||
|
||||
/// Initializes the parser with a provided buffer `buf`.
|
||||
pub fn init(buf: []u8) HeadersParser {
|
||||
return .{
|
||||
.state = .start,
|
||||
.header_bytes_buffer = buf,
|
||||
.header_bytes_len = 0,
|
||||
.done = false,
|
||||
.next_chunk_length = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Reinitialize the parser.
|
||||
/// Asserts the parser is in the `complete` state.
|
||||
/// Asserts the parser is in the "done" state.
|
||||
pub fn reset(hp: *HeadersParser) void {
|
||||
assert(hp.state == .complete);
|
||||
assert(hp.done);
|
||||
hp.* = .{
|
||||
.state = .start,
|
||||
.header_bytes_buffer = hp.header_bytes_buffer,
|
||||
.header_bytes_len = 0,
|
||||
.done = false,
|
||||
.next_chunk_length = 0,
|
||||
};
|
||||
}
|
||||
@ -101,8 +89,7 @@ pub const HeadersParser = struct {
|
||||
while (true) {
|
||||
switch (r.state) {
|
||||
.invalid => unreachable,
|
||||
.complete => unreachable,
|
||||
.headers_end => return index,
|
||||
.finished => return index,
|
||||
.start => switch (len - index) {
|
||||
0 => return index,
|
||||
1 => {
|
||||
@ -126,7 +113,7 @@ pub const HeadersParser = struct {
|
||||
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .seen_rn,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -145,7 +132,7 @@ pub const HeadersParser = struct {
|
||||
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .seen_rn,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -170,7 +157,7 @@ pub const HeadersParser = struct {
|
||||
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .seen_rn,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -180,7 +167,7 @@ pub const HeadersParser = struct {
|
||||
}
|
||||
|
||||
switch (b32) {
|
||||
int32("\r\n\r\n") => r.state = .headers_end,
|
||||
int32("\r\n\r\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -228,7 +215,7 @@ pub const HeadersParser = struct {
|
||||
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .seen_rn,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
@ -245,7 +232,7 @@ pub const HeadersParser = struct {
|
||||
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .seen_rn,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -262,10 +249,10 @@ pub const HeadersParser = struct {
|
||||
const b16 = intShift(u16, b32);
|
||||
|
||||
if (b32 == int32("\r\n\r\n")) {
|
||||
r.state = .headers_end;
|
||||
r.state = .finished;
|
||||
return index + i + 4;
|
||||
} else if (b16 == int16("\n\n")) {
|
||||
r.state = .headers_end;
|
||||
r.state = .finished;
|
||||
return index + i + 2;
|
||||
}
|
||||
}
|
||||
@ -282,7 +269,7 @@ pub const HeadersParser = struct {
|
||||
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .seen_rn,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -302,7 +289,7 @@ pub const HeadersParser = struct {
|
||||
0 => return index,
|
||||
else => {
|
||||
switch (bytes[index]) {
|
||||
'\n' => r.state = .headers_end,
|
||||
'\n' => r.state = .finished,
|
||||
else => r.state = .start,
|
||||
}
|
||||
|
||||
@ -334,7 +321,7 @@ pub const HeadersParser = struct {
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .seen_rn,
|
||||
int16("\n\r") => r.state = .seen_rnr,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -353,12 +340,12 @@ pub const HeadersParser = struct {
|
||||
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .seen_rn,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
switch (b24) {
|
||||
int24("\n\r\n") => r.state = .headers_end,
|
||||
int24("\n\r\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -388,8 +375,8 @@ pub const HeadersParser = struct {
|
||||
}
|
||||
|
||||
switch (b16) {
|
||||
int16("\r\n") => r.state = .headers_end,
|
||||
int16("\n\n") => r.state = .headers_end,
|
||||
int16("\r\n") => r.state = .finished,
|
||||
int16("\n\n") => r.state = .finished,
|
||||
else => {},
|
||||
}
|
||||
|
||||
@ -401,7 +388,7 @@ pub const HeadersParser = struct {
|
||||
0 => return index,
|
||||
else => {
|
||||
switch (bytes[index]) {
|
||||
'\n' => r.state = .headers_end,
|
||||
'\n' => r.state = .finished,
|
||||
else => r.state = .start,
|
||||
}
|
||||
|
||||
@ -502,6 +489,13 @@ pub const HeadersParser = struct {
|
||||
return len;
|
||||
}
|
||||
|
||||
/// Returns whether or not the parser has finished parsing a complete
|
||||
/// message. A message is only complete after the entire body has been read
|
||||
/// and any trailing headers have been parsed.
|
||||
pub fn isComplete(r: *HeadersParser) bool {
|
||||
return r.done and r.state == .finished;
|
||||
}
|
||||
|
||||
pub const CheckCompleteHeadError = error{HttpHeadersOversize};
|
||||
|
||||
/// Pushes `in` into the parser. Returns the number of bytes consumed by
|
||||
@ -532,12 +526,13 @@ pub const HeadersParser = struct {
|
||||
/// See `std.http.Client.Connection for an example of `conn`.
|
||||
pub fn read(r: *HeadersParser, conn: anytype, buffer: []u8, skip: bool) !usize {
|
||||
assert(r.state.isContent());
|
||||
if (r.done) return 0;
|
||||
|
||||
var out_index: usize = 0;
|
||||
while (true) {
|
||||
switch (r.state) {
|
||||
.complete => return out_index,
|
||||
.invalid, .start, .seen_n, .seen_r, .seen_rn, .seen_rnr => unreachable,
|
||||
.headers_end => {
|
||||
.finished => {
|
||||
const data_avail = r.next_chunk_length;
|
||||
|
||||
if (skip) {
|
||||
@ -547,8 +542,7 @@ pub const HeadersParser = struct {
|
||||
conn.drop(@intCast(nread));
|
||||
r.next_chunk_length -= nread;
|
||||
|
||||
if (r.next_chunk_length == 0 or nread == 0)
|
||||
r.state = .complete;
|
||||
if (r.next_chunk_length == 0 or nread == 0) r.done = true;
|
||||
|
||||
return out_index;
|
||||
} else if (out_index < buffer.len) {
|
||||
@ -558,8 +552,7 @@ pub const HeadersParser = struct {
|
||||
const nread = try conn.read(buffer[0..can_read]);
|
||||
r.next_chunk_length -= nread;
|
||||
|
||||
if (r.next_chunk_length == 0 or nread == 0)
|
||||
r.state = .complete;
|
||||
if (r.next_chunk_length == 0 or nread == 0) r.done = true;
|
||||
|
||||
return nread;
|
||||
} else {
|
||||
@ -576,12 +569,14 @@ pub const HeadersParser = struct {
|
||||
.invalid => return error.HttpChunkInvalid,
|
||||
.chunk_data => if (r.next_chunk_length == 0) {
|
||||
if (std.mem.eql(u8, conn.peek(), "\r\n")) {
|
||||
r.state = .complete;
|
||||
r.state = .finished;
|
||||
r.done = true;
|
||||
} else {
|
||||
// The trailer section is formatted identically
|
||||
// to the header section.
|
||||
// The trailer section is formatted identically to the header section.
|
||||
r.state = .seen_rn;
|
||||
}
|
||||
r.done = true;
|
||||
|
||||
return out_index;
|
||||
},
|
||||
else => return out_index,
|
||||
@ -619,21 +614,21 @@ pub const HeadersParser = struct {
|
||||
};
|
||||
|
||||
inline fn int16(array: *const [2]u8) u16 {
|
||||
return @bitCast(array.*);
|
||||
return @as(u16, @bitCast(array.*));
|
||||
}
|
||||
|
||||
inline fn int24(array: *const [3]u8) u24 {
|
||||
return @bitCast(array.*);
|
||||
return @as(u24, @bitCast(array.*));
|
||||
}
|
||||
|
||||
inline fn int32(array: *const [4]u8) u32 {
|
||||
return @bitCast(array.*);
|
||||
return @as(u32, @bitCast(array.*));
|
||||
}
|
||||
|
||||
inline fn intShift(comptime T: type, x: anytype) T {
|
||||
switch (@import("builtin").cpu.arch.endian()) {
|
||||
.little => return @truncate(x >> (@bitSizeOf(@TypeOf(x)) - @bitSizeOf(T))),
|
||||
.big => return @truncate(x),
|
||||
.little => return @as(T, @truncate(x >> (@bitSizeOf(@TypeOf(x)) - @bitSizeOf(T)))),
|
||||
.big => return @as(T, @truncate(x)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -673,7 +673,7 @@ pub fn main() !void {
|
||||
var req = try client.open(.GET, uri, .{
|
||||
.server_header_buffer = headers_buf,
|
||||
});
|
||||
req.response.parser.state = .complete;
|
||||
req.response.parser.done = true;
|
||||
req.connection.?.closing = false;
|
||||
requests[i] = req;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user