DoxigAlpha

parse

Function parameters

Parameters

#
bytes:[]const u8

A Least-Recently-Used cache of open connections to be reused.

Types

#
ConnectionPool
A Least-Recently-Used cache of open connections to be reused.

Release all associated resources with the client.

Functions

#
deinit
Release all associated resources with the client.
initDefaultProxies
Populates `http_proxy` and `https_proxy` via standard proxy environment variables.
connectTcp
Reuses a `Connection` if one matching `host` and `port` is already open.
connectUnix
Connect to `path` as a unix domain socket.
connectProxied
Connect to `proxied_host:proxied_port` using the specified proxy with HTTP
connect
Connect to `host:port` using the specified protocol.
request
Open a connection to the host specified by `uri` and prepare to send a HTTP request.
fetch
Perform a one-shot HTTP request with the provided options.

Error sets in this namespace

Error Sets

#

= std.options.http_disable_tls

Values

#
disable_tls
= std.options.http_disable_tls

Source

Implementation

#
pub fn parse(bytes: []const u8) ParseError!Head {
    var res: Head = .{
        .bytes = bytes,
        .status = undefined,
        .reason = undefined,
        .version = undefined,
        .keep_alive = false,
    };
    var it = mem.splitSequence(u8, bytes, "\r\n");

    const first_line = it.first();
    if (first_line.len < 12) return error.HttpHeadersInvalid;

    const version: http.Version = switch (int64(first_line[0..8])) {
        int64("HTTP/1.0") => .@"HTTP/1.0",
        int64("HTTP/1.1") => .@"HTTP/1.1",
        else => return error.HttpHeadersInvalid,
    };
    if (first_line[8] != ' ') return error.HttpHeadersInvalid;
    const status: http.Status = @enumFromInt(parseInt3(first_line[9..12]));
    const reason = mem.trimLeft(u8, first_line[12..], " ");

    res.version = version;
    res.status = status;
    res.reason = reason;
    res.keep_alive = switch (version) {
        .@"HTTP/1.0" => false,
        .@"HTTP/1.1" => true,
    };

    while (it.next()) |line| {
        if (line.len == 0) return res;
        switch (line[0]) {
            ' ', '\t' => return error.HttpHeaderContinuationsUnsupported,
            else => {},
        }

        var line_it = mem.splitScalar(u8, line, ':');
        const header_name = line_it.next().?;
        const header_value = mem.trim(u8, line_it.rest(), " \t");
        if (header_name.len == 0) return error.HttpHeadersInvalid;

        if (std.ascii.eqlIgnoreCase(header_name, "connection")) {
            res.keep_alive = !std.ascii.eqlIgnoreCase(header_value, "close");
        } else if (std.ascii.eqlIgnoreCase(header_name, "content-type")) {
            res.content_type = header_value;
        } else if (std.ascii.eqlIgnoreCase(header_name, "location")) {
            res.location = header_value;
        } else if (std.ascii.eqlIgnoreCase(header_name, "content-disposition")) {
            res.content_disposition = header_value;
        } else if (std.ascii.eqlIgnoreCase(header_name, "transfer-encoding")) {
            // Transfer-Encoding: second, first
            // Transfer-Encoding: deflate, chunked
            var iter = mem.splitBackwardsScalar(u8, header_value, ',');

            const first = iter.first();
            const trimmed_first = mem.trim(u8, first, " ");

            var next: ?[]const u8 = first;
            if (std.meta.stringToEnum(http.TransferEncoding, trimmed_first)) |transfer| {
                if (res.transfer_encoding != .none) return error.HttpHeadersInvalid; // we already have a transfer encoding
                res.transfer_encoding = transfer;

                next = iter.next();
            }

            if (next) |second| {
                const trimmed_second = mem.trim(u8, second, " ");

                if (http.ContentEncoding.fromString(trimmed_second)) |transfer| {
                    if (res.content_encoding != .identity) return error.HttpHeadersInvalid; // double compression is not supported
                    res.content_encoding = transfer;
                } else {
                    return error.HttpTransferEncodingUnsupported;
                }
            }

            if (iter.next()) |_| return error.HttpTransferEncodingUnsupported;
        } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) {
            const content_length = std.fmt.parseInt(u64, header_value, 10) catch return error.InvalidContentLength;

            if (res.content_length != null and res.content_length != content_length) return error.HttpHeadersInvalid;

            res.content_length = content_length;
        } else if (std.ascii.eqlIgnoreCase(header_name, "content-encoding")) {
            if (res.content_encoding != .identity) return error.HttpHeadersInvalid;

            const trimmed = mem.trim(u8, header_value, " ");

            if (http.ContentEncoding.fromString(trimmed)) |ce| {
                res.content_encoding = ce;
            } else {
                return error.HttpContentEncodingUnsupported;
            }
        }
    }
    return error.HttpHeadersInvalid; // missing empty line
}