add s3 exception for virtual host addressing
This commit is contained in:
parent
c531164cfa
commit
b9aaffb08d
145
src/aws_http.zig
145
src/aws_http.zig
|
@ -56,11 +56,13 @@ const EndPoint = struct {
|
||||||
host: []const u8,
|
host: []const u8,
|
||||||
scheme: []const u8,
|
scheme: []const u8,
|
||||||
port: u16,
|
port: u16,
|
||||||
|
path: []const u8,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
fn deinit(self: EndPoint) void {
|
fn deinit(self: EndPoint) void {
|
||||||
self.allocator.free(self.uri);
|
self.allocator.free(self.uri);
|
||||||
self.allocator.free(self.host);
|
self.allocator.free(self.host);
|
||||||
|
self.allocator.free(self.path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
pub const AwsHttp = struct {
|
pub const AwsHttp = struct {
|
||||||
|
@ -112,7 +114,7 @@ pub const AwsHttp = struct {
|
||||||
// S3 control uses <account-id>.s3-control.<region>.amazonaws.com
|
// S3 control uses <account-id>.s3-control.<region>.amazonaws.com
|
||||||
//
|
//
|
||||||
// So this regionSubDomain call needs to handle generic customization
|
// So this regionSubDomain call needs to handle generic customization
|
||||||
const endpoint = try endpointForRequest(self.allocator, service, options.region, options.dualstack);
|
const endpoint = try endpointForRequest(self.allocator, service, request, options);
|
||||||
defer endpoint.deinit();
|
defer endpoint.deinit();
|
||||||
log.debug("Calling endpoint {s}", .{endpoint.uri});
|
log.debug("Calling endpoint {s}", .{endpoint.uri});
|
||||||
// TODO: Should we allow customization here?
|
// TODO: Should we allow customization here?
|
||||||
|
@ -145,12 +147,17 @@ pub const AwsHttp = struct {
|
||||||
pub fn makeRequest(self: Self, endpoint: EndPoint, request: HttpRequest, signing_config: ?signing.Config) !HttpResult {
|
pub fn makeRequest(self: Self, endpoint: EndPoint, request: HttpRequest, signing_config: ?signing.Config) !HttpResult {
|
||||||
var request_cp = request;
|
var request_cp = request;
|
||||||
|
|
||||||
log.debug("Path: {s}", .{request_cp.path});
|
log.debug("Request Path: {s}", .{request_cp.path});
|
||||||
|
log.debug("Endpoint Path (actually used): {s}", .{endpoint.path});
|
||||||
log.debug("Query: {s}", .{request_cp.query});
|
log.debug("Query: {s}", .{request_cp.query});
|
||||||
log.debug("Method: {s}", .{request_cp.method});
|
log.debug("Method: {s}", .{request_cp.method});
|
||||||
log.debug("body length: {d}", .{request_cp.body.len});
|
log.debug("body length: {d}", .{request_cp.body.len});
|
||||||
log.debug("Body\n====\n{s}\n====", .{request_cp.body});
|
log.debug("Body\n====\n{s}\n====", .{request_cp.body});
|
||||||
|
|
||||||
|
// Endpoint calculation might be different from the request (e.g. S3 requests)
|
||||||
|
// We will use endpoint instead
|
||||||
|
request_cp.path = endpoint.path;
|
||||||
|
|
||||||
var request_headers = std.ArrayList(base.Header).init(self.allocator);
|
var request_headers = std.ArrayList(base.Header).init(self.allocator);
|
||||||
defer request_headers.deinit();
|
defer request_headers.deinit();
|
||||||
|
|
||||||
|
@ -176,7 +183,7 @@ pub const AwsHttp = struct {
|
||||||
log.debug("\t{s}: {s}", .{ h.name, h.value });
|
log.debug("\t{s}: {s}", .{ h.name, h.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = try std.fmt.allocPrint(self.allocator, "{s}{s}{s}", .{ endpoint.uri, request.path, request.query });
|
const url = try std.fmt.allocPrint(self.allocator, "{s}{s}{s}", .{ endpoint.uri, request_cp.path, request_cp.query });
|
||||||
defer self.allocator.free(url);
|
defer self.allocator.free(url);
|
||||||
log.debug("Request url: {s}", .{url});
|
log.debug("Request url: {s}", .{url});
|
||||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
@ -253,32 +260,26 @@ fn getEnvironmentVariable(allocator: std.mem.Allocator, key: []const u8) !?[]con
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endpointForRequest(allocator: std.mem.Allocator, service: []const u8, region: []const u8, use_dual_stack: bool) !EndPoint {
|
fn endpointForRequest(allocator: std.mem.Allocator, service: []const u8, request: HttpRequest, options: Options) !EndPoint {
|
||||||
const environment_override = try getEnvironmentVariable(allocator, "AWS_ENDPOINT_URL");
|
const environment_override = try getEnvironmentVariable(allocator, "AWS_ENDPOINT_URL");
|
||||||
if (environment_override) |override| {
|
if (environment_override) |override| {
|
||||||
const uri = try allocator.dupeZ(u8, override);
|
const uri = try allocator.dupeZ(u8, override);
|
||||||
return endPointFromUri(allocator, uri);
|
return endPointFromUri(allocator, uri);
|
||||||
}
|
}
|
||||||
if (std.mem.eql(u8, service, "cloudfront")) {
|
|
||||||
return EndPoint{
|
|
||||||
.uri = try allocator.dupe(u8, "https://cloudfront.amazonaws.com"),
|
|
||||||
.host = try allocator.dupe(u8, "cloudfront.amazonaws.com"),
|
|
||||||
.scheme = "https",
|
|
||||||
.port = 443,
|
|
||||||
.allocator = allocator,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Fallback to us-east-1 if global endpoint does not exist.
|
// Fallback to us-east-1 if global endpoint does not exist.
|
||||||
const realregion = if (std.mem.eql(u8, region, "aws-global")) "us-east-1" else region;
|
const realregion = if (std.mem.eql(u8, options.region, "aws-global")) "us-east-1" else options.region;
|
||||||
const dualstack = if (use_dual_stack) ".dualstack" else "";
|
const dualstack = if (options.dualstack) ".dualstack" else "";
|
||||||
|
|
||||||
const domain = switch (std.hash_map.hashString(region)) {
|
const domain = switch (std.hash_map.hashString(options.region)) {
|
||||||
US_ISO_EAST_1_HASH => "c2s.ic.gov",
|
US_ISO_EAST_1_HASH => "c2s.ic.gov",
|
||||||
CN_NORTH_1_HASH, CN_NORTHWEST_1_HASH => "amazonaws.com.cn",
|
CN_NORTH_1_HASH, CN_NORTHWEST_1_HASH => "amazonaws.com.cn",
|
||||||
US_ISOB_EAST_1_HASH => "sc2s.sgov.gov",
|
US_ISOB_EAST_1_HASH => "sc2s.sgov.gov",
|
||||||
else => "amazonaws.com",
|
else => "amazonaws.com",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (try endpointException(allocator, service, request, options, realregion, dualstack, domain)) |e|
|
||||||
|
return e;
|
||||||
|
|
||||||
const uri = try std.fmt.allocPrintZ(allocator, "https://{s}{s}.{s}.{s}", .{ service, dualstack, realregion, domain });
|
const uri = try std.fmt.allocPrintZ(allocator, "https://{s}{s}.{s}.{s}", .{ service, dualstack, realregion, domain });
|
||||||
const host = try allocator.dupe(u8, uri["https://".len..]);
|
const host = try allocator.dupe(u8, uri["https://".len..]);
|
||||||
log.debug("host: {s}, scheme: {s}, port: {}", .{ host, "https", 443 });
|
log.debug("host: {s}, scheme: {s}, port: {}", .{ host, "https", 443 });
|
||||||
|
@ -288,9 +289,68 @@ fn endpointForRequest(allocator: std.mem.Allocator, service: []const u8, region:
|
||||||
.scheme = "https",
|
.scheme = "https",
|
||||||
.port = 443,
|
.port = 443,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
.path = try allocator.dupe(u8, request.path),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn endpointException(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
service: []const u8,
|
||||||
|
request: HttpRequest,
|
||||||
|
options: Options,
|
||||||
|
realregion: []const u8,
|
||||||
|
dualstack: []const u8,
|
||||||
|
domain: []const u8,
|
||||||
|
) !?EndPoint {
|
||||||
|
if (std.mem.eql(u8, service, "cloudfront")) {
|
||||||
|
return EndPoint{
|
||||||
|
.uri = try allocator.dupe(u8, "https://cloudfront.amazonaws.com"),
|
||||||
|
.host = try allocator.dupe(u8, "cloudfront.amazonaws.com"),
|
||||||
|
.scheme = "https",
|
||||||
|
.port = 443,
|
||||||
|
.allocator = allocator,
|
||||||
|
.path = try allocator.dupe(u8, request.path),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (std.mem.eql(u8, service, "s3")) {
|
||||||
|
if (request.path.len == 1 or std.mem.indexOf(u8, request.path[1..], "/") == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// We need to adjust the host and the path to accomodate virtual
|
||||||
|
// host addressing. This only applies to bucket operations, but
|
||||||
|
// right now I'm hoping that bucket operations do not include a path
|
||||||
|
// component, so will be handled by the return null statement above.
|
||||||
|
const bucket_name = s3BucketFromPath(request.path);
|
||||||
|
const rest_of_path = request.path[bucket_name.len + 1 ..];
|
||||||
|
// TODO: Implement
|
||||||
|
_ = options;
|
||||||
|
const uri = try std.fmt.allocPrintZ(allocator, "https://{s}.{s}{s}.{s}.{s}", .{ bucket_name, service, dualstack, realregion, domain });
|
||||||
|
const host = try allocator.dupe(u8, uri["https://".len..]);
|
||||||
|
log.debug("S3 host: {s}, scheme: {s}, port: {}", .{ host, "https", 443 });
|
||||||
|
return EndPoint{
|
||||||
|
.uri = uri,
|
||||||
|
.host = host,
|
||||||
|
.scheme = "https",
|
||||||
|
.port = 443,
|
||||||
|
.allocator = allocator,
|
||||||
|
.path = try allocator.dupe(u8, rest_of_path),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn s3BucketFromPath(path: []const u8) []const u8 {
|
||||||
|
var in_bucket = false;
|
||||||
|
var start: usize = 0;
|
||||||
|
for (path) |c, inx| {
|
||||||
|
if (c == '/') {
|
||||||
|
if (in_bucket) return path[start..inx];
|
||||||
|
start = inx + 1;
|
||||||
|
in_bucket = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
/// creates an endpoint from a uri string.
|
/// creates an endpoint from a uri string.
|
||||||
///
|
///
|
||||||
/// allocator: Will be used only to construct the EndPoint struct
|
/// allocator: Will be used only to construct the EndPoint struct
|
||||||
|
@ -337,27 +397,68 @@ fn endPointFromUri(allocator: std.mem.Allocator, uri: []const u8) !EndPoint {
|
||||||
.scheme = scheme,
|
.scheme = scheme,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.port = port,
|
.port = port,
|
||||||
|
.path = try allocator.dupe(u8, "/"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test "endpointForRequest standard operation" {
|
test "endpointForRequest standard operation" {
|
||||||
|
const request: HttpRequest = .{};
|
||||||
|
const options: Options = .{
|
||||||
|
.region = "us-west-2",
|
||||||
|
.dualstack = false,
|
||||||
|
.sigv4_service_name = null,
|
||||||
|
};
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
const service = "dynamodb";
|
const service = "dynamodb";
|
||||||
const region = "us-west-2";
|
|
||||||
const use_dual_stack = false;
|
|
||||||
|
|
||||||
const endpoint = try endpointForRequest(allocator, service, region, use_dual_stack);
|
const endpoint = try endpointForRequest(allocator, service, request, options);
|
||||||
defer endpoint.deinit();
|
defer endpoint.deinit();
|
||||||
try std.testing.expectEqualStrings("https://dynamodb.us-west-2.amazonaws.com", endpoint.uri);
|
try std.testing.expectEqualStrings("https://dynamodb.us-west-2.amazonaws.com", endpoint.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "endpointForRequest for cloudfront" {
|
test "endpointForRequest for cloudfront" {
|
||||||
|
const request = HttpRequest{};
|
||||||
|
const options = Options{
|
||||||
|
.region = "us-west-2",
|
||||||
|
.dualstack = false,
|
||||||
|
.sigv4_service_name = null,
|
||||||
|
};
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
const service = "cloudfront";
|
const service = "cloudfront";
|
||||||
const region = "us-west-2";
|
|
||||||
const use_dual_stack = false;
|
|
||||||
|
|
||||||
const endpoint = try endpointForRequest(allocator, service, region, use_dual_stack);
|
const endpoint = try endpointForRequest(allocator, service, request, options);
|
||||||
defer endpoint.deinit();
|
defer endpoint.deinit();
|
||||||
try std.testing.expectEqualStrings("https://cloudfront.amazonaws.com", endpoint.uri);
|
try std.testing.expectEqualStrings("https://cloudfront.amazonaws.com", endpoint.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "endpointForRequest for s3" {
|
||||||
|
const request = HttpRequest{};
|
||||||
|
const options = Options{
|
||||||
|
.region = "us-east-2",
|
||||||
|
.dualstack = false,
|
||||||
|
.sigv4_service_name = null,
|
||||||
|
};
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
const service = "s3";
|
||||||
|
|
||||||
|
const endpoint = try endpointForRequest(allocator, service, request, options);
|
||||||
|
defer endpoint.deinit();
|
||||||
|
try std.testing.expectEqualStrings("https://s3.us-east-2.amazonaws.com", endpoint.uri);
|
||||||
|
}
|
||||||
|
test "endpointForRequest for s3 - specific bucket" {
|
||||||
|
const request = HttpRequest{
|
||||||
|
.path = "/bucket/key",
|
||||||
|
};
|
||||||
|
const options = Options{
|
||||||
|
.region = "us-east-2",
|
||||||
|
.dualstack = false,
|
||||||
|
.sigv4_service_name = null,
|
||||||
|
};
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
const service = "s3";
|
||||||
|
|
||||||
|
const endpoint = try endpointForRequest(allocator, service, request, options);
|
||||||
|
defer endpoint.deinit();
|
||||||
|
try std.testing.expectEqualStrings("https://bucket.s3.us-east-2.amazonaws.com", endpoint.uri);
|
||||||
|
try std.testing.expectEqualStrings("/key", endpoint.path);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user