zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob bb06bb1c (10608B) - Raw


      1 pub const Client = @import("http/Client.zig");
      2 pub const Server = @import("http/Server.zig");
      3 pub const protocol = @import("http/protocol.zig");
      4 const headers = @import("http/Headers.zig");
      5 
      6 pub const Headers = headers.Headers;
      7 pub const Field = headers.Field;
      8 
      9 pub const Version = enum {
     10     @"HTTP/1.0",
     11     @"HTTP/1.1",
     12 };
     13 
     14 /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
     15 /// https://datatracker.ietf.org/doc/html/rfc7231#section-4 Initial definiton
     16 /// https://datatracker.ietf.org/doc/html/rfc5789#section-2 PATCH
     17 pub const Method = enum {
     18     GET,
     19     HEAD,
     20     POST,
     21     PUT,
     22     DELETE,
     23     CONNECT,
     24     OPTIONS,
     25     TRACE,
     26     PATCH,
     27 
     28     /// Returns true if a request of this method is allowed to have a body
     29     /// Actual behavior from servers may vary and should still be checked
     30     pub fn requestHasBody(self: Method) bool {
     31         return switch (self) {
     32             .POST, .PUT, .PATCH => true,
     33             .GET, .HEAD, .DELETE, .CONNECT, .OPTIONS, .TRACE => false,
     34         };
     35     }
     36 
     37     /// Returns true if a response to this method is allowed to have a body
     38     /// Actual behavior from clients may vary and should still be checked
     39     pub fn responseHasBody(self: Method) bool {
     40         return switch (self) {
     41             .GET, .POST, .DELETE, .CONNECT, .OPTIONS, .PATCH => true,
     42             .HEAD, .PUT, .TRACE => false,
     43         };
     44     }
     45 
     46     /// An HTTP method is safe if it doesn't alter the state of the server.
     47     /// https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP
     48     /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1
     49     pub fn safe(self: Method) bool {
     50         return switch (self) {
     51             .GET, .HEAD, .OPTIONS, .TRACE => true,
     52             .POST, .PUT, .DELETE, .CONNECT, .PATCH => false,
     53         };
     54     }
     55 
     56     /// 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.
     57     /// https://developer.mozilla.org/en-US/docs/Glossary/Idempotent
     58     /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.2
     59     pub fn idempotent(self: Method) bool {
     60         return switch (self) {
     61             .GET, .HEAD, .PUT, .DELETE, .OPTIONS, .TRACE => true,
     62             .CONNECT, .POST, .PATCH => false,
     63         };
     64     }
     65 
     66     /// 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.
     67     /// https://developer.mozilla.org/en-US/docs/Glossary/cacheable
     68     /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.3
     69     pub fn cacheable(self: Method) bool {
     70         return switch (self) {
     71             .GET, .HEAD => true,
     72             .POST, .PUT, .DELETE, .CONNECT, .OPTIONS, .TRACE, .PATCH => false,
     73         };
     74     }
     75 };
     76 
     77 /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
     78 pub const Status = enum(u10) {
     79     @"continue" = 100, // RFC7231, Section 6.2.1
     80     switching_protocols = 101, // RFC7231, Section 6.2.2
     81     processing = 102, // RFC2518
     82     early_hints = 103, // RFC8297
     83 
     84     ok = 200, // RFC7231, Section 6.3.1
     85     created = 201, // RFC7231, Section 6.3.2
     86     accepted = 202, // RFC7231, Section 6.3.3
     87     non_authoritative_info = 203, // RFC7231, Section 6.3.4
     88     no_content = 204, // RFC7231, Section 6.3.5
     89     reset_content = 205, // RFC7231, Section 6.3.6
     90     partial_content = 206, // RFC7233, Section 4.1
     91     multi_status = 207, // RFC4918
     92     already_reported = 208, // RFC5842
     93     im_used = 226, // RFC3229
     94 
     95     multiple_choice = 300, // RFC7231, Section 6.4.1
     96     moved_permanently = 301, // RFC7231, Section 6.4.2
     97     found = 302, // RFC7231, Section 6.4.3
     98     see_other = 303, // RFC7231, Section 6.4.4
     99     not_modified = 304, // RFC7232, Section 4.1
    100     use_proxy = 305, // RFC7231, Section 6.4.5
    101     temporary_redirect = 307, // RFC7231, Section 6.4.7
    102     permanent_redirect = 308, // RFC7538
    103 
    104     bad_request = 400, // RFC7231, Section 6.5.1
    105     unauthorized = 401, // RFC7235, Section 3.1
    106     payment_required = 402, // RFC7231, Section 6.5.2
    107     forbidden = 403, // RFC7231, Section 6.5.3
    108     not_found = 404, // RFC7231, Section 6.5.4
    109     method_not_allowed = 405, // RFC7231, Section 6.5.5
    110     not_acceptable = 406, // RFC7231, Section 6.5.6
    111     proxy_auth_required = 407, // RFC7235, Section 3.2
    112     request_timeout = 408, // RFC7231, Section 6.5.7
    113     conflict = 409, // RFC7231, Section 6.5.8
    114     gone = 410, // RFC7231, Section 6.5.9
    115     length_required = 411, // RFC7231, Section 6.5.10
    116     precondition_failed = 412, // RFC7232, Section 4.2][RFC8144, Section 3.2
    117     payload_too_large = 413, // RFC7231, Section 6.5.11
    118     uri_too_long = 414, // RFC7231, Section 6.5.12
    119     unsupported_media_type = 415, // RFC7231, Section 6.5.13][RFC7694, Section 3
    120     range_not_satisfiable = 416, // RFC7233, Section 4.4
    121     expectation_failed = 417, // RFC7231, Section 6.5.14
    122     teapot = 418, // RFC 7168, 2.3.3
    123     misdirected_request = 421, // RFC7540, Section 9.1.2
    124     unprocessable_entity = 422, // RFC4918
    125     locked = 423, // RFC4918
    126     failed_dependency = 424, // RFC4918
    127     too_early = 425, // RFC8470
    128     upgrade_required = 426, // RFC7231, Section 6.5.15
    129     precondition_required = 428, // RFC6585
    130     too_many_requests = 429, // RFC6585
    131     request_header_fields_too_large = 431, // RFC6585
    132     unavailable_for_legal_reasons = 451, // RFC7725
    133 
    134     internal_server_error = 500, // RFC7231, Section 6.6.1
    135     not_implemented = 501, // RFC7231, Section 6.6.2
    136     bad_gateway = 502, // RFC7231, Section 6.6.3
    137     service_unavailable = 503, // RFC7231, Section 6.6.4
    138     gateway_timeout = 504, // RFC7231, Section 6.6.5
    139     http_version_not_supported = 505, // RFC7231, Section 6.6.6
    140     variant_also_negotiates = 506, // RFC2295
    141     insufficient_storage = 507, // RFC4918
    142     loop_detected = 508, // RFC5842
    143     not_extended = 510, // RFC2774
    144     network_authentication_required = 511, // RFC6585
    145 
    146     _,
    147 
    148     pub fn phrase(self: Status) ?[]const u8 {
    149         return switch (self) {
    150             // 1xx statuses
    151             .@"continue" => "Continue",
    152             .switching_protocols => "Switching Protocols",
    153             .processing => "Processing",
    154             .early_hints => "Early Hints",
    155 
    156             // 2xx statuses
    157             .ok => "OK",
    158             .created => "Created",
    159             .accepted => "Accepted",
    160             .non_authoritative_info => "Non-Authoritative Information",
    161             .no_content => "No Content",
    162             .reset_content => "Reset Content",
    163             .partial_content => "Partial Content",
    164             .multi_status => "Multi-Status",
    165             .already_reported => "Already Reported",
    166             .im_used => "IM Used",
    167 
    168             // 3xx statuses
    169             .multiple_choice => "Multiple Choice",
    170             .moved_permanently => "Moved Permanently",
    171             .found => "Found",
    172             .see_other => "See Other",
    173             .not_modified => "Not Modified",
    174             .use_proxy => "Use Proxy",
    175             .temporary_redirect => "Temporary Redirect",
    176             .permanent_redirect => "Permanent Redirect",
    177 
    178             // 4xx statuses
    179             .bad_request => "Bad Request",
    180             .unauthorized => "Unauthorized",
    181             .payment_required => "Payment Required",
    182             .forbidden => "Forbidden",
    183             .not_found => "Not Found",
    184             .method_not_allowed => "Method Not Allowed",
    185             .not_acceptable => "Not Acceptable",
    186             .proxy_auth_required => "Proxy Authentication Required",
    187             .request_timeout => "Request Timeout",
    188             .conflict => "Conflict",
    189             .gone => "Gone",
    190             .length_required => "Length Required",
    191             .precondition_failed => "Precondition Failed",
    192             .payload_too_large => "Payload Too Large",
    193             .uri_too_long => "URI Too Long",
    194             .unsupported_media_type => "Unsupported Media Type",
    195             .range_not_satisfiable => "Range Not Satisfiable",
    196             .expectation_failed => "Expectation Failed",
    197             .teapot => "I'm a teapot",
    198             .misdirected_request => "Misdirected Request",
    199             .unprocessable_entity => "Unprocessable Entity",
    200             .locked => "Locked",
    201             .failed_dependency => "Failed Dependency",
    202             .too_early => "Too Early",
    203             .upgrade_required => "Upgrade Required",
    204             .precondition_required => "Precondition Required",
    205             .too_many_requests => "Too Many Requests",
    206             .request_header_fields_too_large => "Request Header Fields Too Large",
    207             .unavailable_for_legal_reasons => "Unavailable For Legal Reasons",
    208 
    209             // 5xx statuses
    210             .internal_server_error => "Internal Server Error",
    211             .not_implemented => "Not Implemented",
    212             .bad_gateway => "Bad Gateway",
    213             .service_unavailable => "Service Unavailable",
    214             .gateway_timeout => "Gateway Timeout",
    215             .http_version_not_supported => "HTTP Version Not Supported",
    216             .variant_also_negotiates => "Variant Also Negotiates",
    217             .insufficient_storage => "Insufficient Storage",
    218             .loop_detected => "Loop Detected",
    219             .not_extended => "Not Extended",
    220             .network_authentication_required => "Network Authentication Required",
    221 
    222             else => return null,
    223         };
    224     }
    225 
    226     pub const Class = enum {
    227         informational,
    228         success,
    229         redirect,
    230         client_error,
    231         server_error,
    232     };
    233 
    234     pub fn class(self: Status) Class {
    235         return switch (@enumToInt(self)) {
    236             100...199 => .informational,
    237             200...299 => .success,
    238             300...399 => .redirect,
    239             400...499 => .client_error,
    240             else => .server_error,
    241         };
    242     }
    243 
    244     test {
    245         try std.testing.expectEqualStrings("OK", Status.ok.phrase().?);
    246         try std.testing.expectEqualStrings("Not Found", Status.not_found.phrase().?);
    247     }
    248 
    249     test {
    250         try std.testing.expectEqual(@as(?Status.Class, Status.Class.success), Status.ok.class());
    251         try std.testing.expectEqual(@as(?Status.Class, Status.Class.client_error), Status.not_found.class());
    252     }
    253 };
    254 
    255 pub const TransferEncoding = enum {
    256     chunked,
    257     // compression is intentionally omitted here, as std.http.Client stores it as content-encoding
    258 };
    259 
    260 pub const ContentEncoding = enum {
    261     compress,
    262     deflate,
    263     gzip,
    264     zstd,
    265 };
    266 
    267 pub const Connection = enum {
    268     keep_alive,
    269     close,
    270 };
    271 
    272 const std = @import("std.zig");
    273 
    274 test {
    275     _ = Client;
    276     _ = Method;
    277     _ = Status;
    278     _ = @import("http/test.zig");
    279 }