| ... | @@ -339,7 +339,7 @@ pub const Connection = struct { | ... | @@ -339,7 +339,7 @@ pub const Connection = struct { |
| 339 | | 339 | |
| 340 | /// Writes the given buffer to the connection. | 340 | /// Writes the given buffer to the connection. |
| 341 | pub fn write(conn: *Connection, buffer: []const u8) WriteError!usize { | 341 | pub fn write(conn: *Connection, buffer: []const u8) WriteError!usize { |
| 342 | if (conn.write_end + buffer.len > conn.write_buf.len) { | 342 | if (conn.write_buf.len - conn.write_end < buffer.len) { |
| 343 | try conn.flush(); | 343 | try conn.flush(); |
| 344 | | 344 | |
| 345 | if (buffer.len > conn.write_buf.len) { | 345 | if (buffer.len > conn.write_buf.len) { |
| ... | @@ -354,6 +354,13 @@ pub const Connection = struct { | ... | @@ -354,6 +354,13 @@ pub const Connection = struct { |
| 354 | return buffer.len; | 354 | return buffer.len; |
| 355 | } | 355 | } |
| 356 | | 356 | |
| | 357 | /// Returns a buffer to be filled with exactly len bytes to write to the connection. |
| | 358 | pub fn allocWriteBuffer(conn: *Connection, len: BufferSize) WriteError![]u8 { |
| | 359 | if (conn.write_buf.len - conn.write_end < len) try conn.flush(); |
| | 360 | defer conn.write_end += len; |
| | 361 | return conn.write_buf[conn.write_end..][0..len]; |
| | 362 | } |
| | 363 | |
| 357 | /// Flushes the write buffer to the connection. | 364 | /// Flushes the write buffer to the connection. |
| 358 | pub fn flush(conn: *Connection) WriteError!void { | 365 | pub fn flush(conn: *Connection) WriteError!void { |
| 359 | if (conn.write_end == 0) return; | 366 | if (conn.write_end == 0) return; |
| ... | @@ -657,7 +664,7 @@ pub const Request = struct { | ... | @@ -657,7 +664,7 @@ pub const Request = struct { |
| 657 | }; | 664 | }; |
| 658 | } | 665 | } |
| 659 | | 666 | |
| 660 | pub const SendError = Allocator.Error || Connection.WriteError || error{ InvalidContentLength, UnsupportedTransferEncoding }; | 667 | pub const SendError = Connection.WriteError || error{ InvalidContentLength, UnsupportedTransferEncoding }; |
| 661 | | 668 | |
| 662 | pub const SendOptions = struct { | 669 | pub const SendOptions = struct { |
| 663 | /// Specifies that the uri should be used as is. You guarantee that the uri is already escaped. | 670 | /// Specifies that the uri should be used as is. You guarantee that the uri is already escaped. |
| ... | @@ -699,7 +706,10 @@ pub const Request = struct { | ... | @@ -699,7 +706,10 @@ pub const Request = struct { |
| 699 | !req.headers.contains("authorization")) | 706 | !req.headers.contains("authorization")) |
| 700 | { | 707 | { |
| 701 | try w.writeAll("Authorization: "); | 708 | try w.writeAll("Authorization: "); |
| 702 | try w.writeAll(try basicAuthorizationValue(req.arena.allocator(), req.uri)); | 709 | const authorization = try req.connection.?.allocWriteBuffer( |
| | 710 | @intCast(basic_authorization.valueLengthFromUri(req.uri)), |
| | 711 | ); |
| | 712 | std.debug.assert(basic_authorization.value(req.uri, authorization).len == authorization.len); |
| 703 | try w.writeAll("\r\n"); | 713 | try w.writeAll("\r\n"); |
| 704 | } | 714 | } |
| 705 | | 715 | |
| ... | @@ -1131,10 +1141,8 @@ pub fn loadDefaultProxies(client: *Client) !void { | ... | @@ -1131,10 +1141,8 @@ pub fn loadDefaultProxies(client: *Client) !void { |
| 1131 | }; | 1141 | }; |
| 1132 | | 1142 | |
| 1133 | if (uri.user != null or uri.password != null) { | 1143 | if (uri.user != null or uri.password != null) { |
| 1134 | const authorization = try basicAuthorizationValue(client.allocator, uri); | 1144 | var authorization: [basic_authorization.max_value_len]u8 = undefined; |
| 1135 | defer client.allocator.free(authorization); | 1145 | try client.http_proxy.?.headers.append("proxy-authorization", basic_authorization.value(uri, &authorization)); |
| 1136 | | | |
| 1137 | try client.http_proxy.?.headers.append("proxy-authorization", authorization); | | |
| 1138 | } | 1146 | } |
| 1139 | } | 1147 | } |
| 1140 | | 1148 | |
| ... | @@ -1174,31 +1182,45 @@ pub fn loadDefaultProxies(client: *Client) !void { | ... | @@ -1174,31 +1182,45 @@ pub fn loadDefaultProxies(client: *Client) !void { |
| 1174 | }; | 1182 | }; |
| 1175 | | 1183 | |
| 1176 | if (uri.user != null or uri.password != null) { | 1184 | if (uri.user != null or uri.password != null) { |
| 1177 | const authorization = try basicAuthorizationValue(client.allocator, uri); | 1185 | var authorization: [basic_authorization.max_value_len]u8 = undefined; |
| 1178 | defer client.allocator.free(authorization); | 1186 | try client.https_proxy.?.headers.append("proxy-authorization", basic_authorization.value(uri, &authorization)); |
| 1179 | | | |
| 1180 | try client.https_proxy.?.headers.append("proxy-authorization", authorization); | | |
| 1181 | } | 1187 | } |
| 1182 | } | 1188 | } |
| 1183 | } | 1189 | } |
| 1184 | | 1190 | |
| 1185 | pub fn basicAuthorizationValue( | 1191 | pub const basic_authorization = struct { |
| 1186 | allocator: Allocator, | 1192 | pub const max_user_len = 255; |
| 1187 | uri: Uri, | 1193 | pub const max_password_len = 255; |
| 1188 | ) Allocator.Error![]const u8 { | 1194 | pub const max_value_len = valueLength(max_user_len, max_password_len); |
| | 1195 | |
| 1189 | const prefix = "Basic "; | 1196 | const prefix = "Basic "; |
| 1190 | | 1197 | |
| 1191 | const unencoded = try std.fmt.allocPrint(allocator, "{s}:{s}", .{ uri.user orelse "", uri.password orelse "" }); | 1198 | pub fn valueLength(user_len: usize, password_len: usize) usize { |
| 1192 | defer allocator.free(unencoded); | 1199 | return prefix.len + std.base64.standard.Encoder.calcSize(user_len + 1 + password_len); |
| | 1200 | } |
| | 1201 | |
| | 1202 | pub fn valueLengthFromUri(uri: Uri) usize { |
| | 1203 | return valueLength( |
| | 1204 | if (uri.user) |user| user.len else 0, |
| | 1205 | if (uri.password) |password| password.len else 0, |
| | 1206 | ); |
| | 1207 | } |
| 1193 | | 1208 | |
| 1194 | const buffer = try allocator.alloc(u8, prefix.len + std.base64.standard.Encoder.calcSize(unencoded.len)); | 1209 | pub fn value(uri: Uri, out: []u8) []u8 { |
| 1195 | errdefer allocator.free(buffer); | 1210 | std.debug.assert(uri.user == null or uri.user.?.len <= max_user_len); |
| | 1211 | std.debug.assert(uri.password == null or uri.password.?.len <= max_password_len); |
| 1196 | | 1212 | |
| 1197 | @memcpy(buffer[0..prefix.len], prefix); | 1213 | @memcpy(out[0..prefix.len], prefix); |
| 1198 | _ = std.base64.standard.Encoder.encode(buffer[prefix.len..], unencoded); | | |
| 1199 | | 1214 | |
| 1200 | return buffer; | 1215 | var buf: [max_user_len + ":".len + max_password_len]u8 = undefined; |
| 1201 | } | 1216 | const unencoded = std.fmt.bufPrint(&buf, "{s}:{s}", .{ |
| | 1217 | uri.user orelse "", uri.password orelse "", |
| | 1218 | }) catch unreachable; |
| | 1219 | const base64 = std.base64.standard.Encoder.encode(out[prefix.len..], unencoded); |
| | 1220 | |
| | 1221 | return out[0 .. prefix.len + base64.len]; |
| | 1222 | } |
| | 1223 | }; |
| 1202 | | 1224 | |
| 1203 | pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, NetworkUnreachable, ConnectionTimedOut, ConnectionResetByPeer, TemporaryNameServerFailure, NameServerFailure, UnknownHostName, HostLacksNetworkAddresses, UnexpectedConnectFailure, TlsInitializationFailed }; | 1225 | pub const ConnectTcpError = Allocator.Error || error{ ConnectionRefused, NetworkUnreachable, ConnectionTimedOut, ConnectionResetByPeer, TemporaryNameServerFailure, NameServerFailure, UnknownHostName, HostLacksNetworkAddresses, UnexpectedConnectFailure, TlsInitializationFailed }; |
| 1204 | | 1226 | |