Skip to content

Commit 0537b2d

Browse files
committed
http: replace _forbidden with _auth_challenge struct
1 parent b221c5a commit 0537b2d

File tree

1 file changed

+87
-5
lines changed

1 file changed

+87
-5
lines changed

src/http/Client.zig

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,22 @@ fn perform(self: *Client, timeout_ms: c_int) !void {
370370
const easy = msg.easy_handle.?;
371371
const transfer = try Transfer.fromEasy(easy);
372372

373+
// In case of auth challenge
374+
if (transfer._auth_challenge != null) {
375+
if (transfer.client.notification) |notification| {
376+
var wait_for_interception = false;
377+
notification.dispatch(.http_request_auth_required, &.{ .transfer = transfer, .wait_for_interception = &wait_for_interception });
378+
if (wait_for_interception) {
379+
// the request is put on hold to be intercepted.
380+
// In this case we ignore callbacks for now.
381+
// Note: we don't deinit transfer on purpose: we want to keep
382+
// using it for the following request.
383+
self.endTransfer(transfer);
384+
continue;
385+
}
386+
}
387+
}
388+
373389
// release it ASAP so that it's available; some done_callbacks
374390
// will load more resources.
375391
self.endTransfer(transfer);
@@ -565,6 +581,44 @@ pub const Request = struct {
565581
};
566582
};
567583

584+
pub const AuthChallenge = struct {
585+
source: enum { server, proxy },
586+
scheme: enum { basic, digest },
587+
realm: []const u8,
588+
589+
pub fn parse(header: []const u8) !AuthChallenge {
590+
var ac: AuthChallenge = .{
591+
.source = undefined,
592+
.realm = "TODO", // TODO parser and set realm
593+
.scheme = undefined,
594+
};
595+
596+
const sep = std.mem.indexOfPos(u8, header, 0, ": ") orelse return error.InvalidHeader;
597+
const hname = header[0..sep];
598+
const hvalue = header[sep + 2 ..];
599+
600+
if (std.ascii.eqlIgnoreCase("WWW-Authenticate", hname)) {
601+
ac.source = .server;
602+
} else if (std.ascii.eqlIgnoreCase("Proxy-Authenticate", hname)) {
603+
ac.source = .proxy;
604+
} else {
605+
return error.InvalidAuthChallenge;
606+
}
607+
608+
const pos = std.mem.indexOfPos(u8, std.mem.trim(u8, hvalue, std.ascii.whitespace[0..]), 0, " ") orelse hvalue.len;
609+
const _scheme = hvalue[0..pos];
610+
if (std.ascii.eqlIgnoreCase(_scheme, "basic")) {
611+
ac.scheme = .basic;
612+
} else if (std.ascii.eqlIgnoreCase(_scheme, "digest")) {
613+
ac.scheme = .digest;
614+
} else {
615+
return error.UnknownAuthChallengeScheme;
616+
}
617+
618+
return ac;
619+
}
620+
};
621+
568622
pub const Transfer = struct {
569623
arena: ArenaAllocator,
570624
id: usize = 0,
@@ -588,7 +642,7 @@ pub const Transfer = struct {
588642
_handle: ?*Handle = null,
589643

590644
_redirecting: bool = false,
591-
_forbidden: bool = false,
645+
_auth_challenge: ?AuthChallenge = null,
592646

593647
fn deinit(self: *Transfer) void {
594648
self.req.headers.deinit();
@@ -667,6 +721,14 @@ pub const Transfer = struct {
667721
self.deinit();
668722
}
669723

724+
// abortAuthChallenge is called when an auth chanllenge interception is
725+
// abort. We don't call self.client.endTransfer here b/c it has been done
726+
// before interception process.
727+
pub fn abortAuthChallenge(self: *Transfer) void {
728+
self.client.requestFailed(self, error.AbortAuthChallenge);
729+
self.deinit();
730+
}
731+
670732
// redirectionCookies manages cookies during redirections handled by Curl.
671733
// It sets the cookies from the current response to the cookie jar.
672734
// It also immediately sets cookies for the following request.
@@ -792,20 +854,40 @@ pub const Transfer = struct {
792854
transfer._redirecting = false;
793855

794856
if (status == 401 or status == 407) {
795-
transfer._forbidden = true;
857+
// The auth challenge must be parsed from a following
858+
// WWW-Authenticate or Proxy-Authenticate header.
859+
transfer._auth_challenge = .{
860+
.source = undefined,
861+
.scheme = undefined,
862+
.realm = undefined,
863+
};
796864
return buf_len;
797865
}
798-
transfer._forbidden = false;
866+
transfer._auth_challenge = null;
799867

800868
transfer.bytes_received = buf_len;
801869
return buf_len;
802870
}
803871

804-
if (transfer._redirecting == false and transfer._forbidden == false) {
872+
if (transfer._redirecting == false and transfer._auth_challenge != null) {
805873
transfer.bytes_received += buf_len;
806874
}
807875

808876
if (buf_len != 2) {
877+
if (transfer._auth_challenge != null) {
878+
// try to parse auth challenge.
879+
if (std.ascii.startsWithIgnoreCase(header, "WWW-Authenticate") or
880+
std.ascii.startsWithIgnoreCase(header, "Proxy-Authenticate"))
881+
{
882+
const ac = AuthChallenge.parse(header) catch |err| {
883+
// We can't parse the auth challenge
884+
log.err(.http, "parse auth challenge", .{ .err = err, .header = header });
885+
// Should we cancel the request? I don't think so.
886+
return buf_len;
887+
};
888+
transfer._auth_challenge = ac;
889+
}
890+
}
809891
return buf_len;
810892
}
811893

@@ -833,7 +915,7 @@ pub const Transfer = struct {
833915
return c.CURL_WRITEFUNC_ERROR;
834916
};
835917

836-
if (transfer._redirecting) {
918+
if (transfer._redirecting or transfer._auth_challenge != null) {
837919
return chunk_len;
838920
}
839921

0 commit comments

Comments
 (0)