pub const Client = @import("http/Client.zig"); pub const Server = @import("http/Server.zig"); pub const protocol = @import("http/protocol.zig"); const headers = @import("http/Headers.zig"); pub const Headers = headers.Headers; pub const Field = headers.Field; pub const Version = enum { @"HTTP/1.0", @"HTTP/1.1", }; /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods /// https://datatracker.ietf.org/doc/html/rfc7231#section-4 Initial definition /// https://datatracker.ietf.org/doc/html/rfc5789#section-2 PATCH pub const Method = enum { GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH, /// Returns true if a request of this method is allowed to have a body /// Actual behavior from servers may vary and should still be checked pub fn requestHasBody(self: Method) bool { return switch (self) { .POST, .PUT, .PATCH => true, .GET, .HEAD, .DELETE, .CONNECT, .OPTIONS, .TRACE => false, }; } /// Returns true if a response to this method is allowed to have a body /// Actual behavior from clients may vary and should still be checked pub fn responseHasBody(self: Method) bool { return switch (self) { .GET, .POST, .DELETE, .CONNECT, .OPTIONS, .PATCH => true, .HEAD, .PUT, .TRACE => false, }; } /// An HTTP method is safe if it doesn't alter the state of the server. /// https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1 pub fn safe(self: Method) bool { return switch (self) { .GET, .HEAD, .OPTIONS, .TRACE => true, .POST, .PUT, .DELETE, .CONNECT, .PATCH => false, }; } /// An HTTP method is idempotent if an identical request can be made once or several times in a row with the same effect while leaving the server in the same state. /// https://developer.mozilla.org/en-US/docs/Glossary/Idempotent /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.2 pub fn idempotent(self: Method) bool { return switch (self) { .GET, .HEAD, .PUT, .DELETE, .OPTIONS, .TRACE => true, .CONNECT, .POST, .PATCH => false, }; } /// A cacheable response is an HTTP response that can be cached, that is stored to be retrieved and used later, saving a new request to the server. /// https://developer.mozilla.org/en-US/docs/Glossary/cacheable /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.3 pub fn cacheable(self: Method) bool { return switch (self) { .GET, .HEAD => true, .POST, .PUT, .DELETE, .CONNECT, .OPTIONS, .TRACE, .PATCH => false, }; } }; /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status pub const Status = enum(u10) { @"continue" = 100, // RFC7231, Section 6.2.1 switching_protocols = 101, // RFC7231, Section 6.2.2 processing = 102, // RFC2518 early_hints = 103, // RFC8297 ok = 200, // RFC7231, Section 6.3.1 created = 201, // RFC7231, Section 6.3.2 accepted = 202, // RFC7231, Section 6.3.3 non_authoritative_info = 203, // RFC7231, Section 6.3.4 no_content = 204, // RFC7231, Section 6.3.5 reset_content = 205, // RFC7231, Section 6.3.6 partial_content = 206, // RFC7233, Section 4.1 multi_status = 207, // RFC4918 already_reported = 208, // RFC5842 im_used = 226, // RFC3229 multiple_choice = 300, // RFC7231, Section 6.4.1 moved_permanently = 301, // RFC7231, Section 6.4.2 found = 302, // RFC7231, Section 6.4.3 see_other = 303, // RFC7231, Section 6.4.4 not_modified = 304, // RFC7232, Section 4.1 use_proxy = 305, // RFC7231, Section 6.4.5 temporary_redirect = 307, // RFC7231, Section 6.4.7 permanent_redirect = 308, // RFC7538 bad_request = 400, // RFC7231, Section 6.5.1 unauthorized = 401, // RFC7235, Section 3.1 payment_required = 402, // RFC7231, Section 6.5.2 forbidden = 403, // RFC7231, Section 6.5.3 not_found = 404, // RFC7231, Section 6.5.4 method_not_allowed = 405, // RFC7231, Section 6.5.5 not_acceptable = 406, // RFC7231, Section 6.5.6 proxy_auth_required = 407, // RFC7235, Section 3.2 request_timeout = 408, // RFC7231, Section 6.5.7 conflict = 409, // RFC7231, Section 6.5.8 gone = 410, // RFC7231, Section 6.5.9 length_required = 411, // RFC7231, Section 6.5.10 precondition_failed = 412, // RFC7232, Section 4.2][RFC8144, Section 3.2 payload_too_large = 413, // RFC7231, Section 6.5.11 uri_too_long = 414, // RFC7231, Section 6.5.12 unsupported_media_type = 415, // RFC7231, Section 6.5.13][RFC7694, Section 3 range_not_satisfiable = 416, // RFC7233, Section 4.4 expectation_failed = 417, // RFC7231, Section 6.5.14 teapot = 418, // RFC 7168, 2.3.3 misdirected_request = 421, // RFC7540, Section 9.1.2 unprocessable_entity = 422, // RFC4918 locked = 423, // RFC4918 failed_dependency = 424, // RFC4918 too_early = 425, // RFC8470 upgrade_required = 426, // RFC7231, Section 6.5.15 precondition_required = 428, // RFC6585 too_many_requests = 429, // RFC6585 request_header_fields_too_large = 431, // RFC6585 unavailable_for_legal_reasons = 451, // RFC7725 internal_server_error = 500, // RFC7231, Section 6.6.1 not_implemented = 501, // RFC7231, Section 6.6.2 bad_gateway = 502, // RFC7231, Section 6.6.3 service_unavailable = 503, // RFC7231, Section 6.6.4 gateway_timeout = 504, // RFC7231, Section 6.6.5 http_version_not_supported = 505, // RFC7231, Section 6.6.6 variant_also_negotiates = 506, // RFC2295 insufficient_storage = 507, // RFC4918 loop_detected = 508, // RFC5842 not_extended = 510, // RFC2774 network_authentication_required = 511, // RFC6585 _, pub fn phrase(self: Status) ?[]const u8 { return switch (self) { // 1xx statuses .@"continue" => "Continue", .switching_protocols => "Switching Protocols", .processing => "Processing", .early_hints => "Early Hints", // 2xx statuses .ok => "OK", .created => "Created", .accepted => "Accepted", .non_authoritative_info => "Non-Authoritative Information", .no_content => "No Content", .reset_content => "Reset Content", .partial_content => "Partial Content", .multi_status => "Multi-Status", .already_reported => "Already Reported", .im_used => "IM Used", // 3xx statuses .multiple_choice => "Multiple Choice", .moved_permanently => "Moved Permanently", .found => "Found", .see_other => "See Other", .not_modified => "Not Modified", .use_proxy => "Use Proxy", .temporary_redirect => "Temporary Redirect", .permanent_redirect => "Permanent Redirect", // 4xx statuses .bad_request => "Bad Request", .unauthorized => "Unauthorized", .payment_required => "Payment Required", .forbidden => "Forbidden", .not_found => "Not Found", .method_not_allowed => "Method Not Allowed", .not_acceptable => "Not Acceptable", .proxy_auth_required => "Proxy Authentication Required", .request_timeout => "Request Timeout", .conflict => "Conflict", .gone => "Gone", .length_required => "Length Required", .precondition_failed => "Precondition Failed", .payload_too_large => "Payload Too Large", .uri_too_long => "URI Too Long", .unsupported_media_type => "Unsupported Media Type", .range_not_satisfiable => "Range Not Satisfiable", .expectation_failed => "Expectation Failed", .teapot => "I'm a teapot", .misdirected_request => "Misdirected Request", .unprocessable_entity => "Unprocessable Entity", .locked => "Locked", .failed_dependency => "Failed Dependency", .too_early => "Too Early", .upgrade_required => "Upgrade Required", .precondition_required => "Precondition Required", .too_many_requests => "Too Many Requests", .request_header_fields_too_large => "Request Header Fields Too Large", .unavailable_for_legal_reasons => "Unavailable For Legal Reasons", // 5xx statuses .internal_server_error => "Internal Server Error", .not_implemented => "Not Implemented", .bad_gateway => "Bad Gateway", .service_unavailable => "Service Unavailable", .gateway_timeout => "Gateway Timeout", .http_version_not_supported => "HTTP Version Not Supported", .variant_also_negotiates => "Variant Also Negotiates", .insufficient_storage => "Insufficient Storage", .loop_detected => "Loop Detected", .not_extended => "Not Extended", .network_authentication_required => "Network Authentication Required", else => return null, }; } pub const Class = enum { informational, success, redirect, client_error, server_error, }; pub fn class(self: Status) Class { return switch (@intFromEnum(self)) { 100...199 => .informational, 200...299 => .success, 300...399 => .redirect, 400...499 => .client_error, else => .server_error, }; } test { try std.testing.expectEqualStrings("OK", Status.ok.phrase().?); try std.testing.expectEqualStrings("Not Found", Status.not_found.phrase().?); } test { try std.testing.expectEqual(@as(?Status.Class, Status.Class.success), Status.ok.class()); try std.testing.expectEqual(@as(?Status.Class, Status.Class.client_error), Status.not_found.class()); } }; pub const TransferEncoding = enum { chunked, // compression is intentionally omitted here, as std.http.Client stores it as content-encoding }; pub const ContentEncoding = enum { compress, deflate, gzip, zstd, }; pub const Connection = enum { keep_alive, close, }; const std = @import("std.zig"); test { _ = Client; _ = Method; _ = Status; }