diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index 7d0055109..c4dacf90c 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -405,6 +405,20 @@ pub fn send(self: *XMLHttpRequest, body_: ?BodyInit, exec_: *const Execution) !v self._response_url = self._url; self._response_len = resp.body.items.len; + for (resp.headers) |hdr| { + if (std.ascii.eqlIgnoreCase(hdr.name, "content-type")) { + self.applyContentType(hdr.value) catch |e| { + log.info(.http, "invalid content type", .{ + .content_Type = hdr.value, + .err = e, + .url = self._url, + }); + }; + break; + } + } + try self.applyResponseHeaders(.{ .list = .{ .list = resp.headers } }); + try self._response_data.appendSlice(self._arena.allocator(), resp.body.items); var ls: js.Local.Scope = undefined; @@ -635,6 +649,22 @@ fn getResponseXML(self: *XMLHttpRequest, exec: *const Execution) !?*Node.Documen } } +fn applyContentType(self: *XMLHttpRequest, content_type: []const u8) !void { + self._response_mime = try Mime.parse(content_type); + self._response_mime_raw = try self._arena.dupe(u8, std.mem.trim(u8, content_type, &std.ascii.whitespace)); +} + +fn applyResponseHeaders(self: *XMLHttpRequest, headers: http.HeaderIterator) !void { + var it = headers; + while (it.next()) |hdr| { + if (Headers.isForbiddenResponseHeaderName(hdr.name)) { + continue; + } + const joined = try std.fmt.allocPrint(self._arena.allocator(), "{s}: {s}", .{ hdr.name, hdr.value }); + try self._response_headers.append(self._arena.allocator(), joined); + } +} + fn httpHeaderDoneCallback(transfer: *Transfer) !Transfer.HeaderResult { const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.req.ctx)); @@ -647,7 +677,7 @@ fn httpHeaderDoneCallback(transfer: *Transfer) !Transfer.HeaderResult { } if (transfer.contentType()) |ct| { - self._response_mime = Mime.parse(ct) catch |e| { + self.applyContentType(ct) catch |e| { log.info(.http, "invalid content type", .{ .content_Type = ct, .err = e, @@ -655,17 +685,9 @@ fn httpHeaderDoneCallback(transfer: *Transfer) !Transfer.HeaderResult { }); return .abort; }; - self._response_mime_raw = try self._arena.dupe(u8, std.mem.trim(u8, ct, &std.ascii.whitespace)); } - var it = transfer.responseHeaderIterator(); - while (it.next()) |hdr| { - if (Headers.isForbiddenResponseHeaderName(hdr.name)) { - continue; - } - const joined = try std.fmt.allocPrint(self._arena.allocator(), "{s}: {s}", .{ hdr.name, hdr.value }); - try self._response_headers.append(self._arena.allocator(), joined); - } + try self.applyResponseHeaders(transfer.responseHeaderIterator()); self._response_status = transfer.responseStatus().?; if (transfer.statusText()) |st| { diff --git a/src/network/HttpClient.zig b/src/network/HttpClient.zig index 30f772e90..9022edcb3 100644 --- a/src/network/HttpClient.zig +++ b/src/network/HttpClient.zig @@ -1436,6 +1436,7 @@ const SyncContext = struct { status: u16 = 0, status_text: http.StatusText = .{}, body: std.ArrayList(u8), + headers: std.ArrayList(http.Header) = .empty, // Acquired on the first byte we have to buffer, so a bodyless response // never takes one. Ownership moves to the SyncResponse. @@ -1447,8 +1448,18 @@ const SyncContext = struct { self.status = transfer.responseStatus().?; self.status_text = transfer.res.status_text; const body_len = transfer.bodyLen(); + const allocator = try self.bodyAllocator(body_len); + + var it = transfer.responseHeaderIterator(); + while (it.next()) |hdr| { + try self.headers.append(allocator, .{ + .name = try allocator.dupe(u8, hdr.name), + .value = try allocator.dupe(u8, hdr.value), + }); + } + if (body_len > 0) { - try self.body.ensureTotalCapacityPrecise(try self.bodyAllocator(body_len), body_len); + try self.body.ensureTotalCapacityPrecise(allocator, body_len); } return .proceed; } @@ -2115,6 +2126,7 @@ const SyncResponse = struct { status: u16, body: std.ArrayList(u8), status_text: http.StatusText, + headers: []const http.Header, // Owns `body`. Null when the response had nothing to buffer. Callers that // keep `body` past this call take the arena instead of releasing it. @@ -2676,6 +2688,7 @@ pub const Transfer = struct { .status = sync_ctx.status, .status_text = sync_ctx.status_text, .body = sync_ctx.body, + .headers = sync_ctx.headers.items, .arena = sync_ctx.arena, }, .err => |e| return e,