Last active 1719876461

This is a http server that serves a infinitely big file filled with random content

adridoesthings's Avatar adridoesthings revised this gist 1719876461. Go to revision

1 file changed, 1 insertion, 1 deletion

unlimited-download.zig

@@ -102,7 +102,7 @@ pub fn main() !void {
102 102 unreachable;
103 103 }
104 104
105 - const listen_address = try std.net.Address.resolveIp(args[3], try std.fmt.parseInt(u16, args[4], 10));
105 + const listen_address = try std.net.Address.parseIp(args[3], try std.fmt.parseInt(u16, args[4], 10));
106 106 var server = try listen_address.listen(.{ .reuse_address = true });
107 107 defer server.deinit();
108 108 std.log.info("Listening on http://{s}:{s}", .{ args[3], args[4] });

adridoesthings's Avatar adridoesthings revised this gist 1719864389. Go to revision

No changes

adridoesthings's Avatar adridoesthings revised this gist 1719864300. Go to revision

No changes

AdriDoesThings revised this gist 1719864205. Go to revision

1 file changed, 114 insertions

unlimited-download.zig(file created)

@@ -0,0 +1,114 @@
1 + const std = @import("std");
2 + const eql = std.mem.eql;
3 +
4 + pub const std_options = .{
5 + .log_level = .info,
6 + };
7 +
8 + fn http_error(writer: std.net.Stream.Writer, status_code: u16) !void {
9 + const status = switch (status_code) {
10 + 200 => "OK",
11 + 400 => "Bad Request",
12 + 404 => "Not Found",
13 + 405 => "Method Not Allowed",
14 + else => @panic("Invalid status"),
15 + };
16 +
17 + try writer.print("HTTP/1.1 {d} {s}\r\nServer: zig\r\nContent-Type: text/plain\r\nContent-Length: {d}\r\n\r\n{s}", .{ status_code, status, status.len, status });
18 + }
19 +
20 + fn format_bytes(buf: []u8, bytes: usize) ![]u8 {
21 + const fbytes: f32 = @floatFromInt(bytes);
22 + return switch (bytes) {
23 + 0...1024 => std.fmt.bufPrint(buf, "{d} B", .{bytes}),
24 + 1025...(1024 * 1024) => std.fmt.bufPrint(buf, "{d:.1} KiB", .{fbytes / 1024}),
25 + (1024 * 1024 + 1)...(1024 * 1024 * 1024) => std.fmt.bufPrint(buf, "{d:.1} MiB", .{fbytes / 1024 / 1024}),
26 + else => std.fmt.bufPrint(buf, "{d:.1} GiB", .{fbytes / 1024 / 1024 / 1024}),
27 + };
28 + }
29 +
30 + fn handle_connection(connection: std.net.Server.Connection, path: []u8, filename: []u8) !void {
31 + const writer = connection.stream.writer();
32 +
33 + {
34 + var buffer: [255]u8 = undefined;
35 + const n = try connection.stream.read(&buffer);
36 + if (n < 12) {
37 + return http_error(writer, 400);
38 + }
39 +
40 + if (!eql(u8, buffer[0..4], "GET ")) {
41 + return http_error(writer, 405);
42 + }
43 +
44 + if (n < (11 + path.len) or !eql(u8, buffer[4 .. 4 + path.len], path)) {
45 + return http_error(writer, 404);
46 + }
47 + }
48 +
49 + var prng = std.rand.DefaultPrng.init(blk: {
50 + var seed: u64 = undefined;
51 + try std.posix.getrandom(std.mem.asBytes(&seed));
52 + break :blk seed;
53 + });
54 + const random = prng.random();
55 +
56 + std.log.info("Client {} connected", .{connection.address});
57 + defer {
58 + connection.stream.close();
59 + std.log.info("Client {} disconnected", .{connection.address});
60 + }
61 +
62 + try writer.print("HTTP/1.1 200 OK\r\nServer: zig\r\nContent-Type: application/octet-stream\r\nContent-Disposition: attachment; filename=\"{s}\"\r\n\r\n", .{filename});
63 +
64 + var downloaded: usize = 0;
65 + var out_counter: usize = 0;
66 +
67 + while (true) {
68 + var writeBuffer: [1024]u8 = undefined;
69 + random.bytes(&writeBuffer);
70 + const wrote = connection.stream.write(&writeBuffer) catch |err| switch (err) {
71 + error.BrokenPipe => break,
72 + error.ConnectionResetByPeer => break,
73 + else => return err,
74 + };
75 + downloaded += wrote;
76 + out_counter += wrote;
77 +
78 + if (out_counter >= 1024 * 1024 * 100) {
79 + var buf: [16]u8 = undefined;
80 + const downloaded_fmt = try format_bytes(&buf, downloaded);
81 + std.log.info("Client {} downloaded {s}", .{ connection.address, downloaded_fmt });
82 + out_counter = 0;
83 + }
84 + }
85 + }
86 +
87 + pub fn main() !void {
88 + var gpa = std.heap.GeneralPurposeAllocator(.{}){};
89 + const allocator = gpa.allocator();
90 + defer {
91 + const check = gpa.deinit();
92 + if (check == .leak) @panic("memory leaks");
93 + }
94 +
95 + const args = try std.process.argsAlloc(allocator);
96 + defer std.process.argsFree(allocator, args);
97 +
98 + if (args.len != 5) {
99 + const writer = std.io.getStdErr().writer();
100 + try writer.print("Usage: {s} DOWNLOAD_PATH DOWNLOAD_FILENAME ADDRESS PORT\n", .{args[0]});
101 + std.process.exit(1);
102 + unreachable;
103 + }
104 +
105 + const listen_address = try std.net.Address.resolveIp(args[3], try std.fmt.parseInt(u16, args[4], 10));
106 + var server = try listen_address.listen(.{ .reuse_address = true });
107 + defer server.deinit();
108 + std.log.info("Listening on http://{s}:{s}", .{ args[3], args[4] });
109 +
110 + while (true) {
111 + const connection = try server.accept();
112 + _ = try std.Thread.spawn(.{}, handle_connection, .{ connection, args[1], args[2] });
113 + }
114 + }
Newer Older