mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 08:18:59 +00:00
xhr: implement async http client
This commit is contained in:
@@ -5,6 +5,7 @@ const Console = @import("jsruntime").Console;
|
|||||||
const DOM = @import("dom/dom.zig");
|
const DOM = @import("dom/dom.zig");
|
||||||
const HTML = @import("html/html.zig");
|
const HTML = @import("html/html.zig");
|
||||||
const Events = @import("events/event.zig");
|
const Events = @import("events/event.zig");
|
||||||
|
const XHR = @import("xhr/xhr.zig");
|
||||||
|
|
||||||
pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
|
pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
|
||||||
|
|
||||||
@@ -14,4 +15,5 @@ pub const Interfaces = generate.Tuple(.{
|
|||||||
DOM.Interfaces,
|
DOM.Interfaces,
|
||||||
Events.Interfaces,
|
Events.Interfaces,
|
||||||
HTML.Interfaces,
|
HTML.Interfaces,
|
||||||
|
XHR.Interfaces,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const AttrTestExecFn = @import("dom/attribute.zig").testExecFn;
|
|||||||
const EventTargetTestExecFn = @import("dom/event_target.zig").testExecFn;
|
const EventTargetTestExecFn = @import("dom/event_target.zig").testExecFn;
|
||||||
const EventTestExecFn = @import("events/event.zig").testExecFn;
|
const EventTestExecFn = @import("events/event.zig").testExecFn;
|
||||||
const xhr = @import("xhr/xhr.zig");
|
const xhr = @import("xhr/xhr.zig");
|
||||||
|
const XHRTestExecFn = xhr.testExecFn;
|
||||||
|
|
||||||
pub const Types = jsruntime.reflect(apiweb.Interfaces);
|
pub const Types = jsruntime.reflect(apiweb.Interfaces);
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ fn testsAllExecFn(
|
|||||||
AttrTestExecFn,
|
AttrTestExecFn,
|
||||||
EventTargetTestExecFn,
|
EventTargetTestExecFn,
|
||||||
EventTestExecFn,
|
EventTestExecFn,
|
||||||
|
XHRTestExecFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline for (testFns) |testFn| {
|
inline for (testFns) |testFn| {
|
||||||
|
|||||||
121
src/xhr/xhr.zig
121
src/xhr/xhr.zig
@@ -1,20 +1,55 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jsruntime = @import("jsruntime");
|
||||||
|
const Case = jsruntime.test_utils.Case;
|
||||||
|
const checkCases = jsruntime.test_utils.checkCases;
|
||||||
const generate = @import("../generate.zig");
|
const generate = @import("../generate.zig");
|
||||||
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
|
||||||
|
|
||||||
|
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
||||||
|
const Callback = jsruntime.Callback;
|
||||||
const DOMError = @import("../netsurf.zig").DOMError;
|
const DOMError = @import("../netsurf.zig").DOMError;
|
||||||
|
|
||||||
|
const Loop = jsruntime.Loop;
|
||||||
|
const YieldImpl = Loop.Yield(XMLHttpRequest);
|
||||||
|
const Client = @import("../async/Client.zig");
|
||||||
|
|
||||||
// XHR interfaces
|
// XHR interfaces
|
||||||
// https://xhr.spec.whatwg.org/#interface-xmlhttprequest
|
// https://xhr.spec.whatwg.org/#interface-xmlhttprequest
|
||||||
pub const Interfaces = generate.Tuple(.{
|
pub const Interfaces = generate.Tuple(.{
|
||||||
XMLHttpRequestEventTarget,
|
XMLHttpRequestEventTarget,
|
||||||
XMLHttpRequestUpload,
|
XMLHttpRequestUpload,
|
||||||
|
XMLHttpRequest,
|
||||||
});
|
});
|
||||||
|
|
||||||
pub const XMLHttpRequestEventTarget = struct {
|
pub const XMLHttpRequestEventTarget = struct {
|
||||||
pub const prototype = *EventTarget;
|
pub const prototype = *EventTarget;
|
||||||
pub const mem_guarantied = true;
|
pub const mem_guarantied = true;
|
||||||
|
|
||||||
|
onloadstart_cbk: ?Callback = null,
|
||||||
|
onprogress_cbk: ?Callback = null,
|
||||||
|
onabort_cbk: ?Callback = null,
|
||||||
|
onload_cbk: ?Callback = null,
|
||||||
|
ontimeout_cbk: ?Callback = null,
|
||||||
|
onloadend_cbk: ?Callback = null,
|
||||||
|
|
||||||
|
pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, handler: Callback) void {
|
||||||
|
self.onloadstart_cbk = handler;
|
||||||
|
}
|
||||||
|
pub fn set_onprogress(self: *XMLHttpRequestEventTarget, handler: Callback) void {
|
||||||
|
self.onprogress_cbk = handler;
|
||||||
|
}
|
||||||
|
pub fn set_onabort(self: *XMLHttpRequestEventTarget, handler: Callback) void {
|
||||||
|
self.onabort = handler;
|
||||||
|
}
|
||||||
|
pub fn set_onload(self: *XMLHttpRequestEventTarget, handler: Callback) void {
|
||||||
|
self.onload = handler;
|
||||||
|
}
|
||||||
|
pub fn set_ontimeout(self: *XMLHttpRequestEventTarget, handler: Callback) void {
|
||||||
|
self.ontimeout = handler;
|
||||||
|
}
|
||||||
|
pub fn set_onloadend(self: *XMLHttpRequestEventTarget, handler: Callback) void {
|
||||||
|
self.onloadend = handler;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const XMLHttpRequestUpload = struct {
|
pub const XMLHttpRequestUpload = struct {
|
||||||
@@ -26,17 +61,43 @@ pub const XMLHttpRequest = struct {
|
|||||||
pub const prototype = *XMLHttpRequestEventTarget;
|
pub const prototype = *XMLHttpRequestEventTarget;
|
||||||
pub const mem_guarantied = true;
|
pub const mem_guarantied = true;
|
||||||
|
|
||||||
pub fn constructor() XMLHttpRequest {
|
|
||||||
return XMLHttpRequest{};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const UNSENT: u16 = 0;
|
pub const UNSENT: u16 = 0;
|
||||||
pub const OPENED: u16 = 1;
|
pub const OPENED: u16 = 1;
|
||||||
pub const HEADERS_RECEIVED: u16 = 2;
|
pub const HEADERS_RECEIVED: u16 = 2;
|
||||||
pub const LOADING: u16 = 3;
|
pub const LOADING: u16 = 3;
|
||||||
pub const DONE: u16 = 4;
|
pub const DONE: u16 = 4;
|
||||||
|
|
||||||
|
cli: Client,
|
||||||
|
impl: YieldImpl,
|
||||||
|
|
||||||
readyState: u16 = UNSENT,
|
readyState: u16 = UNSENT,
|
||||||
|
uri: std.Uri,
|
||||||
|
headers: std.http.Headers,
|
||||||
|
asyn: bool = true,
|
||||||
|
err: ?anyerror = null,
|
||||||
|
|
||||||
|
pub fn constructor(alloc: std.mem.Allocator, loop: *Loop) !*XMLHttpRequest {
|
||||||
|
var req = try alloc.create(XMLHttpRequest);
|
||||||
|
req.* = XMLHttpRequest{
|
||||||
|
.headers = .{ .allocator = alloc, .owned = false },
|
||||||
|
.impl = undefined,
|
||||||
|
.uri = undefined,
|
||||||
|
// TODO retrieve the HTTP client globally to reuse existing connections.
|
||||||
|
.cli = .{
|
||||||
|
.allocator = alloc,
|
||||||
|
.loop = loop,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
req.impl = YieldImpl.init(loop, req);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *XMLHttpRequest, alloc: std.mem.Allocator) void {
|
||||||
|
self.headers.deinit();
|
||||||
|
// TODO the client must be shared between requests.
|
||||||
|
self.cli.deinit();
|
||||||
|
alloc.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_readyState(self: *XMLHttpRequest) u16 {
|
pub fn get_readyState(self: *XMLHttpRequest) u16 {
|
||||||
return self.readyState;
|
return self.readyState;
|
||||||
@@ -50,9 +111,6 @@ pub const XMLHttpRequest = struct {
|
|||||||
username: ?[]const u8,
|
username: ?[]const u8,
|
||||||
password: ?[]const u8,
|
password: ?[]const u8,
|
||||||
) !void {
|
) !void {
|
||||||
_ = self;
|
|
||||||
_ = url;
|
|
||||||
_ = asyn;
|
|
||||||
_ = username;
|
_ = username;
|
||||||
_ = password;
|
_ = password;
|
||||||
|
|
||||||
@@ -61,6 +119,9 @@ pub const XMLHttpRequest = struct {
|
|||||||
// "InvalidStateError" DOMException.
|
// "InvalidStateError" DOMException.
|
||||||
|
|
||||||
try validMethod(method);
|
try validMethod(method);
|
||||||
|
|
||||||
|
self.uri = try std.Uri.parse(url);
|
||||||
|
self.asyn = if (asyn) |b| b else true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const methods = [_][]const u8{ "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT" };
|
const methods = [_][]const u8{ "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT" };
|
||||||
@@ -82,4 +143,48 @@ pub const XMLHttpRequest = struct {
|
|||||||
// If method is not a method, then throw a "SyntaxError" DOMException.
|
// If method is not a method, then throw a "SyntaxError" DOMException.
|
||||||
return DOMError.Syntax;
|
return DOMError.Syntax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn _send(self: *XMLHttpRequest) void {
|
||||||
|
self.impl.yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn onerr(self: *XMLHttpRequest, err: anyerror) void {
|
||||||
|
self.err = err;
|
||||||
|
self.readyState = DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn onYield(self: *XMLHttpRequest, err: ?anyerror) void {
|
||||||
|
if (err) |e| return self.onerr(e);
|
||||||
|
var req = self.cli.open(.GET, self.uri, self.headers, .{}) catch |e| return self.onerr(e);
|
||||||
|
defer req.deinit();
|
||||||
|
|
||||||
|
self.readyState = OPENED;
|
||||||
|
|
||||||
|
req.send(.{}) catch |e| return self.onerr(e);
|
||||||
|
req.finish() catch |e| return self.onerr(e);
|
||||||
|
req.wait() catch |e| return self.onerr(e);
|
||||||
|
self.readyState = HEADERS_RECEIVED;
|
||||||
|
self.readyState = LOADING;
|
||||||
|
self.readyState = DONE;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn testExecFn(
|
||||||
|
_: std.mem.Allocator,
|
||||||
|
js_env: *jsruntime.Env,
|
||||||
|
) anyerror!void {
|
||||||
|
var send = [_]Case{
|
||||||
|
.{ .src =
|
||||||
|
\\var nb = 0; var evt;
|
||||||
|
\\function cbk(event) {
|
||||||
|
\\ evt = event;
|
||||||
|
\\ nb ++;
|
||||||
|
\\}
|
||||||
|
, .ex = "undefined" },
|
||||||
|
.{ .src = "const req = new XMLHttpRequest();", .ex = "undefined" },
|
||||||
|
.{ .src = "req.onload = cbk; true;", .ex = "true" },
|
||||||
|
.{ .src = "req.open('GET', 'https://w3.org');", .ex = "undefined" },
|
||||||
|
.{ .src = "req.send();", .ex = "undefined" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &send);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user