Commit 5b8b5f2505
Changed files (4)
lib
std
lib/std/crypto/Certificate/Bundle.zig
@@ -105,7 +105,9 @@ pub fn addCertsFromFile(
// This is possible by computing the decoded length and reserving the space
// for the decoded bytes first.
const decoded_size_upper_bound = size / 4 * 3;
- try cb.bytes.ensureUnusedCapacity(gpa, decoded_size_upper_bound + size);
+ const needed_capacity = std.math.cast(u32, decoded_size_upper_bound + size) orelse
+ return error.CertificateAuthorityBundleTooBig;
+ try cb.bytes.ensureUnusedCapacity(gpa, needed_capacity);
const end_reserved = cb.bytes.items.len + decoded_size_upper_bound;
const buffer = cb.bytes.allocatedSlice()[end_reserved..];
const end_index = try file.readAll(buffer);
lib/std/http/Client.zig
@@ -3,6 +3,7 @@ const assert = std.debug.assert;
const http = std.http;
const net = std.net;
const Client = @This();
+const Url = std.Url;
allocator: std.mem.Allocator,
headers: std.ArrayListUnmanaged(u8) = .{},
@@ -19,14 +20,7 @@ pub const Request = struct {
pub const Protocol = enum { http, https };
pub const Options = struct {
- family: Family = .any,
- protocol: Protocol = .https,
method: http.Method = .GET,
- host: []const u8 = "localhost",
- path: []const u8 = "/",
- port: u16 = 0,
-
- pub const Family = enum { any, ip4, ip6 };
};
pub fn deinit(req: *Request) void {
@@ -90,20 +84,27 @@ pub fn deinit(client: *Client) void {
client.* = undefined;
}
-pub fn request(client: *Client, options: Request.Options) !Request {
+pub fn request(client: *Client, url: Url, options: Request.Options) !Request {
+ const protocol = std.meta.stringToEnum(Request.Protocol, url.scheme) orelse
+ return error.UnsupportedUrlScheme;
+ const port: u16 = url.port orelse switch (protocol) {
+ .http => 80,
+ .https => 443,
+ };
+
var req: Request = .{
.client = client,
- .stream = try net.tcpConnectToHost(client.allocator, options.host, options.port),
- .protocol = options.protocol,
+ .stream = try net.tcpConnectToHost(client.allocator, url.host, port),
+ .protocol = protocol,
.tls_client = undefined,
};
client.active_requests += 1;
errdefer req.deinit();
- switch (options.protocol) {
+ switch (protocol) {
.http => {},
.https => {
- req.tls_client = try std.crypto.tls.Client.init(req.stream, client.ca_bundle, options.host);
+ req.tls_client = try std.crypto.tls.Client.init(req.stream, client.ca_bundle, url.host);
},
}
@@ -111,19 +112,19 @@ pub fn request(client: *Client, options: Request.Options) !Request {
client.allocator,
@tagName(options.method).len +
1 +
- options.path.len +
+ url.path.len +
" HTTP/1.1\r\nHost: ".len +
- options.host.len +
+ url.host.len +
"\r\nUpgrade-Insecure-Requests: 1\r\n".len +
client.headers.items.len +
2, // for the \r\n at the end of headers
);
req.headers.appendSliceAssumeCapacity(@tagName(options.method));
req.headers.appendSliceAssumeCapacity(" ");
- req.headers.appendSliceAssumeCapacity(options.path);
+ req.headers.appendSliceAssumeCapacity(url.path);
req.headers.appendSliceAssumeCapacity(" HTTP/1.1\r\nHost: ");
- req.headers.appendSliceAssumeCapacity(options.host);
- switch (options.protocol) {
+ req.headers.appendSliceAssumeCapacity(url.host);
+ switch (protocol) {
.https => req.headers.appendSliceAssumeCapacity("\r\nUpgrade-Insecure-Requests: 1\r\n"),
.http => req.headers.appendSliceAssumeCapacity("\r\n"),
}
lib/std/std.zig
@@ -42,6 +42,7 @@ pub const Target = @import("target.zig").Target;
pub const Thread = @import("Thread.zig");
pub const Treap = @import("treap.zig").Treap;
pub const Tz = tz.Tz;
+pub const Url = @import("Url.zig");
pub const array_hash_map = @import("array_hash_map.zig");
pub const atomic = @import("atomic.zig");
lib/std/Url.zig
@@ -0,0 +1,98 @@
+scheme: []const u8,
+host: []const u8,
+path: []const u8,
+port: ?u16,
+
+/// TODO: redo this implementation according to RFC 1738. This code is only a
+/// placeholder for now.
+pub fn parse(s: []const u8) !Url {
+ var scheme_end: usize = 0;
+ var host_start: usize = 0;
+ var host_end: usize = 0;
+ var path_start: usize = 0;
+ var port_start: usize = 0;
+ var port_end: usize = 0;
+ var state: enum {
+ scheme,
+ scheme_slash1,
+ scheme_slash2,
+ host,
+ port,
+ path,
+ } = .scheme;
+
+ for (s) |b, i| switch (state) {
+ .scheme => switch (b) {
+ ':' => {
+ state = .scheme_slash1;
+ scheme_end = i;
+ },
+ else => {},
+ },
+ .scheme_slash1 => switch (b) {
+ '/' => {
+ state = .scheme_slash2;
+ },
+ else => return error.InvalidUrl,
+ },
+ .scheme_slash2 => switch (b) {
+ '/' => {
+ state = .host;
+ host_start = i + 1;
+ },
+ else => return error.InvalidUrl,
+ },
+ .host => switch (b) {
+ ':' => {
+ state = .port;
+ host_end = i;
+ port_start = i + 1;
+ },
+ '/' => {
+ state = .path;
+ host_end = i;
+ path_start = i;
+ },
+ else => {},
+ },
+ .port => switch (b) {
+ '/' => {
+ port_end = i;
+ state = .path;
+ path_start = i;
+ },
+ else => {},
+ },
+ .path => {},
+ };
+
+ const port_slice = s[port_start..port_end];
+ const port = if (port_slice.len == 0) null else try std.fmt.parseInt(u16, port_slice, 10);
+
+ return .{
+ .scheme = s[0..scheme_end],
+ .host = s[host_start..host_end],
+ .path = s[path_start..],
+ .port = port,
+ };
+}
+
+const Url = @This();
+const std = @import("std.zig");
+const testing = std.testing;
+
+test "basic" {
+ const parsed = try parse("https://ziglang.org/download");
+ try testing.expectEqualStrings("https", parsed.scheme);
+ try testing.expectEqualStrings("ziglang.org", parsed.host);
+ try testing.expectEqualStrings("/download", parsed.path);
+ try testing.expectEqual(@as(?u16, null), parsed.port);
+}
+
+test "with port" {
+ const parsed = try parse("http://example:1337/");
+ try testing.expectEqualStrings("http", parsed.scheme);
+ try testing.expectEqualStrings("example", parsed.host);
+ try testing.expectEqualStrings("/", parsed.path);
+ try testing.expectEqual(@as(?u16, 1337), parsed.port);
+}