@@ -25,29 +25,35 @@ const Page = @import("../page.zig").Page;
2525const Response = @import ("./Response.zig" );
2626
2727const Http = @import ("../../http/Http.zig" );
28- const HttpClient = @import ("../../http/Client.zig" );
29- const Mime = @import ("../mime.zig" ).Mime ;
3028
3129const v8 = @import ("v8" );
3230const Env = @import ("../env.zig" ).Env ;
3331
32+ const Headers = @import ("Headers.zig" );
33+ const HeadersInit = @import ("Headers.zig" ).HeadersInit ;
34+
3435pub const RequestInput = union (enum ) {
3536 string : []const u8 ,
36- request : Request ,
37+ request : * Request ,
3738};
3839
3940// https://developer.mozilla.org/en-US/docs/Web/API/RequestInit
4041pub const RequestInit = struct {
4142 method : ? []const u8 = null ,
4243 body : ? []const u8 = null ,
44+ integrity : ? []const u8 = null ,
45+ headers : ? HeadersInit = null ,
4346};
4447
4548// https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
4649const Request = @This ();
4750
4851method : Http.Method ,
4952url : [:0 ]const u8 ,
53+ headers : Headers ,
5054body : ? []const u8 ,
55+ body_used : bool = false ,
56+ integrity : []const u8 ,
5157
5258pub fn constructor (input : RequestInput , _options : ? RequestInit , page : * Page ) ! Request {
5359 const arena = page .arena ;
@@ -77,165 +83,115 @@ pub fn constructor(input: RequestInput, _options: ?RequestInit, page: *Page) !Re
7783 };
7884
7985 const body = if (options .body ) | body | try arena .dupe (u8 , body ) else null ;
86+ const integrity = if (options .integrity ) | integ | try arena .dupe (u8 , integ ) else "" ;
87+ const headers = if (options .headers ) | hdrs | try Headers .constructor (hdrs , page ) else Headers {};
8088
8189 return .{
8290 .method = method ,
8391 .url = url ,
92+ .headers = headers ,
8493 .body = body ,
94+ .integrity = integrity ,
8595 };
8696}
8797
88- pub fn get_url (self : * const Request ) []const u8 {
89- return self .url ;
98+ // pub fn get_body(self: *const Request) ?[]const u8 {
99+ // return self.body;
100+ // }
101+
102+ pub fn get_bodyUsed (self : * const Request ) bool {
103+ return self .body_used ;
104+ }
105+
106+ pub fn get_headers (self : * Request ) * Headers {
107+ return & self .headers ;
90108}
91109
110+ pub fn get_integrity (self : * const Request ) []const u8 {
111+ return self .integrity ;
112+ }
113+
114+ // TODO: If we ever support the Navigation API, we need isHistoryNavigation
115+ // https://developer.mozilla.org/en-US/docs/Web/API/Request/isHistoryNavigation
116+
92117pub fn get_method (self : * const Request ) []const u8 {
93118 return @tagName (self .method );
94119}
95120
96- // pub fn get_body (self: *const Request) ? []const u8 {
97- // return self.body ;
98- // }
121+ pub fn get_url (self : * const Request ) []const u8 {
122+ return self .url ;
123+ }
99124
100- const FetchContext = struct {
101- arena : std.mem.Allocator ,
102- js_ctx : * Env.JsContext ,
103- promise_resolver : v8 .Persistent (v8 .PromiseResolver ),
104-
105- method : Http.Method ,
106- url : []const u8 ,
107- body : std .ArrayListUnmanaged (u8 ) = .empty ,
108- headers : std .ArrayListUnmanaged ([]const u8 ) = .empty ,
109- status : u16 = 0 ,
110- mime : ? Mime = null ,
111- transfer : ? * HttpClient.Transfer = null ,
112-
113- /// This effectively takes ownership of the FetchContext.
114- ///
115- /// We just return the underlying slices used for `headers`
116- /// and for `body` here to avoid an allocation.
117- pub fn toResponse (self : * const FetchContext ) ! Response {
118- return Response {
119- .status = self .status ,
120- .headers = self .headers .items ,
121- .mime = self .mime ,
122- .body = self .body .items ,
123- };
125+ pub fn _clone (self : * Request , page : * Page ) ! Request {
126+ // Not allowed to clone if the body was used.
127+ if (self .body_used ) {
128+ return error .TypeError ;
124129 }
125- };
126130
127- // https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch
128- pub fn fetch (input : RequestInput , options : ? RequestInit , page : * Page ) ! Env.Promise {
129131 const arena = page .arena ;
130132
131- const req = try Request .constructor (input , options , page );
133+ return Request {
134+ .body = if (self .body ) | body | try arena .dupe (u8 , body ) else null ,
135+ .body_used = self .body_used ,
136+ .headers = try self .headers .clone (arena ),
137+ .method = self .method ,
138+ .integrity = try arena .dupe (u8 , self .integrity ),
139+ .url = try arena .dupeZ (u8 , self .url ),
140+ };
141+ }
142+
143+ pub fn _bytes (self : * Response , page : * Page ) ! Env.Promise {
144+ if (self .body_used ) {
145+ return error .TypeError ;
146+ }
132147
133148 const resolver = Env.PromiseResolver {
134149 .js_context = page .main_context ,
135150 .resolver = v8 .PromiseResolver .init (page .main_context .v8_context ),
136151 };
137152
138- var headers = try Http .Headers .init ();
139- try page .requestCookie (.{}).headersForRequest (arena , req .url , & headers );
140-
141- const fetch_ctx = try arena .create (FetchContext );
142- fetch_ctx .* = .{
143- .arena = arena ,
144- .js_ctx = page .main_context ,
145- .promise_resolver = v8 .Persistent (v8 .PromiseResolver ).init (
146- page .main_context .isolate ,
147- resolver .resolver ,
148- ),
149- .method = req .method ,
150- .url = req .url ,
151- };
153+ try resolver .resolve (self .body );
154+ self .body_used = true ;
155+ return resolver .promise ();
156+ }
152157
153- try page .http_client .request (.{
154- .ctx = @ptrCast (fetch_ctx ),
155- .url = req .url ,
156- .method = req .method ,
157- .headers = headers ,
158- .body = req .body ,
159- .cookie_jar = page .cookie_jar ,
160- .resource_type = .fetch ,
158+ pub fn _json (self : * Response , page : * Page ) ! Env.Promise {
159+ if (self .body_used ) {
160+ return error .TypeError ;
161+ }
161162
162- . start_callback = struct {
163- fn startCallback ( transfer : * HttpClient.Transfer ) ! void {
164- const self : * FetchContext = @alignCast ( @ptrCast ( transfer . ctx ));
165- log . debug ( .http , "request start" , .{ . method = self . method , . url = self . url , . source = "fetch" }) ;
163+ const resolver = Env.PromiseResolver {
164+ . js_context = page . main_context ,
165+ . resolver = v8 . PromiseResolver . init ( page . main_context . v8_context ),
166+ } ;
166167
167- self .transfer = transfer ;
168- }
169- }.startCallback ,
170- .header_callback = struct {
171- fn headerCallback (transfer : * HttpClient.Transfer ) ! void {
172- const self : * FetchContext = @alignCast (@ptrCast (transfer .ctx ));
173-
174- const header = & transfer .response_header .? ;
175-
176- log .debug (.http , "request header" , .{
177- .source = "fetch" ,
178- .method = self .method ,
179- .url = self .url ,
180- .status = header .status ,
181- });
182-
183- if (header .contentType ()) | ct | {
184- self .mime = Mime .parse (ct ) catch {
185- return error .MimeParsing ;
186- };
187- }
168+ const p = std .json .parseFromSliceLeaky (
169+ std .json .Value ,
170+ page .arena ,
171+ self .body ,
172+ .{},
173+ ) catch | e | {
174+ log .warn (.browser , "invalid json" , .{ .err = e , .source = "Request" });
175+ return error .SyntaxError ;
176+ };
188177
189- var it = transfer .responseHeaderIterator ();
190- while (it .next ()) | hdr | {
191- const joined = try std .fmt .allocPrint (self .arena , "{s}: {s}" , .{ hdr .name , hdr .value });
192- try self .headers .append (self .arena , joined );
193- }
178+ try resolver .resolve (p );
179+ self .body_used = true ;
180+ return resolver .promise ();
181+ }
194182
195- self .status = header .status ;
196- }
197- }.headerCallback ,
198- .data_callback = struct {
199- fn dataCallback (transfer : * HttpClient.Transfer , data : []const u8 ) ! void {
200- const self : * FetchContext = @alignCast (@ptrCast (transfer .ctx ));
201- try self .body .appendSlice (self .arena , data );
202- }
203- }.dataCallback ,
204- .done_callback = struct {
205- fn doneCallback (ctx : * anyopaque ) ! void {
206- const self : * FetchContext = @alignCast (@ptrCast (ctx ));
207-
208- log .info (.http , "request complete" , .{
209- .source = "fetch" ,
210- .method = self .method ,
211- .url = self .url ,
212- .status = self .status ,
213- });
214-
215- const response = try self .toResponse ();
216- const promise_resolver : Env.PromiseResolver = .{
217- .js_context = self .js_ctx ,
218- .resolver = self .promise_resolver .castToPromiseResolver (),
219- };
220-
221- try promise_resolver .resolve (response );
222- }
223- }.doneCallback ,
224- .error_callback = struct {
225- fn errorCallback (ctx : * anyopaque , err : anyerror ) void {
226- const self : * FetchContext = @alignCast (@ptrCast (ctx ));
227-
228- self .transfer = null ;
229- const promise_resolver : Env.PromiseResolver = .{
230- .js_context = self .js_ctx ,
231- .resolver = self .promise_resolver .castToPromiseResolver (),
232- };
233-
234- promise_resolver .reject (@errorName (err )) catch unreachable ;
235- }
236- }.errorCallback ,
237- });
183+ pub fn _text (self : * Response , page : * Page ) ! Env.Promise {
184+ if (self .body_used ) {
185+ return error .TypeError ;
186+ }
187+
188+ const resolver = Env.PromiseResolver {
189+ .js_context = page .main_context ,
190+ .resolver = v8 .PromiseResolver .init (page .main_context .v8_context ),
191+ };
238192
193+ try resolver .resolve (self .body );
194+ self .body_used = true ;
239195 return resolver .promise ();
240196}
241197
0 commit comments