diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-01-31 21:03:40 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-31 21:03:40 -0800 |
| commit | 776cd673f206099012d789fd5d05d49dd72b9faa (patch) | |
| tree | 6f8f852fffbcb55724aa1a123d602472a2d855c8 /lib/std/http/Client.zig | |
| parent | 788a0409af15d5823e0e96652ffb71458f78f820 (diff) | |
| parent | c1e7d0c08f96f390a641b082b33a8a8717cdd706 (diff) | |
| download | zig-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.zig | 86 |
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 }; |
