Commit graph

172 commits

Author SHA1 Message Date
Andrew Kelley
63acc856c1 std.http.Client: remove invalid use of refAllDecls 2024-02-23 02:37:11 -07:00
Andrew Kelley
e204b2ca92 std.http.Client.connectUnix: handle unsupported OS at compile time 2024-02-23 02:37:11 -07:00
Andrew Kelley
651aa5e8e4 std.http.Client: eliminate arena allocator usage
Before, this code constructed an arena allocator and then used it when
handling redirects.

You know what's better than having threads fight over an allocator?
Avoiding dynamic memory allocation in the first place.

This commit reuses the http headers static buffer for handling
redirects. The new location is copied to the beginning of the static
header buffer and then the subsequent request uses a subslice of that
buffer.
2024-02-23 02:37:11 -07:00
Andrew Kelley
743a0c966d std.http.Client: remove bad decisions from fetch()
* "storage" is a better name than "strategy".
* The most flexible memory-based storage API is appending to an
  ArrayList.
* HTTP method should default to POST if there is a payload.
* Avoid storing unnecessary data in the FetchResult
* Avoid the need for a deinit() method in the FetchResult

The decisions that this logic made about how to handle files is beyond
repair:
- fail to use sendfile() on a plain connection
- redundant stat
- does not handle arbitrary streams
So, file-based response storage is no longer supported. Users should use
the lower-level open() API which allows avoiding these pitfalls.
2024-02-23 02:37:11 -07:00
Andrew Kelley
0ddcb83418 std.http.Client.fetch: remove inappropriate seek
no
2024-02-23 02:37:11 -07:00
Andrew Kelley
7036644ed2 std.http.Client: remove advisory file lock on fetch
This is not an appropriate place to put this code. It belongs in the
caller's code, if at all.
2024-02-23 02:37:11 -07:00
Andrew Kelley
ddb754ff2f std.http: fix parsing incorrect tokenization 2024-02-23 02:37:11 -07:00
Andrew Kelley
ae630b695e std.http.Client.connect: case insensitive host comparison
for checking if a proxy is connecting to itself
2024-02-23 02:37:11 -07:00
Andrew Kelley
f9dff2fcf1 std.http: fields at the top of the struct
Perhaps the language should enforce this.
2024-02-23 02:37:11 -07:00
Andrew Kelley
78192637fb std.http: parser fixes
* add API for iterating over custom HTTP headers
* remove `trailing` flag from std.http.Client.parse. Instead, simply
  don't call parse() for trailers.
* fix the logic inside that parse() function. it was using wrong std.mem
  functions, ignoring malformed data, and returned errors on dead
  branches.
* simplify logic inside wait()
* fix HeadersParser not dropping the 2 read bytes of \r\n after a
  chunked transfer
* move the trailers test to be a std lib unit test and make it pass
2024-02-23 02:37:11 -07:00
Andrew Kelley
d574875f00 Revert "std.http: remove 'done' flag"
This reverts commit 42be972a72c86b32ad8403d082ab42763c6facec.

Using a bit to distinguish between headers and trailers is fine. It was
just named and documented poorly.
2024-02-23 02:37:11 -07:00
Andrew Kelley
cf4a2c4d18 std.http.Client.Response.ParseError: remove OutOfMemory
This can no longer fail due to OOM.
2024-02-23 02:37:11 -07:00
Andrew Kelley
99a5de9dbb git fetching: fix redirect handling
I mistakenly thought this was dead code in an earlier commit in this
branch. This commit restores the proper behavior.
2024-02-23 02:37:11 -07:00
Andrew Kelley
b6ca89fa7c std.http.Client: disable zstd for now
The Allocator requirement is problematic.
2024-02-23 02:37:11 -07:00
Andrew Kelley
4d401e6159 std.http: remove Headers API
I originally removed these in 402f967ed5.
I allowed them to be added back in #15299 because they were smuggled in
alongside a bug fix, however, I wasn't kidding when I said that I wanted
to take the design of std.http in a different direction than using this
data structure.

Instead, some headers are provided via explicit field names populated
while parsing the HTTP request/response, and some are provided via
new fields that support passing extra, arbitrary headers.

This resulted in simplification of logic in many places, as well as
elimination of the possibility of failure in many places. There is
less deinitialization code happening now.

Furthermore, it made it no longer necessary to clone the headers data
structure in order to handle redirects.

http_proxy and https_proxy fields are now pointers since it is common
for them to be unpopulated.

loadDefaultProxies is changed into initDefaultProxies to communicate
that it does not actually load anything from disk or from the network.
The function now is leaky; the API user must pass an already
instantiated arena allocator. Removes the need to deinitialize proxies.

Before, proxies stored arbitrary sets of headers. Now they only store
the authorization value.

Removed the duplicated code between https_proxy and http_proxy. Finally,
parsing failures of the environment variables result in errors being
emitted rather than silently ignoring the proxy.

error.CompressionNotSupported is renamed to
error.CompressionUnsupported, matching the naming convention from all
the other errors in the same set.

Removed documentation comments that were redundant with field and type
names.

Disabling zstd decompression in the server for now; see #18937.

I found some apparently dead code in src/Package/Fetch/git.zig. I want
to check with Ian about this.

I discovered that test/standalone/http.zig is dead code, it is only
being compiled but not being run. Furthermore it hangs at the end if you
run it manually. The previous commits in this branch were written under
the assumption that this test was being run with
`zig build test-standalone`.
2024-02-23 02:37:11 -07:00
Andrew Kelley
f46447e6a1 std.http.Client.fetch: add redirect behavior to options 2024-02-23 02:37:11 -07:00
Andrew Kelley
50e2a5f673 std.http: remove 'done' flag
This is a state machine that already has a `state` field. No need to
additionally store "done" - it just makes things unnecessarily
complicated and buggy.
2024-02-23 02:37:11 -07:00
Andrew Kelley
90bd4f226e std.http: remove the ability to heap-allocate headers
The buffer for HTTP headers is now always provided via a static buffer.
As a consequence, OutOfMemory is no longer a member of the read() error
set, and the API and implementation of Client and Server are simplified.

error.HttpHeadersExceededSizeLimit is renamed to
error.HttpHeadersOversize.
2024-02-23 02:37:10 -07:00
Igor Anić
d645114f7e add deflate implemented from first principles
Zig deflate compression/decompression implementation. It supports compression and decompression of gzip, zlib and raw deflate format.

Fixes #18062.

This PR replaces current compress/gzip and compress/zlib packages. Deflate package is renamed to flate. Flate is common name for deflate/inflate where deflate is compression and inflate decompression.

There are breaking change. Methods signatures are changed because of removal of the allocator, and I also unified API for all three namespaces (flate, gzip, zlib).

Currently I put old packages under v1 namespace they are still available as compress/v1/gzip, compress/v1/zlib, compress/v1/deflate. Idea is to give users of the current API little time to postpone analyzing what they had to change. Although that rises question when it is safe to remove that v1 namespace.

Here is current API in the compress package:

```Zig
// deflate
    fn compressor(allocator, writer, options) !Compressor(@TypeOf(writer))
    fn Compressor(comptime WriterType) type

    fn decompressor(allocator, reader, null) !Decompressor(@TypeOf(reader))
    fn Decompressor(comptime ReaderType: type) type

// gzip
    fn compress(allocator, writer, options) !Compress(@TypeOf(writer))
    fn Compress(comptime WriterType: type) type

    fn decompress(allocator, reader) !Decompress(@TypeOf(reader))
    fn Decompress(comptime ReaderType: type) type

// zlib
    fn compressStream(allocator, writer, options) !CompressStream(@TypeOf(writer))
    fn CompressStream(comptime WriterType: type) type

    fn decompressStream(allocator, reader) !DecompressStream(@TypeOf(reader))
    fn DecompressStream(comptime ReaderType: type) type

// xz
   fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader))
   fn Decompress(comptime ReaderType: type) type

// lzma
    fn decompress(allocator, reader) !Decompress(@TypeOf(reader))
    fn Decompress(comptime ReaderType: type) type

// lzma2
    fn decompress(allocator, reader, writer !void

// zstandard:
    fn DecompressStream(ReaderType, options) type
    fn decompressStream(allocator, reader) DecompressStream(@TypeOf(reader), .{})
    struct decompress
```

The proposed naming convention:
 - Compressor/Decompressor for functions which return type, like Reader/Writer/GeneralPurposeAllocator
 - compressor/compressor for functions which are initializers for that type, like reader/writer/allocator
 - compress/decompress for one shot operations, accepts reader/writer pair, like read/write/alloc

```Zig
/// Compress from reader and write compressed data to the writer.
fn compress(reader: anytype, writer: anytype, options: Options) !void

/// Create Compressor which outputs the writer.
fn compressor(writer: anytype, options: Options) !Compressor(@TypeOf(writer))

/// Compressor type
fn Compressor(comptime WriterType: type) type

/// Decompress from reader and write plain data to the writer.
fn decompress(reader: anytype, writer: anytype) !void

/// Create Decompressor which reads from reader.
fn decompressor(reader: anytype) Decompressor(@TypeOf(reader)

/// Decompressor type
fn Decompressor(comptime ReaderType: type) type

```

Comparing this implementation with the one we currently have in Zig's standard library (std).
Std is roughly 1.2-1.4 times slower in decompression, and 1.1-1.2 times slower in compression. Compressed sizes are pretty much same in both cases.
More resutls in [this](https://github.com/ianic/flate) repo.

This library uses static allocations for all structures, doesn't require allocator. That makes sense especially for deflate where all structures, internal buffers are allocated to the full size. Little less for inflate where we std version uses less memory by not preallocating to theoretical max size array which are usually not fully used.

For deflate this library allocates 395K while std 779K.
For inflate this library allocates 74.5K while std around 36K.

Inflate difference is because we here use 64K history instead of 32K in std.

If merged existing usage of compress gzip/zlib/deflate need some changes. Here is example with necessary changes in comments:

```Zig

const std = @import("std");

// To get this file:
// wget -nc -O war_and_peace.txt https://www.gutenberg.org/ebooks/2600.txt.utf-8
const data = @embedFile("war_and_peace.txt");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    try oldDeflate(allocator);
    try new(std.compress.flate, allocator);

    try oldZlib(allocator);
    try new(std.compress.zlib, allocator);

    try oldGzip(allocator);
    try new(std.compress.gzip, allocator);
}

pub fn new(comptime pkg: type, allocator: std.mem.Allocator) !void {
    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();

    // Compressor
    var cmp = try pkg.compressor(buf.writer(), .{});
    _ = try cmp.write(data);
    try cmp.finish();

    var fbs = std.io.fixedBufferStream(buf.items);
    // Decompressor
    var dcp = pkg.decompressor(fbs.reader());

    const plain = try dcp.reader().readAllAlloc(allocator, std.math.maxInt(usize));
    defer allocator.free(plain);
    try std.testing.expectEqualSlices(u8, data, plain);
}

pub fn oldDeflate(allocator: std.mem.Allocator) !void {
    const deflate = std.compress.v1.deflate;

    // Compressor
    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();
    // Remove allocator
    // Rename deflate -> flate
    var cmp = try deflate.compressor(allocator, buf.writer(), .{});
    _ = try cmp.write(data);
    try cmp.close(); // Rename to finish
    cmp.deinit(); // Remove

    // Decompressor
    var fbs = std.io.fixedBufferStream(buf.items);
    // Remove allocator and last param
    // Rename deflate -> flate
    // Remove try
    var dcp = try deflate.decompressor(allocator, fbs.reader(), null);
    defer dcp.deinit(); // Remove

    const plain = try dcp.reader().readAllAlloc(allocator, std.math.maxInt(usize));
    defer allocator.free(plain);
    try std.testing.expectEqualSlices(u8, data, plain);
}

pub fn oldZlib(allocator: std.mem.Allocator) !void {
    const zlib = std.compress.v1.zlib;

    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();

    // Compressor
    // Rename compressStream => compressor
    // Remove allocator
    var cmp = try zlib.compressStream(allocator, buf.writer(), .{});
    _ = try cmp.write(data);
    try cmp.finish();
    cmp.deinit(); // Remove

    var fbs = std.io.fixedBufferStream(buf.items);
    // Decompressor
    // decompressStream => decompressor
    // Remove allocator
    // Remove try
    var dcp = try zlib.decompressStream(allocator, fbs.reader());
    defer dcp.deinit(); // Remove

    const plain = try dcp.reader().readAllAlloc(allocator, std.math.maxInt(usize));
    defer allocator.free(plain);
    try std.testing.expectEqualSlices(u8, data, plain);
}

pub fn oldGzip(allocator: std.mem.Allocator) !void {
    const gzip = std.compress.v1.gzip;

    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();

    // Compressor
    // Rename compress => compressor
    // Remove allocator
    var cmp = try gzip.compress(allocator, buf.writer(), .{});
    _ = try cmp.write(data);
    try cmp.close(); // Rename to finisho
    cmp.deinit(); // Remove

    var fbs = std.io.fixedBufferStream(buf.items);
    // Decompressor
    // Rename decompress => decompressor
    // Remove allocator
    // Remove try
    var dcp = try gzip.decompress(allocator, fbs.reader());
    defer dcp.deinit(); // Remove

    const plain = try dcp.reader().readAllAlloc(allocator, std.math.maxInt(usize));
    defer allocator.free(plain);
    try std.testing.expectEqualSlices(u8, data, plain);
}

```
2024-02-14 18:28:20 +01:00
Jacob Young
919a3bae1c http: protect against zero-length chunks
A zero-length chunk marks the end of the body, so prevent any from
possibly occurring in the middle of the body.
2024-02-08 01:29:49 -08:00
Jacob Young
c1e7d0c08f http: optimize allocations for proxy basic authorization 2024-01-31 15:00:27 +01:00
Jacob Young
a111f805cd http: avoid allocator use when encoding basic authorization 2024-01-31 15:00:27 +01:00
Jacob Young
82b37ea024 http: support basic access authentication 2024-01-31 12:05:12 +01:00
Nameless
a7dbc57a35 std.http.Client: read response messages with no length until eof 2024-01-18 15:43:58 -08:00
Nameless
b723296e1f std.http: add missing documentation and a few examples 2024-01-13 18:51:38 -08:00
mlugg
51595d6b75
lib: correct unnecessary uses of 'var' 2023-11-19 09:55:07 +00:00
Jacob Young
509be7cf1f x86_64: fix std test failures 2023-11-03 23:18:21 -04:00
Jordyfel
61861ef395 std.http: account for renames in docs 2023-11-01 14:46:00 +02:00
Andrew Kelley
3fc6fc6812 std.builtin.Endian: make the tags lower case
Let's take this breaking change opportunity to fix the style of this
enum.
2023-10-31 21:37:35 -04:00
Nameless
87735e4dae std.http.Client: add proxy scheme guessing, fix typo
This adds scheme guessing when loading proxies, such that
`HTTP_PROXY=127.0.0.1` and such are valid now and it behaves identically
to `HTTP_PROXY=http://127.0.0.1`. Additionally fixed a typo that was
causing loadDefaultProxies to never populate the https_proxy.
2023-10-27 03:42:33 -04:00
Jacob Young
8f69e977f1 x86_64: implement 128-bit builtins
* `@clz`
 * `@ctz`
 * `@popCount`
 * `@byteSwap`
 * `@bitReverse`
 * various encodings used by std
2023-10-23 22:42:18 -04:00
Andrew Kelley
b82459fa43
Merge pull request #17407 from truemedian/http-ng
std.http: more proxy support, buffer writes, tls toggle
2023-10-22 17:48:03 -04:00
Jacob Young
27fe945a00 Revert "Revert "Merge pull request #17637 from jacobly0/x86_64-test-std""
This reverts commit 6f0198cadb.
2023-10-22 15:46:43 -04:00
Andrew Kelley
6f0198cadb Revert "Merge pull request #17637 from jacobly0/x86_64-test-std"
This reverts commit 0c99ba1eab, reversing
changes made to 5f92b070bf.

This caused a CI failure when it landed in master branch due to a
128-bit `@byteSwap` in std.mem.
2023-10-22 12:16:35 -07:00
Nameless
93e1f8c8e5
std.http.Client: documentaion fixes 2023-10-21 20:53:00 -05:00
Nameless
dd010e9e90
std.http.Client: ignore unknown proxies, fix basic proxy auth 2023-10-21 20:53:00 -05:00
Nameless
7dd3099519
std.http: fix crashes found via fuzzing 2023-10-21 20:52:59 -05:00
Nameless
363d0ee5e1
std.http: rename start->send and request->open to be more inline with operation 2023-10-21 20:52:59 -05:00
Nameless
544ed34d99
std.http.Server: improve documentation, do -> start
Response.do was renamed to Response.start to mimic the
naming scheme in http.Client
2023-10-21 20:52:59 -05:00
Nameless
d4cf8ea0b7
std.http.Client: improve documentation 2023-10-21 20:52:59 -05:00
Nameless
c523b5421b
std.http: make encoding fields non-null, store as enum variant 2023-10-21 20:52:59 -05:00
Nameless
16f89eab45
std.http.Client: make transfer-encoding priority over content-length as per spec 2023-10-21 20:52:59 -05:00
Nameless
0eef21d8ec
std.http.Client: add option to disable https
std_options.http_connection_pool_size removed in favor of

```
client.connection_pool.resize(client.allocator, size);
```

std_options.http_disable_tls will remove all https capability from
std.http when true. Any https request will error with
`error.TlsInitializationFailed`.

Solves #17051.
2023-10-21 20:52:58 -05:00
Nameless
e1c37f70d4
std.http.Client: store *Connection instead of a pool node, buffer writes 2023-10-21 20:52:58 -05:00
Nameless
1afeada2d9
std.http.Client: enhance proxy support
adds connectTunnel to form a HTTP CONNECT tunnel to the desired host.
Primarily implemented for proxies, but like connectUnix may be called by
any user.

adds loadDefaultProxies to load proxy information from common
environment variables (http_proxy, HTTP_PROXY, https_proxy, HTTPS_PROXY,
all_proxy, ALL_PROXY).
- no_proxy and NO_PROXY are currently unsupported.

splits proxy into http_proxy and https_proxy, adds headers field for
arbitrary headers to each proxy.
2023-10-21 20:52:58 -05:00
Jacob Young
2e6e39a700 x86_64: fix bugs and disable erroring tests 2023-10-21 10:55:41 -04:00
Emil Lerch
fcca3cd1a3
std.http: introduce options to http client to allow for raw uris
Addresses #17015 by introducing a new startWithOptions. The only option is currently is a flag
to use the provided URI as is, without modification when passed to the server. Normally, this
is not needed nor desired. However, some REST APIs may have requirements that cannot be satisfied
with the default handling.
2023-09-28 14:16:39 +03:00
Chris Burgess
1c726bcb32
std.http: add identity to content encodings (#16493)
Some servers will respond with the identity encoding, meaning no
encoding, especially when responding to range-get requests. Adding the
identity encoding stops the header parser from failing when it
encounters this.
2023-09-26 17:16:40 -04:00
Jan Weidner
c8ba5839f7
std.http.Client: add note about resource management 2023-09-25 12:17:11 +03:00
Nameless
4689d93cb2
std.http: allow for arbitrary http methods 2023-08-30 13:05:45 -05:00