authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-05 13:39:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-05 13:39:17-07:00
log3055ab7f8639deca318f238f21680776a7149acb
treeda69d3846019d24f3a41b3a77898fd39eb55097e
parent450f3bc9250eca86b470ec29c228165054016c76

std.http.Client: fail header parsing under more conditions

* when HTTP header continuations are used * when content-type or location header occurs more than once

1 files changed, 32 insertions(+), 2 deletions(-)

lib/std/http/Client.zig+32-2
......@@ -96,7 +96,7 @@ pub const Request = struct {
9696 int64("HTTP/1.1") => .@"HTTP/1.1",
9797 else => return error.BadHttpVersion,
9898 };
99 if (first_line[8] != ' ') return error.InvalidHttpHeaders;
99 if (first_line[8] != ' ') return error.HttpHeadersInvalid;
100100 const status = @intToEnum(http.Status, parseInt3(first_line[9..12].*));
101101
102102 var headers: Response.Headers = .{
......@@ -105,12 +105,19 @@ pub const Request = struct {
105105 };
106106
107107 while (it.next()) |line| {
108 if (line.len == 0) return error.HttpHeadersInvalid;
109 switch (line[0]) {
110 ' ', '\t' => return error.HttpHeaderContinuationsUnsupported,
111 else => {},
112 }
108113 var line_it = mem.split(u8, line, ": ");
109114 const header_name = line_it.first();
110115 const header_value = line_it.rest();
111116 if (std.ascii.eqlIgnoreCase(header_name, "location")) {
117 if (headers.location != null) return error.HttpHeadersInvalid;
112118 headers.location = header_value;
113119 } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) {
120 if (headers.content_length != null) return error.HttpHeadersInvalid;
114121 headers.content_length = try std.fmt.parseInt(u64, header_value, 10);
115122 }
116123 }
......@@ -131,6 +138,29 @@ pub const Request = struct {
131138 return error.TestFailed);
132139 try testing.expectEqual(@as(?u64, 220), parsed.content_length);
133140 }
141
142 test "header continuation" {
143 const example =
144 "HTTP/1.0 200 OK\r\n" ++
145 "Content-Type: text/html;\r\n charset=UTF-8\r\n" ++
146 "Content-Length: 220\r\n\r\n";
147 try testing.expectError(
148 error.HttpHeaderContinuationsUnsupported,
149 Response.Headers.parse(example),
150 );
151 }
152
153 test "extra content length" {
154 const example =
155 "HTTP/1.0 200 OK\r\n" ++
156 "Content-Length: 220\r\n" ++
157 "Content-Type: text/html; charset=UTF-8\r\n" ++
158 "content-length: 220\r\n\r\n";
159 try testing.expectError(
160 error.HttpHeadersInvalid,
161 Response.Headers.parse(example),
162 );
163 }
134164 };
135165
136166 pub const State = enum {
......@@ -442,7 +472,7 @@ pub const Request = struct {
442472 const amt = try req.connection.read(buffer);
443473 const data = buffer[0..amt];
444474 const i = req.response.findHeadersEnd(data);
445 if (req.response.state == .invalid) return error.InvalidHttpHeaders;
475 if (req.response.state == .invalid) return error.HttpHeadersInvalid;
446476
447477 const headers_data = data[0..i];
448478 if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) {