aboutsummaryrefslogtreecommitdiff
path: root/lib/std/http/Client.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-01-31 21:03:40 -0800
committerGitHub <noreply@github.com>2024-01-31 21:03:40 -0800
commit776cd673f206099012d789fd5d05d49dd72b9faa (patch)
tree6f8f852fffbcb55724aa1a123d602472a2d855c8 /lib/std/http/Client.zig
parent788a0409af15d5823e0e96652ffb71458f78f820 (diff)
parentc1e7d0c08f96f390a641b082b33a8a8717cdd706 (diff)
downloadzig-776cd673f206099012d789fd5d05d49dd72b9faa.tar.gz
zig-776cd673f206099012d789fd5d05d49dd72b9faa.zip
Merge pull request #18746 from jacobly0/http-auth
http: support basic access authentication
Diffstat (limited to 'lib/std/http/Client.zig')
-rw-r--r--lib/std/http/Client.zig86
1 files changed, 61 insertions, 25 deletions
diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig
index 8438a7bbd3..2c45b36173 100644
--- a/lib/std/http/Client.zig
+++ b/lib/std/http/Client.zig
@@ -339,7 +339,7 @@ pub const Connection = struct {
/// Writes the given buffer to the connection.
pub fn write(conn: *Connection, buffer: []const u8) WriteError!usize {
- if (conn.write_end + buffer.len > conn.write_buf.len) {
+ if (conn.write_buf.len - conn.write_end < buffer.len) {
try conn.flush();
if (buffer.len > conn.write_buf.len) {
@@ -354,6 +354,13 @@ pub const Connection = struct {
return buffer.len;
}
+ /// Returns a buffer to be filled with exactly len bytes to write to the connection.
+ pub fn allocWriteBuffer(conn: *Connection, len: BufferSize) WriteError![]u8 {
+ if (conn.write_buf.len - conn.write_end < len) try conn.flush();
+ defer conn.write_end += len;
+ return conn.write_buf[conn.write_end..][0..len];
+ }
+
/// Flushes the write buffer to the connection.
pub fn flush(conn: *Connection) WriteError!void {
if (conn.write_end == 0) return;
@@ -695,6 +702,17 @@ pub const Request = struct {
try w.writeAll("\r\n");
}
+ if ((req.uri.user != null or req.uri.password != null) and
+ !req.headers.contains("authorization"))
+ {
+ try w.writeAll("Authorization: ");
+ const authorization = try req.connection.?.allocWriteBuffer(
+ @intCast(basic_authorization.valueLengthFromUri(req.uri)),
+ );
+ std.debug.assert(basic_authorization.value(req.uri, authorization).len == authorization.len);
+ try w.writeAll("\r\n");
+ }
+
if (!req.headers.contains("user-agent")) {
try w.writeAll("User-Agent: zig/");
try w.writeAll(builtin.zig_version_string);
@@ -1122,19 +1140,11 @@ pub fn loadDefaultProxies(client: *Client) !void {
},
};
- if (uri.user != null and uri.password != null) {
- const prefix = "Basic ";
-
- const unencoded = try std.fmt.allocPrint(client.allocator, "{s}:{s}", .{ uri.user.?, uri.password.? });
- defer client.allocator.free(unencoded);
-
- const buffer = try client.allocator.alloc(u8, std.base64.standard.Encoder.calcSize(unencoded.len) + prefix.len);
- defer client.allocator.free(buffer);
-
- const result = std.base64.standard.Encoder.encode(buffer[prefix.len..], unencoded);
- @memcpy(buffer[0..prefix.len], prefix);
-
- try client.http_proxy.?.headers.append("proxy-authorization", result);
+ if (uri.user != null or uri.password != null) {
+ const authorization = try client.allocator.alloc(u8, basic_authorization.valueLengthFromUri(uri));
+ errdefer client.allocator.free(authorization);
+ std.debug.assert(basic_authorization.value(uri, authorization).len == authorization.len);
+ try client.http_proxy.?.headers.appendOwned(.{ .unowned = "proxy-authorization" }, .{ .owned = authorization });
}
}
@@ -1173,22 +1183,48 @@ pub fn loadDefaultProxies(client: *Client) !void {
},
};
- if (uri.user != null and uri.password != null) {
- const prefix = "Basic ";
+ if (uri.user != null or uri.password != null) {
+ const authorization = try client.allocator.alloc(u8, basic_authorization.valueLengthFromUri(uri));
+ errdefer client.allocator.free(authorization);
+ std.debug.assert(basic_authorization.value(uri, authorization).len == authorization.len);
+ try client.https_proxy.?.headers.appendOwned(.{ .unowned = "proxy-authorization" }, .{ .owned = authorization });
+ }
+ }
+}
- const unencoded = try std.fmt.allocPrint(client.allocator, "{s}:{s}", .{ uri.user.?, uri.password.? });
- defer client.allocator.free(unencoded);
+pub const basic_authorization = struct {
+ pub const max_user_len = 255;
+ pub const max_password_len = 255;
+ pub const max_value_len = valueLength(max_user_len, max_password_len);
- const buffer = try client.allocator.alloc(u8, std.base64.standard.Encoder.calcSize(unencoded.len) + prefix.len);
- defer client.allocator.free(buffer);
+ const prefix = "Basic ";
- const result = std.base64.standard.Encoder.encode(buffer[prefix.len..], unencoded);
- @memcpy(buffer[0..prefix.len], prefix);
+ pub fn valueLength(user_len: usize, password_len: usize) usize {
+ return prefix.len + std.base64.standard.Encoder.calcSize(user_len + 1 + password_len);
+ }
- try client.https_proxy.?.headers.append("proxy-authorization", result);
- }
+ pub fn valueLengthFromUri(uri: Uri) usize {
+ return valueLength(
+ if (uri.user) |user| user.len else 0,
+ if (uri.password) |password| password.len else 0,
+ );
}
-}
+
+ pub fn value(uri: Uri, out: []u8) []u8 {
+ std.debug.assert(uri.user == null or uri.user.?.len <= max_user_len);
+ std.debug.assert(uri.password == null or uri.password.?.len <= max_password_len);
+
+ @memcpy(out[0..prefix.len], prefix);
+
+ var buf: [max_user_len + ":".len + max_password_len]u8 = undefined;
+ const unencoded = std.fmt.bufPrint(&buf, "{s}:{s}", .{
+ uri.user orelse "", uri.password orelse "",
+ }) catch unreachable;
+ const base64 = std.base64.standard.Encoder.encode(out[prefix.len..], unencoded);
+
+ return out[0 .. prefix.len + base64.len];
+ }
+};
pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, NetworkUnreachable, ConnectionTimedOut, ConnectionResetByPeer, TemporaryNameServerFailure, NameServerFailure, UnknownHostName, HostLacksNetworkAddresses, UnexpectedConnectFailure, TlsInitializationFailed };