From 6329666154ac5a3fff609c2417e3e2e98474e393 Mon Sep 17 00:00:00 2001 From: Sonic Date: Thu, 12 Sep 2024 13:34:12 +0300 Subject: [PATCH] add examples of http client & server --- .../15-advanced-formatting.md | 4 -- .../02-standard-library/16-http.mdx | 23 +++++++++ .../16.http-receive-response.zig | 50 ++++++++++++++++++ .../16.http-send-request.zig | 51 +++++++++++++++++++ .../03-build-system/01-build-modes.md | 2 +- .../15-advanced-formatting.md | 4 -- .../02-standard-library/16-http.mdx | 23 +++++++++ .../16.http-receive-response.zig | 50 ++++++++++++++++++ .../16.http-send-request.zig | 51 +++++++++++++++++++ .../03-build-system/01-build-modes.md | 2 +- 10 files changed, 250 insertions(+), 10 deletions(-) create mode 100644 website/versioned_docs/version-0.13/02-standard-library/16-http.mdx create mode 100644 website/versioned_docs/version-0.13/02-standard-library/16.http-receive-response.zig create mode 100644 website/versioned_docs/version-0.13/02-standard-library/16.http-send-request.zig create mode 100644 website/versioned_docs/version-0.14/02-standard-library/16-http.mdx create mode 100644 website/versioned_docs/version-0.14/02-standard-library/16.http-receive-response.zig create mode 100644 website/versioned_docs/version-0.14/02-standard-library/16.http-send-request.zig diff --git a/website/versioned_docs/version-0.13/02-standard-library/15-advanced-formatting.md b/website/versioned_docs/version-0.13/02-standard-library/15-advanced-formatting.md index e509a21..e8a48c2 100644 --- a/website/versioned_docs/version-0.13/02-standard-library/15-advanced-formatting.md +++ b/website/versioned_docs/version-0.13/02-standard-library/15-advanced-formatting.md @@ -1,7 +1,3 @@ ---- -pagination_next: build-system/build-modes ---- - # Advanced Formatting So far we have only covered formatting specifiers. Format strings actually diff --git a/website/versioned_docs/version-0.13/02-standard-library/16-http.mdx b/website/versioned_docs/version-0.13/02-standard-library/16-http.mdx new file mode 100644 index 0000000..5e2c3b8 --- /dev/null +++ b/website/versioned_docs/version-0.13/02-standard-library/16-http.mdx @@ -0,0 +1,23 @@ +--- +pagination_next: build-system/build-modes +--- + +import CodeBlock from "@theme/CodeBlock"; + +import HttpSendRequest from "!!raw-loader!./16.http-send-request.zig"; +import HttpReceiveResponse from "!!raw-loader!./16.http-receive-response.zig"; + +# HTTP + +## Client + +We have already learned a lot about [Allocators](/standard-library/allocators), [ArrayLists](/standard-library/arraylist), and [JSON](/standard-library/json) parsing, let's combine our knowledge and send our first HTTP request with Zig + +{HttpSendRequest} + + +## Server + +To showcase spinning-up the http server we're going to use the approach from the [Threads](/standard-library/threads) chapter which we read before + +{HttpReceiveResponse} diff --git a/website/versioned_docs/version-0.13/02-standard-library/16.http-receive-response.zig b/website/versioned_docs/version-0.13/02-standard-library/16.http-receive-response.zig new file mode 100644 index 0000000..d03623a --- /dev/null +++ b/website/versioned_docs/version-0.13/02-standard-library/16.http-receive-response.zig @@ -0,0 +1,50 @@ +const std = @import("std"); + +const http = std.http; +const net = std.net; +const expect = std.testing.expect; +const test_allocator = std.testing.allocator; + +fn run_server(server: *net.Server) !void { + while (true) { + var conn = try server.accept(); + defer conn.stream.close(); + + var read_buffer: [1024]u8 = undefined; + var http_server = http.Server.init(conn, &read_buffer); + + var request = try http_server.receiveHead(); + + try request.respond("Hello from http server!\n", .{}); + } +} + +test "http receive response" { + // Create a TCP server listening on localhost:8000. + const addr = net.Address.initIp4([4]u8{ 127, 0, 0, 1 }, 8000); + + var server = try addr.listen(.{}); + + // Spawn a thread to run the server. + const thread = try std.Thread.spawn(.{}, run_server, .{&server}); + _ = thread; // Ignore the thread handle. + + // Use the same client code as in the previous example. + var client = http.Client{ .allocator = test_allocator }; + defer client.deinit(); + + var body = std.ArrayList(u8).init(test_allocator); + errdefer body.deinit(); + + const fetch_result = try client.fetch(.{ + .location = .{ .url = "http://127.0.0.1:8000" }, + .response_storage = .{ .dynamic = &body }, + }); + + const owned_body = try body.toOwnedSlice(); + defer test_allocator.free(owned_body); + + // Check that the request was successful and the response body is correct. + try expect(fetch_result.status == http.Status.ok); + try expect(std.mem.eql(u8, owned_body, "Hello from http server!\n")); +} diff --git a/website/versioned_docs/version-0.13/02-standard-library/16.http-send-request.zig b/website/versioned_docs/version-0.13/02-standard-library/16.http-send-request.zig new file mode 100644 index 0000000..36011ad --- /dev/null +++ b/website/versioned_docs/version-0.13/02-standard-library/16.http-send-request.zig @@ -0,0 +1,51 @@ +const std = @import("std"); + +const http = std.http; +const expect = std.testing.expect; +const test_allocator = std.testing.allocator; + +const Post = struct { id: u8, userId: u8, title: []u8, body: []u8 }; + +test "http send request" { + // Create a client with a custom allocator. + var client = http.Client{ .allocator = test_allocator }; + defer client.deinit(); + + // Allocate a buffer to store the response body. + var body = std.ArrayList(u8).init(test_allocator); + errdefer body.deinit(); + + // Send a GET request to the specified URL. + const fetch_result = try client.fetch(.{ + .location = .{ .url = "https://jsonplaceholder.typicode.com/posts/1" }, + .response_storage = .{ .dynamic = &body }, + }); + + // Check that the request was successful. + try expect(fetch_result.status == http.Status.ok); + + // Convert the response body to an owned slice. + const owned_body = try body.toOwnedSlice(); + defer test_allocator.free(owned_body); + + // Parse the response body as a JSON object. + const parsed = try std.json.parseFromSlice( + Post, + test_allocator, + owned_body, + .{}, + ); + defer parsed.deinit(); + + const post = parsed.value; + + // Check that the response body was parsed correctly. + try expect(post.userId == 1); + try expect(post.id == 1); + try expect(std.mem.eql(u8, post.title, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit")); + try expect(std.mem.eql( + u8, + post.body, + "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + )); +} diff --git a/website/versioned_docs/version-0.13/03-build-system/01-build-modes.md b/website/versioned_docs/version-0.13/03-build-system/01-build-modes.md index 1328a2b..1db4254 100644 --- a/website/versioned_docs/version-0.13/03-build-system/01-build-modes.md +++ b/website/versioned_docs/version-0.13/03-build-system/01-build-modes.md @@ -1,5 +1,5 @@ --- -pagination_prev: standard-library/advanced-formatting +pagination_prev: standard-library/http --- # Build Modes diff --git a/website/versioned_docs/version-0.14/02-standard-library/15-advanced-formatting.md b/website/versioned_docs/version-0.14/02-standard-library/15-advanced-formatting.md index e509a21..e8a48c2 100644 --- a/website/versioned_docs/version-0.14/02-standard-library/15-advanced-formatting.md +++ b/website/versioned_docs/version-0.14/02-standard-library/15-advanced-formatting.md @@ -1,7 +1,3 @@ ---- -pagination_next: build-system/build-modes ---- - # Advanced Formatting So far we have only covered formatting specifiers. Format strings actually diff --git a/website/versioned_docs/version-0.14/02-standard-library/16-http.mdx b/website/versioned_docs/version-0.14/02-standard-library/16-http.mdx new file mode 100644 index 0000000..5e2c3b8 --- /dev/null +++ b/website/versioned_docs/version-0.14/02-standard-library/16-http.mdx @@ -0,0 +1,23 @@ +--- +pagination_next: build-system/build-modes +--- + +import CodeBlock from "@theme/CodeBlock"; + +import HttpSendRequest from "!!raw-loader!./16.http-send-request.zig"; +import HttpReceiveResponse from "!!raw-loader!./16.http-receive-response.zig"; + +# HTTP + +## Client + +We have already learned a lot about [Allocators](/standard-library/allocators), [ArrayLists](/standard-library/arraylist), and [JSON](/standard-library/json) parsing, let's combine our knowledge and send our first HTTP request with Zig + +{HttpSendRequest} + + +## Server + +To showcase spinning-up the http server we're going to use the approach from the [Threads](/standard-library/threads) chapter which we read before + +{HttpReceiveResponse} diff --git a/website/versioned_docs/version-0.14/02-standard-library/16.http-receive-response.zig b/website/versioned_docs/version-0.14/02-standard-library/16.http-receive-response.zig new file mode 100644 index 0000000..d03623a --- /dev/null +++ b/website/versioned_docs/version-0.14/02-standard-library/16.http-receive-response.zig @@ -0,0 +1,50 @@ +const std = @import("std"); + +const http = std.http; +const net = std.net; +const expect = std.testing.expect; +const test_allocator = std.testing.allocator; + +fn run_server(server: *net.Server) !void { + while (true) { + var conn = try server.accept(); + defer conn.stream.close(); + + var read_buffer: [1024]u8 = undefined; + var http_server = http.Server.init(conn, &read_buffer); + + var request = try http_server.receiveHead(); + + try request.respond("Hello from http server!\n", .{}); + } +} + +test "http receive response" { + // Create a TCP server listening on localhost:8000. + const addr = net.Address.initIp4([4]u8{ 127, 0, 0, 1 }, 8000); + + var server = try addr.listen(.{}); + + // Spawn a thread to run the server. + const thread = try std.Thread.spawn(.{}, run_server, .{&server}); + _ = thread; // Ignore the thread handle. + + // Use the same client code as in the previous example. + var client = http.Client{ .allocator = test_allocator }; + defer client.deinit(); + + var body = std.ArrayList(u8).init(test_allocator); + errdefer body.deinit(); + + const fetch_result = try client.fetch(.{ + .location = .{ .url = "http://127.0.0.1:8000" }, + .response_storage = .{ .dynamic = &body }, + }); + + const owned_body = try body.toOwnedSlice(); + defer test_allocator.free(owned_body); + + // Check that the request was successful and the response body is correct. + try expect(fetch_result.status == http.Status.ok); + try expect(std.mem.eql(u8, owned_body, "Hello from http server!\n")); +} diff --git a/website/versioned_docs/version-0.14/02-standard-library/16.http-send-request.zig b/website/versioned_docs/version-0.14/02-standard-library/16.http-send-request.zig new file mode 100644 index 0000000..36011ad --- /dev/null +++ b/website/versioned_docs/version-0.14/02-standard-library/16.http-send-request.zig @@ -0,0 +1,51 @@ +const std = @import("std"); + +const http = std.http; +const expect = std.testing.expect; +const test_allocator = std.testing.allocator; + +const Post = struct { id: u8, userId: u8, title: []u8, body: []u8 }; + +test "http send request" { + // Create a client with a custom allocator. + var client = http.Client{ .allocator = test_allocator }; + defer client.deinit(); + + // Allocate a buffer to store the response body. + var body = std.ArrayList(u8).init(test_allocator); + errdefer body.deinit(); + + // Send a GET request to the specified URL. + const fetch_result = try client.fetch(.{ + .location = .{ .url = "https://jsonplaceholder.typicode.com/posts/1" }, + .response_storage = .{ .dynamic = &body }, + }); + + // Check that the request was successful. + try expect(fetch_result.status == http.Status.ok); + + // Convert the response body to an owned slice. + const owned_body = try body.toOwnedSlice(); + defer test_allocator.free(owned_body); + + // Parse the response body as a JSON object. + const parsed = try std.json.parseFromSlice( + Post, + test_allocator, + owned_body, + .{}, + ); + defer parsed.deinit(); + + const post = parsed.value; + + // Check that the response body was parsed correctly. + try expect(post.userId == 1); + try expect(post.id == 1); + try expect(std.mem.eql(u8, post.title, "sunt aut facere repellat provident occaecati excepturi optio reprehenderit")); + try expect(std.mem.eql( + u8, + post.body, + "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + )); +} diff --git a/website/versioned_docs/version-0.14/03-build-system/01-build-modes.md b/website/versioned_docs/version-0.14/03-build-system/01-build-modes.md index 1328a2b..1db4254 100644 --- a/website/versioned_docs/version-0.14/03-build-system/01-build-modes.md +++ b/website/versioned_docs/version-0.14/03-build-system/01-build-modes.md @@ -1,5 +1,5 @@ --- -pagination_prev: standard-library/advanced-formatting +pagination_prev: standard-library/http --- # Build Modes