mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Merge pull request #960 from lightpanda-io/auth-challenge
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
auth required interception
This commit is contained in:
@@ -474,12 +474,16 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
|||||||
self.cdp.browser.notification.unregister(.http_response_header_done, self);
|
self.cdp.browser.notification.unregister(.http_response_header_done, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetchEnable(self: *Self) !void {
|
pub fn fetchEnable(self: *Self, authRequests: bool) !void {
|
||||||
try self.cdp.browser.notification.register(.http_request_intercept, self, onHttpRequestIntercept);
|
try self.cdp.browser.notification.register(.http_request_intercept, self, onHttpRequestIntercept);
|
||||||
|
if (authRequests) {
|
||||||
|
try self.cdp.browser.notification.register(.http_request_auth_required, self, onHttpRequestAuthRequired);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetchDisable(self: *Self) void {
|
pub fn fetchDisable(self: *Self) void {
|
||||||
self.cdp.browser.notification.unregister(.http_request_intercept, self);
|
self.cdp.browser.notification.unregister(.http_request_intercept, self);
|
||||||
|
self.cdp.browser.notification.unregister(.http_request_auth_required, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn onPageRemove(ctx: *anyopaque, _: Notification.PageRemove) !void {
|
pub fn onPageRemove(ctx: *anyopaque, _: Notification.PageRemove) !void {
|
||||||
@@ -545,6 +549,12 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
|||||||
try gop.value_ptr.appendSlice(arena, try arena.dupe(u8, msg.data));
|
try gop.value_ptr.appendSlice(arena, try arena.dupe(u8, msg.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn onHttpRequestAuthRequired(ctx: *anyopaque, data: *const Notification.RequestAuthRequired) !void {
|
||||||
|
const self: *Self = @alignCast(@ptrCast(ctx));
|
||||||
|
defer self.resetNotificationArena();
|
||||||
|
try @import("domains/fetch.zig").requestAuthRequired(self.notification_arena, self, data);
|
||||||
|
}
|
||||||
|
|
||||||
fn resetNotificationArena(self: *Self) void {
|
fn resetNotificationArena(self: *Self) void {
|
||||||
defer _ = self.cdp.notification_arena.reset(.{ .retain_with_limit = 1024 * 64 });
|
defer _ = self.cdp.notification_arena.reset(.{ .retain_with_limit = 1024 * 64 });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,14 @@ pub fn processMessage(cmd: anytype) !void {
|
|||||||
continueRequest,
|
continueRequest,
|
||||||
failRequest,
|
failRequest,
|
||||||
fulfillRequest,
|
fulfillRequest,
|
||||||
|
continueWithAuth,
|
||||||
}, cmd.input.action) orelse return error.UnknownMethod;
|
}, cmd.input.action) orelse return error.UnknownMethod;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
.disable => return disable(cmd),
|
.disable => return disable(cmd),
|
||||||
.enable => return enable(cmd),
|
.enable => return enable(cmd),
|
||||||
.continueRequest => return continueRequest(cmd),
|
.continueRequest => return continueRequest(cmd),
|
||||||
|
.continueWithAuth => return continueWithAuth(cmd),
|
||||||
.failRequest => return failRequest(cmd),
|
.failRequest => return failRequest(cmd),
|
||||||
.fulfillRequest => return fulfillRequest(cmd),
|
.fulfillRequest => return fulfillRequest(cmd),
|
||||||
}
|
}
|
||||||
@@ -144,12 +146,8 @@ fn enable(cmd: anytype) !void {
|
|||||||
return cmd.sendResult(null, .{});
|
return cmd.sendResult(null, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.handleAuthRequests) {
|
|
||||||
log.warn(.cdp, "not implemented", .{ .feature = "Fetch.enable handleAuthRequests is not supported yet" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||||
try bc.fetchEnable();
|
try bc.fetchEnable(params.handleAuthRequests);
|
||||||
|
|
||||||
return cmd.sendResult(null, .{});
|
return cmd.sendResult(null, .{});
|
||||||
}
|
}
|
||||||
@@ -276,6 +274,63 @@ fn continueRequest(cmd: anytype) !void {
|
|||||||
return cmd.sendResult(null, .{});
|
return cmd.sendResult(null, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#type-AuthChallengeResponse
|
||||||
|
const AuthChallengeResponse = enum {
|
||||||
|
Default,
|
||||||
|
CancelAuth,
|
||||||
|
ProvideCredentials,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn continueWithAuth(cmd: anytype) !void {
|
||||||
|
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||||
|
const params = (try cmd.params(struct {
|
||||||
|
requestId: []const u8, // "INTERCEPT-{d}"
|
||||||
|
authChallengeResponse: struct {
|
||||||
|
response: AuthChallengeResponse,
|
||||||
|
username: []const u8 = "",
|
||||||
|
password: []const u8 = "",
|
||||||
|
},
|
||||||
|
})) orelse return error.InvalidParams;
|
||||||
|
|
||||||
|
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||||
|
|
||||||
|
var intercept_state = &bc.intercept_state;
|
||||||
|
const request_id = try idFromRequestId(params.requestId);
|
||||||
|
const transfer = intercept_state.remove(request_id) orelse return error.RequestNotFound;
|
||||||
|
|
||||||
|
log.debug(.cdp, "request intercept", .{
|
||||||
|
.state = "continue with auth",
|
||||||
|
.id = transfer.id,
|
||||||
|
.response = params.authChallengeResponse.response,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (params.authChallengeResponse.response != .ProvideCredentials) {
|
||||||
|
transfer.abortAuthChallenge();
|
||||||
|
return cmd.sendResult(null, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancel the request, deinit the transfer on error.
|
||||||
|
errdefer transfer.abortAuthChallenge();
|
||||||
|
|
||||||
|
// restart the request with the provided credentials.
|
||||||
|
const arena = transfer.arena.allocator();
|
||||||
|
transfer.updateCredentials(
|
||||||
|
try std.fmt.allocPrintZ(arena, "{s}:{s}", .{
|
||||||
|
params.authChallengeResponse.username,
|
||||||
|
params.authChallengeResponse.password,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
transfer.reset();
|
||||||
|
try bc.cdp.browser.http_client.process(transfer);
|
||||||
|
|
||||||
|
if (intercept_state.empty()) {
|
||||||
|
page.request_intercepted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd.sendResult(null, .{});
|
||||||
|
}
|
||||||
|
|
||||||
fn fulfillRequest(cmd: anytype) !void {
|
fn fulfillRequest(cmd: anytype) !void {
|
||||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||||
|
|
||||||
@@ -346,6 +401,50 @@ fn failRequest(cmd: anytype) !void {
|
|||||||
return cmd.sendResult(null, .{});
|
return cmd.sendResult(null, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn requestAuthRequired(arena: Allocator, bc: anytype, intercept: *const Notification.RequestAuthRequired) !void {
|
||||||
|
// unreachable because we _have_ to have a page.
|
||||||
|
const session_id = bc.session_id orelse unreachable;
|
||||||
|
const target_id = bc.target_id orelse unreachable;
|
||||||
|
const page = bc.session.currentPage() orelse unreachable;
|
||||||
|
|
||||||
|
// We keep it around to wait for modifications to the request.
|
||||||
|
// NOTE: we assume whomever created the request created it with a lifetime of the Page.
|
||||||
|
// TODO: What to do when receiving replies for a previous page's requests?
|
||||||
|
|
||||||
|
const transfer = intercept.transfer;
|
||||||
|
try bc.intercept_state.put(transfer);
|
||||||
|
|
||||||
|
const challenge = transfer._auth_challenge orelse return error.NullAuthChallenge;
|
||||||
|
|
||||||
|
try bc.cdp.sendEvent("Fetch.authRequired", .{
|
||||||
|
.requestId = try std.fmt.allocPrint(arena, "INTERCEPT-{d}", .{transfer.id}),
|
||||||
|
.request = network.TransferAsRequestWriter.init(transfer),
|
||||||
|
.frameId = target_id,
|
||||||
|
.resourceType = switch (transfer.req.resource_type) {
|
||||||
|
.script => "Script",
|
||||||
|
.xhr => "XHR",
|
||||||
|
.document => "Document",
|
||||||
|
},
|
||||||
|
.authChallenge = .{
|
||||||
|
.source = if (challenge.source == .server) "Server" else "Proxy",
|
||||||
|
.origin = "", // TODO get origin, could be the proxy address for example.
|
||||||
|
.scheme = if (challenge.scheme == .digest) "digest" else "basic",
|
||||||
|
.realm = challenge.realm,
|
||||||
|
},
|
||||||
|
.networkId = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}),
|
||||||
|
}, .{ .session_id = session_id });
|
||||||
|
|
||||||
|
log.debug(.cdp, "request auth required", .{
|
||||||
|
.state = "paused",
|
||||||
|
.id = transfer.id,
|
||||||
|
.url = transfer.uri,
|
||||||
|
});
|
||||||
|
// Await continueWithAuth
|
||||||
|
|
||||||
|
intercept.wait_for_interception.* = true;
|
||||||
|
page.request_intercepted = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Get u64 from requestId which is formatted as: "INTERCEPT-{d}"
|
// Get u64 from requestId which is formatted as: "INTERCEPT-{d}"
|
||||||
fn idFromRequestId(request_id: []const u8) !u64 {
|
fn idFromRequestId(request_id: []const u8) !u64 {
|
||||||
if (!std.mem.startsWith(u8, request_id, "INTERCEPT-")) {
|
if (!std.mem.startsWith(u8, request_id, "INTERCEPT-")) {
|
||||||
|
|||||||
@@ -328,6 +328,11 @@ fn makeRequest(self: *Client, handle: *Handle, transfer: *Transfer) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PRIVATE, transfer));
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PRIVATE, transfer));
|
||||||
|
|
||||||
|
// add credentials
|
||||||
|
if (req.credentials) |creds| {
|
||||||
|
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXYUSERPWD, creds.ptr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once soon as this is called, our "perform" loop is responsible for
|
// Once soon as this is called, our "perform" loop is responsible for
|
||||||
@@ -378,6 +383,22 @@ fn perform(self: *Client, timeout_ms: c_int, socket: ?posix.socket_t) !bool {
|
|||||||
const easy = msg.easy_handle.?;
|
const easy = msg.easy_handle.?;
|
||||||
const transfer = try Transfer.fromEasy(easy);
|
const transfer = try Transfer.fromEasy(easy);
|
||||||
|
|
||||||
|
// In case of auth challenge
|
||||||
|
if (transfer._auth_challenge != null and transfer._tries < 10) { // TODO give a way to configure the number of auth retries.
|
||||||
|
if (transfer.client.notification) |notification| {
|
||||||
|
var wait_for_interception = false;
|
||||||
|
notification.dispatch(.http_request_auth_required, &.{ .transfer = transfer, .wait_for_interception = &wait_for_interception });
|
||||||
|
if (wait_for_interception) {
|
||||||
|
// the request is put on hold to be intercepted.
|
||||||
|
// In this case we ignore callbacks for now.
|
||||||
|
// Note: we don't deinit transfer on purpose: we want to keep
|
||||||
|
// using it for the following request.
|
||||||
|
self.endTransfer(transfer);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// release it ASAP so that it's available; some done_callbacks
|
// release it ASAP so that it's available; some done_callbacks
|
||||||
// will load more resources.
|
// will load more resources.
|
||||||
self.endTransfer(transfer);
|
self.endTransfer(transfer);
|
||||||
@@ -557,6 +578,7 @@ pub const Request = struct {
|
|||||||
body: ?[]const u8 = null,
|
body: ?[]const u8 = null,
|
||||||
cookie_jar: *CookieJar,
|
cookie_jar: *CookieJar,
|
||||||
resource_type: ResourceType,
|
resource_type: ResourceType,
|
||||||
|
credentials: ?[:0]const u8 = null,
|
||||||
|
|
||||||
// arbitrary data that can be associated with this request
|
// arbitrary data that can be associated with this request
|
||||||
ctx: *anyopaque = undefined,
|
ctx: *anyopaque = undefined,
|
||||||
@@ -574,6 +596,46 @@ pub const Request = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const AuthChallenge = struct {
|
||||||
|
status: u16,
|
||||||
|
source: enum { server, proxy },
|
||||||
|
scheme: enum { basic, digest },
|
||||||
|
realm: []const u8,
|
||||||
|
|
||||||
|
pub fn parse(status: u16, header: []const u8) !AuthChallenge {
|
||||||
|
var ac: AuthChallenge = .{
|
||||||
|
.status = status,
|
||||||
|
.source = undefined,
|
||||||
|
.realm = "TODO", // TODO parser and set realm
|
||||||
|
.scheme = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const sep = std.mem.indexOfPos(u8, header, 0, ": ") orelse return error.InvalidHeader;
|
||||||
|
const hname = header[0..sep];
|
||||||
|
const hvalue = header[sep + 2 ..];
|
||||||
|
|
||||||
|
if (std.ascii.eqlIgnoreCase("WWW-Authenticate", hname)) {
|
||||||
|
ac.source = .server;
|
||||||
|
} else if (std.ascii.eqlIgnoreCase("Proxy-Authenticate", hname)) {
|
||||||
|
ac.source = .proxy;
|
||||||
|
} else {
|
||||||
|
return error.InvalidAuthChallenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pos = std.mem.indexOfPos(u8, std.mem.trim(u8, hvalue, std.ascii.whitespace[0..]), 0, " ") orelse hvalue.len;
|
||||||
|
const _scheme = hvalue[0..pos];
|
||||||
|
if (std.ascii.eqlIgnoreCase(_scheme, "basic")) {
|
||||||
|
ac.scheme = .basic;
|
||||||
|
} else if (std.ascii.eqlIgnoreCase(_scheme, "digest")) {
|
||||||
|
ac.scheme = .digest;
|
||||||
|
} else {
|
||||||
|
return error.UnknownAuthChallengeScheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ac;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Transfer = struct {
|
pub const Transfer = struct {
|
||||||
arena: ArenaAllocator,
|
arena: ArenaAllocator,
|
||||||
id: usize = 0,
|
id: usize = 0,
|
||||||
@@ -586,7 +648,6 @@ pub const Transfer = struct {
|
|||||||
bytes_received: usize = 0,
|
bytes_received: usize = 0,
|
||||||
|
|
||||||
// We'll store the response header here
|
// We'll store the response header here
|
||||||
proxy_response_header: ?ResponseHeader = null,
|
|
||||||
response_header: ?ResponseHeader = null,
|
response_header: ?ResponseHeader = null,
|
||||||
|
|
||||||
// track if the header callbacks done have been called.
|
// track if the header callbacks done have been called.
|
||||||
@@ -597,7 +658,22 @@ pub const Transfer = struct {
|
|||||||
_handle: ?*Handle = null,
|
_handle: ?*Handle = null,
|
||||||
|
|
||||||
_redirecting: bool = false,
|
_redirecting: bool = false,
|
||||||
_forbidden: bool = false,
|
_auth_challenge: ?AuthChallenge = null,
|
||||||
|
|
||||||
|
// number of times the transfer has been tried.
|
||||||
|
// incremented by reset func.
|
||||||
|
_tries: u8 = 0,
|
||||||
|
|
||||||
|
pub fn reset(self: *Transfer) void {
|
||||||
|
self._redirecting = false;
|
||||||
|
self._auth_challenge = null;
|
||||||
|
self._notified_fail = false;
|
||||||
|
self._header_done_called = false;
|
||||||
|
self.response_header = null;
|
||||||
|
self.bytes_received = 0;
|
||||||
|
|
||||||
|
self._tries += 1;
|
||||||
|
}
|
||||||
|
|
||||||
fn deinit(self: *Transfer) void {
|
fn deinit(self: *Transfer) void {
|
||||||
self.req.headers.deinit();
|
self.req.headers.deinit();
|
||||||
@@ -615,7 +691,11 @@ pub const Transfer = struct {
|
|||||||
try errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_EFFECTIVE_URL, &url));
|
try errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_EFFECTIVE_URL, &url));
|
||||||
|
|
||||||
var status: c_long = undefined;
|
var status: c_long = undefined;
|
||||||
try errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_RESPONSE_CODE, &status));
|
if (self._auth_challenge) |_| {
|
||||||
|
status = 407;
|
||||||
|
} else {
|
||||||
|
try errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_RESPONSE_CODE, &status));
|
||||||
|
}
|
||||||
|
|
||||||
self.response_header = .{
|
self.response_header = .{
|
||||||
.url = url,
|
.url = url,
|
||||||
@@ -648,6 +728,10 @@ pub const Transfer = struct {
|
|||||||
self.req.url = url;
|
self.req.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn updateCredentials(self: *Transfer, userpwd: [:0]const u8) void {
|
||||||
|
self.req.credentials = userpwd;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn replaceRequestHeaders(self: *Transfer, allocator: Allocator, headers: []const Http.Header) !void {
|
pub fn replaceRequestHeaders(self: *Transfer, allocator: Allocator, headers: []const Http.Header) !void {
|
||||||
self.req.headers.deinit();
|
self.req.headers.deinit();
|
||||||
|
|
||||||
@@ -672,6 +756,14 @@ pub const Transfer = struct {
|
|||||||
self.deinit();
|
self.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// abortAuthChallenge is called when an auth chanllenge interception is
|
||||||
|
// abort. We don't call self.client.endTransfer here b/c it has been done
|
||||||
|
// before interception process.
|
||||||
|
pub fn abortAuthChallenge(self: *Transfer) void {
|
||||||
|
self.client.requestFailed(self, error.AbortAuthChallenge);
|
||||||
|
self.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
// redirectionCookies manages cookies during redirections handled by Curl.
|
// redirectionCookies manages cookies during redirections handled by Curl.
|
||||||
// It sets the cookies from the current response to the cookie jar.
|
// It sets the cookies from the current response to the cookie jar.
|
||||||
// It also immediately sets cookies for the following request.
|
// It also immediately sets cookies for the following request.
|
||||||
@@ -797,20 +889,44 @@ pub const Transfer = struct {
|
|||||||
transfer._redirecting = false;
|
transfer._redirecting = false;
|
||||||
|
|
||||||
if (status == 401 or status == 407) {
|
if (status == 401 or status == 407) {
|
||||||
transfer._forbidden = true;
|
// The auth challenge must be parsed from a following
|
||||||
|
// WWW-Authenticate or Proxy-Authenticate header.
|
||||||
|
transfer._auth_challenge = .{
|
||||||
|
.status = status,
|
||||||
|
.source = undefined,
|
||||||
|
.scheme = undefined,
|
||||||
|
.realm = undefined,
|
||||||
|
};
|
||||||
return buf_len;
|
return buf_len;
|
||||||
}
|
}
|
||||||
transfer._forbidden = false;
|
transfer._auth_challenge = null;
|
||||||
|
|
||||||
transfer.bytes_received = buf_len;
|
transfer.bytes_received = buf_len;
|
||||||
return buf_len;
|
return buf_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transfer._redirecting == false and transfer._forbidden == false) {
|
if (transfer._redirecting == false and transfer._auth_challenge != null) {
|
||||||
transfer.bytes_received += buf_len;
|
transfer.bytes_received += buf_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf_len != 2) {
|
if (buf_len != 2) {
|
||||||
|
if (transfer._auth_challenge != null) {
|
||||||
|
// try to parse auth challenge.
|
||||||
|
if (std.ascii.startsWithIgnoreCase(header, "WWW-Authenticate") or
|
||||||
|
std.ascii.startsWithIgnoreCase(header, "Proxy-Authenticate"))
|
||||||
|
{
|
||||||
|
const ac = AuthChallenge.parse(
|
||||||
|
transfer._auth_challenge.?.status,
|
||||||
|
header,
|
||||||
|
) catch |err| {
|
||||||
|
// We can't parse the auth challenge
|
||||||
|
log.err(.http, "parse auth challenge", .{ .err = err, .header = header });
|
||||||
|
// Should we cancel the request? I don't think so.
|
||||||
|
return buf_len;
|
||||||
|
};
|
||||||
|
transfer._auth_challenge = ac;
|
||||||
|
}
|
||||||
|
}
|
||||||
return buf_len;
|
return buf_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,7 +954,7 @@ pub const Transfer = struct {
|
|||||||
return c.CURL_WRITEFUNC_ERROR;
|
return c.CURL_WRITEFUNC_ERROR;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (transfer._redirecting) {
|
if (transfer._redirecting or transfer._auth_challenge != null) {
|
||||||
return chunk_len;
|
return chunk_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ pub const Notification = struct {
|
|||||||
http_request_start: List = .{},
|
http_request_start: List = .{},
|
||||||
http_request_intercept: List = .{},
|
http_request_intercept: List = .{},
|
||||||
http_request_done: List = .{},
|
http_request_done: List = .{},
|
||||||
|
http_request_auth_required: List = .{},
|
||||||
http_response_data: List = .{},
|
http_response_data: List = .{},
|
||||||
http_response_header_done: List = .{},
|
http_response_header_done: List = .{},
|
||||||
notification_created: List = .{},
|
notification_created: List = .{},
|
||||||
@@ -77,6 +78,7 @@ pub const Notification = struct {
|
|||||||
http_request_fail: *const RequestFail,
|
http_request_fail: *const RequestFail,
|
||||||
http_request_start: *const RequestStart,
|
http_request_start: *const RequestStart,
|
||||||
http_request_intercept: *const RequestIntercept,
|
http_request_intercept: *const RequestIntercept,
|
||||||
|
http_request_auth_required: *const RequestAuthRequired,
|
||||||
http_request_done: *const RequestDone,
|
http_request_done: *const RequestDone,
|
||||||
http_response_data: *const ResponseData,
|
http_response_data: *const ResponseData,
|
||||||
http_response_header_done: *const ResponseHeaderDone,
|
http_response_header_done: *const ResponseHeaderDone,
|
||||||
@@ -106,6 +108,11 @@ pub const Notification = struct {
|
|||||||
wait_for_interception: *bool,
|
wait_for_interception: *bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const RequestAuthRequired = struct {
|
||||||
|
transfer: *Transfer,
|
||||||
|
wait_for_interception: *bool,
|
||||||
|
};
|
||||||
|
|
||||||
pub const ResponseData = struct {
|
pub const ResponseData = struct {
|
||||||
data: []const u8,
|
data: []const u8,
|
||||||
transfer: *Transfer,
|
transfer: *Transfer,
|
||||||
|
|||||||
Reference in New Issue
Block a user