-
-
Save Zorgatone/968ce86711aecea984a2c4a9771eed5f to your computer and use it in GitHub Desktop.
| const builtin = @import("builtin"); | |
| const std = @import("std"); | |
| // This version won't read/print headers, just the response | |
| pub fn main() !void { | |
| var writer_buffer: [8 * 1024]u8 = undefined; | |
| var redirect_buffer: [8 * 1024]u8 = undefined; | |
| var writer = std.fs.File.stdout().writer(&writer_buffer); | |
| var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | |
| defer switch (builtin.mode) { | |
| .Debug => std.debug.assert(debug_allocator.deinit() == .ok), | |
| .ReleaseFast, .ReleaseSmall, .ReleaseSafe => { | |
| // Nothing | |
| }, | |
| }; | |
| const allocator = switch (builtin.mode) { | |
| .Debug => debug_allocator.allocator(), | |
| .ReleaseFast, .ReleaseSmall, .ReleaseSafe => std.heap.smp_allocator, | |
| }; | |
| const uri = try std.Uri.parse("https://postman-echo.com/get"); | |
| var client: std.http.Client = .{ .allocator = allocator }; | |
| defer client.deinit(); | |
| const result = try client.fetch(.{ | |
| .location = .{ .uri = uri }, | |
| .method = .GET, | |
| .redirect_buffer = &redirect_buffer, | |
| .response_writer = &writer.interface, | |
| }); | |
| if (builtin.mode == .Debug) { | |
| std.debug.assert(result.status == .ok); | |
| } | |
| try writer.interface.flush(); | |
| } |
| const builtin = @import("builtin"); | |
| const std = @import("std"); | |
| // Manual http request printing also the response headers (won't work if chunked or compressed) | |
| pub fn main() !void { | |
| var writer_buffer: [8 * 1024]u8 = undefined; | |
| var redirect_buffer: [8 * 1024]u8 = undefined; | |
| var transfer_buffer: [8 * 1024]u8 = undefined; | |
| var reader_buffer: [8 * 1024]u8 = undefined; | |
| var writer = std.fs.File.stdout().writer(&writer_buffer); | |
| var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | |
| defer switch (builtin.mode) { | |
| .Debug => std.debug.assert(debug_allocator.deinit() == .ok), | |
| .ReleaseFast, .ReleaseSmall, .ReleaseSafe => { | |
| // Nothing | |
| }, | |
| }; | |
| const allocator = switch (builtin.mode) { | |
| .Debug => debug_allocator.allocator(), | |
| .ReleaseFast, .ReleaseSmall, .ReleaseSafe => std.heap.smp_allocator, | |
| }; | |
| const uri = try std.Uri.parse("https://postman-echo.com/get"); | |
| var client: std.http.Client = .{ .allocator = allocator }; | |
| defer client.deinit(); | |
| var request = try client.request(.GET, uri, .{}); | |
| defer request.deinit(); | |
| try request.sendBodiless(); | |
| const response = try request.receiveHead(&redirect_buffer); | |
| _ = try writer.interface.write(response.head.bytes); | |
| const content_length = response.head.content_length; | |
| const reader = request.reader.bodyReader(&transfer_buffer, .none, content_length); | |
| var done = false; | |
| var bytes_read: usize = 0; | |
| while (!done) { | |
| const size = try reader.readSliceShort(&reader_buffer); | |
| if (size > 0) { | |
| bytes_read += size; | |
| _ = try writer.interface.write(reader_buffer[0..size]); | |
| } | |
| if (content_length) |c_len| { | |
| if (bytes_read >= c_len) { | |
| done = true; | |
| } | |
| } | |
| if (size < reader_buffer.len) { | |
| done = true; | |
| } | |
| } | |
| try writer.interface.flush(); | |
| } |
Thanks for this. #1 makes sense. #2 your perspective is helping me understand zig better. Your explanation makes sense to me.
I have written a working http request example for POST, PUT, GET, DELETE in ZIG 0.15.1/0.15.2
it also include headers, payload
https://github.com/bhagatverma/zig-http-request-example/blob/main/main.zig
@bhagatverma I checked your implementation:
// Buffer for response body
var response_body: std.ArrayList(u8) = .empty;
defer response_body.deinit(allocator);
You're declaring an arrayList that is never used elsewhere, initialized and deinitialized only, and not actually needed
@bhagatverma also why declaring an identical enum?
pub const HttpMethod = enum {
GET,
POST,
PUT,
DELETE,
PATCH,
HEAD,
OPTIONS,
pub fn toStdMethod(self: HttpMethod) std.http.Method {
return switch (self) {
.GET => .GET,
.POST => .POST,
.PUT => .PUT,
.DELETE => .DELETE,
.PATCH => .PATCH,
.HEAD => .HEAD,
.OPTIONS => .OPTIONS,
};
}
};
instead of all that you could just use the standard std.http.Method yourself
@bhagatverma I checked your implementation:
// Buffer for response body var response_body: std.ArrayList(u8) = .empty; defer response_body.deinit(allocator);You're declaring an arrayList that is never used elsewhere, initialized and deinitialized only, and not actually needed
Hi @Zorgatone , Thanks for pointing out, i missed to remove this unwanted code.
@bhagatverma also why declaring an identical enum?
pub const HttpMethod = enum { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, pub fn toStdMethod(self: HttpMethod) std.http.Method { return switch (self) { .GET => .GET, .POST => .POST, .PUT => .PUT, .DELETE => .DELETE, .PATCH => .PATCH, .HEAD => .HEAD, .OPTIONS => .OPTIONS, }; } };instead of all that you could just use the standard
std.http.Methodyourself
@Zorgatone you are absolutely right, i could have used std.http instead of enum. but its just another way of writing same.
The variable names made me think you were accidentally using the reader's internal fifo buffer as your temporary bounce buffer, but no you gave the reader transfer_buffer instead of reader_buffer.
Maybe this change would make more sense?
- const reader = request.reader.bodyReader(&transfer_buffer, .none, content_length);
+ const reader = request.reader.bodyReader(&reader_buffer, .none, content_length);
var done = false;
var bytes_read: usize = 0;
while (!done) {
- const size = try reader.readSliceShort(&reader_buffer);
+ const size = try reader.readSliceShort(&transfer_buffer);
if (size > 0) {
bytes_read += size;
- _ = try writer.interface.write(reader_buffer[0..size]);
+ _ = try writer.interface.write(transfer_buffer[0..size]);
// ...
- if (size < reader_buffer.len) {
+ if (size < transfer_buffer.len) {
done = true;
}Hi, this code does not seem to work anymore using latest zig version: 0.16.0-dev.1657+985a3565c
Hi @michaelchiche zig 0.16 is not stable yet, only on the master branch in development. Async IO and the std library are being worked on, so the example will have to change once the API for 0.16 are final, and I'm not maintaining a working example for every time the master branch changes (can be frequently).
I will update the code once 0.16.x is released
The variable names made me think you were accidentally using the reader's internal fifo buffer as your temporary bounce buffer, but no you gave the reader
transfer_bufferinstead ofreader_buffer.Maybe this change would make more sense?
- const reader = request.reader.bodyReader(&transfer_buffer, .none, content_length); + const reader = request.reader.bodyReader(&reader_buffer, .none, content_length); var done = false; var bytes_read: usize = 0; while (!done) { - const size = try reader.readSliceShort(&reader_buffer); + const size = try reader.readSliceShort(&transfer_buffer); if (size > 0) { bytes_read += size; - _ = try writer.interface.write(reader_buffer[0..size]); + _ = try writer.interface.write(transfer_buffer[0..size]); // ... - if (size < reader_buffer.len) { + if (size < transfer_buffer.len) { done = true; }
Yeah the names are confusing indeed, I used transfer since it's in the "middle", used for piping the data in the buffer I finally use for reading the response. But it would make more sense to change the names, yes. It doesn't change the example code much anyway
@definitepotato
well, this was an easy example so you could go with both approaches. Since it's going to use Uri.parse anyway, I did it earlier, in case I want to reuse the same uri for multiple fetch/http requests.
Not sure I understand your question, what do you mean why am I using a file descriptor here? I am using the new
std.Ioin this example (all readers, writers also implement the std.Io interface now, ie.writer.interfaceandreader.interface). Turns out standard output (stdout) is also implemented as a file descriptor (macOs, linux). Your example writer.Allocating + heap.FixedBufferAllocator would make sense if you wanted to write to some array (buffer) in memory you could then read, or chunked.buffered stream/pipe to an actual file on the file system (instead of stdout). Here I'm only printing (streaming/piping) to the console (stdout) to debug the result of the HTTP request (body).Did I answer your question, or did you want to know something else?