mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
http: refacto http header parsing
This commit is contained in:
@@ -309,6 +309,9 @@ fn makeRequest(self: *Client, handle: *Handle, transfer: *Transfer) !void {
|
||||
transfer._handle = handle;
|
||||
errdefer transfer.deinit();
|
||||
|
||||
// Store the proxy's information in transfer to ease headers parsing.
|
||||
transfer._use_proxy = conn.opts.use_proxy;
|
||||
|
||||
try conn.setURL(req.url);
|
||||
try conn.setMethod(req.method);
|
||||
if (req.body) |b| {
|
||||
@@ -574,6 +577,13 @@ pub const Transfer = struct {
|
||||
|
||||
_redirecting: bool = false,
|
||||
|
||||
// use_proxy is set when the transfer has been associated to a given
|
||||
// connection in makeRequest().
|
||||
_use_proxy: bool = undefined,
|
||||
|
||||
// stateful variables used to parse responses headers.
|
||||
_resp_header_status: enum { empty, first, next, end } = .empty,
|
||||
|
||||
fn deinit(self: *Transfer) void {
|
||||
self.req.headers.deinit();
|
||||
if (self._handle) |handle| {
|
||||
@@ -684,88 +694,101 @@ pub const Transfer = struct {
|
||||
|
||||
const header = buffer[0 .. buf_len - 2];
|
||||
|
||||
if (transfer.response_header == null) {
|
||||
if (transfer._redirecting and buf_len == 2) {
|
||||
// parse and set cookies for the redirection.
|
||||
redirectionCookies(transfer, easy) catch |err| {
|
||||
log.debug(.http, "redirection cookies", .{ .err = err });
|
||||
// transition the status dependending the previous one.
|
||||
transfer._resp_header_status = switch (transfer._resp_header_status) {
|
||||
.empty => .first,
|
||||
.first => .next,
|
||||
.next => .next,
|
||||
.end => .first,
|
||||
};
|
||||
|
||||
// mark the end of parsing headers
|
||||
if (buf_len == 2) transfer._resp_header_status = .end;
|
||||
|
||||
log.debug(.http, "header parsing", .{ .status = transfer._resp_header_status });
|
||||
|
||||
switch (transfer._resp_header_status) {
|
||||
.empty => unreachable,
|
||||
.first => {
|
||||
if (buf_len < 13) {
|
||||
log.debug(.http, "invalid response line", .{ .line = header });
|
||||
return 0;
|
||||
}
|
||||
const version_start: usize = if (header[5] == '2') 7 else 9;
|
||||
const version_end = version_start + 3;
|
||||
|
||||
// a bit silly, but it makes sure that we don't change the length check
|
||||
// above in a way that could break this.
|
||||
std.debug.assert(version_end < 13);
|
||||
|
||||
const status = std.fmt.parseInt(u16, header[version_start..version_end], 10) catch {
|
||||
log.debug(.http, "invalid status code", .{ .line = header });
|
||||
return 0;
|
||||
};
|
||||
return buf_len;
|
||||
}
|
||||
|
||||
if (buf_len < 13 or std.mem.startsWith(u8, header, "HTTP/") == false) {
|
||||
if (transfer._redirecting) {
|
||||
if (status >= 300 and status <= 399) {
|
||||
transfer._redirecting = true;
|
||||
return buf_len;
|
||||
}
|
||||
log.debug(.http, "invalid response line", .{ .line = header });
|
||||
return 0;
|
||||
}
|
||||
const version_start: usize = if (header[5] == '2') 7 else 9;
|
||||
const version_end = version_start + 3;
|
||||
transfer._redirecting = false;
|
||||
|
||||
// a bit silly, but it makes sure that we don't change the length check
|
||||
// above in a way that could break this.
|
||||
std.debug.assert(version_end < 13);
|
||||
var url: [*c]u8 = undefined;
|
||||
errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_EFFECTIVE_URL, &url)) catch |err| {
|
||||
log.err(.http, "failed to get URL", .{ .err = err });
|
||||
return 0;
|
||||
};
|
||||
|
||||
const status = std.fmt.parseInt(u16, header[version_start..version_end], 10) catch {
|
||||
log.debug(.http, "invalid status code", .{ .line = header });
|
||||
return 0;
|
||||
};
|
||||
transfer.response_header = .{
|
||||
.url = url,
|
||||
.status = status,
|
||||
};
|
||||
},
|
||||
.next => {},
|
||||
.end => {
|
||||
// If we are in a redirection, take care of cookies only.
|
||||
if (transfer._redirecting) {
|
||||
// parse and set cookies for the redirection.
|
||||
redirectionCookies(transfer, easy) catch |err| {
|
||||
log.debug(.http, "redirection cookies", .{ .err = err });
|
||||
return 0;
|
||||
};
|
||||
return buf_len;
|
||||
}
|
||||
|
||||
if (status >= 300 and status <= 399) {
|
||||
transfer._redirecting = true;
|
||||
return buf_len;
|
||||
}
|
||||
transfer._redirecting = false;
|
||||
if (getResponseHeader(easy, "content-type", 0)) |ct| {
|
||||
var hdr = &transfer.response_header.?;
|
||||
const value = ct.value;
|
||||
const len = @min(value.len, ResponseHeader.MAX_CONTENT_TYPE_LEN);
|
||||
hdr._content_type_len = len;
|
||||
@memcpy(hdr._content_type[0..len], value[0..len]);
|
||||
}
|
||||
|
||||
var url: [*c]u8 = undefined;
|
||||
errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_EFFECTIVE_URL, &url)) catch |err| {
|
||||
log.err(.http, "failed to get URL", .{ .err = err });
|
||||
return 0;
|
||||
};
|
||||
var i: usize = 0;
|
||||
while (true) {
|
||||
const ct = getResponseHeader(easy, "set-cookie", i);
|
||||
if (ct == null) break;
|
||||
transfer.req.cookie_jar.populateFromResponse(&transfer.uri, ct.?.value) catch |err| {
|
||||
log.err(.http, "set cookie", .{ .err = err, .req = transfer });
|
||||
};
|
||||
i += 1;
|
||||
if (i >= ct.?.amount) break;
|
||||
}
|
||||
|
||||
transfer.response_header = .{
|
||||
.url = url,
|
||||
.status = status,
|
||||
};
|
||||
transfer.bytes_received = buf_len;
|
||||
return buf_len;
|
||||
transfer.req.header_callback(transfer) catch |err| {
|
||||
log.err(.http, "header_callback", .{ .err = err, .req = transfer });
|
||||
// returning < buf_len terminates the request
|
||||
return 0;
|
||||
};
|
||||
|
||||
if (transfer.client.notification) |notification| {
|
||||
notification.dispatch(.http_response_header_done, &.{
|
||||
.transfer = transfer,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
transfer.bytes_received += buf_len;
|
||||
if (buf_len == 2) {
|
||||
if (getResponseHeader(easy, "content-type", 0)) |ct| {
|
||||
var hdr = &transfer.response_header.?;
|
||||
const value = ct.value;
|
||||
const len = @min(value.len, ResponseHeader.MAX_CONTENT_TYPE_LEN);
|
||||
hdr._content_type_len = len;
|
||||
@memcpy(hdr._content_type[0..len], value[0..len]);
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (true) {
|
||||
const ct = getResponseHeader(easy, "set-cookie", i);
|
||||
if (ct == null) break;
|
||||
transfer.req.cookie_jar.populateFromResponse(&transfer.uri, ct.?.value) catch |err| {
|
||||
log.err(.http, "set cookie", .{ .err = err, .req = transfer });
|
||||
};
|
||||
i += 1;
|
||||
if (i >= ct.?.amount) break;
|
||||
}
|
||||
|
||||
transfer.req.header_callback(transfer) catch |err| {
|
||||
log.err(.http, "header_callback", .{ .err = err, .req = transfer });
|
||||
// returning < buf_len terminates the request
|
||||
return 0;
|
||||
};
|
||||
|
||||
if (transfer.client.notification) |notification| {
|
||||
notification.dispatch(.http_response_header_done, &.{
|
||||
.transfer = transfer,
|
||||
});
|
||||
}
|
||||
}
|
||||
return buf_len;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user