Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: CI/CD with Benchmarks

env:
ZIG_VERSION: 0.15.2
ZIG_VERSION: 0.17.0

on:
push:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.zig-cache/
zig-out/
zig-pkg/

# Benchmark results
benchmark_results.json
Expand Down
56 changes: 37 additions & 19 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn build(b: *std.Build) void {

for (zlib_sources) |src| {
const src_path = zlib_dep.path(src);
zlib_lib.addCSourceFile(.{
zlib_lib.root_module.addCSourceFile(.{
.file = src_path,
.flags = &[_][]const u8{
"-DHAVE_SYS_TYPES_H",
Expand All @@ -51,27 +51,41 @@ pub fn build(b: *std.Build) void {
});
}

zlib_lib.linkLibC();
zlib_lib.root_module.link_libc = true;
zlib_lib.installHeadersDirectory(
zlib_dep.path("."),
".",
.{ .include_extensions = &[_][]const u8{ ".h" } },
.{ .include_extensions = &[_][]const u8{".h"} },
);

const zlib_translate_c = b.addTranslateC(.{
.root_source_file = zlib_dep.path("zlib.h"),
.target = target,
.optimize = optimize,
});
zlib_translate_c.addIncludePath(zlib_dep.path("."));
const zlib_mod = zlib_translate_c.createModule();

// Server module (for internal use and library export)
const server_mod = b.addModule("grpc-server", .{
.root_source_file = b.path("src/server.zig"),
.target = target,
.optimize = optimize,
.imports = &.{.{ .name = "spice", .module = spice_mod }},
.imports = &.{
.{ .name = "spice", .module = spice_mod },
.{ .name = "zlib", .module = zlib_mod },
},
});

// Client module (for internal use and library export)
const client_mod = b.addModule("grpc-client", .{
.root_source_file = b.path("src/client.zig"),
.target = target,
.optimize = optimize,
.imports = &.{.{ .name = "spice", .module = spice_mod }},
.imports = &.{
.{ .name = "spice", .module = spice_mod },
.{ .name = "zlib", .module = zlib_mod },
},
});

// Benchmark executable
Expand All @@ -81,10 +95,13 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/benchmark.zig"),
.target = target,
.optimize = optimize,
.imports = &.{.{ .name = "spice", .module = spice_mod }},
.imports = &.{
.{ .name = "spice", .module = spice_mod },
.{ .name = "zlib", .module = zlib_mod },
},
}),
});
benchmark.linkLibrary(zlib_lib);
benchmark.root_module.linkLibrary(zlib_lib);
b.installArtifact(benchmark);

// Benchmark step with automatic server management
Expand All @@ -95,20 +112,18 @@ pub fn build(b: *std.Build) void {
"sh",
"-c",
"trap 'kill $SERVER_PID 2>/dev/null' EXIT; " ++
"./zig-out/bin/grpc-server-example & SERVER_PID=$!; " ++
"sleep 2; " ++
"./zig-out/bin/grpc-benchmark --host localhost --port 50051 --requests 10 --clients 1 --size 512 --output text; " ++
"kill $SERVER_PID 2>/dev/null || true",
"./zig-out/bin/grpc-server-example & SERVER_PID=$!; " ++
"sleep 2; " ++
"./zig-out/bin/grpc-benchmark --host localhost --port 50051 --requests 10 --clients 1 --size 512 --output text; " ++
"kill $SERVER_PID 2>/dev/null || true",
});
benchmark_cmd.step.dependOn(b.getInstallStep());
benchmark_step.dependOn(&benchmark_cmd.step);

// Also keep the standalone benchmark executable for manual testing
const run_benchmark_manual = b.addRunArtifact(benchmark);
run_benchmark_manual.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_benchmark_manual.addArgs(args);
}
run_benchmark_manual.addPassthruArgs();
const benchmark_manual_step = b.step("benchmark-manual", "Run benchmark manually (requires server running)");
benchmark_manual_step.dependOn(&run_benchmark_manual.step);

Expand All @@ -125,7 +140,7 @@ pub fn build(b: *std.Build) void {
},
}),
});
server_example.linkLibrary(zlib_lib);
server_example.root_module.linkLibrary(zlib_lib);
b.installArtifact(server_example);

const client_example = b.addExecutable(.{
Expand All @@ -140,7 +155,7 @@ pub fn build(b: *std.Build) void {
},
}),
});
client_example.linkLibrary(zlib_lib);
client_example.root_module.linkLibrary(zlib_lib);
b.installArtifact(client_example);

// Tests
Expand All @@ -150,10 +165,13 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/tests.zig"),
.target = target,
.optimize = optimize,
.imports = &.{.{ .name = "spice", .module = spice_mod }},
.imports = &.{
.{ .name = "spice", .module = spice_mod },
.{ .name = "zlib", .module = zlib_mod },
},
}),
});
tests.linkLibrary(zlib_lib);
tests.root_module.linkLibrary(zlib_lib);

const run_tests = b.addRunArtifact(tests);
const test_step = b.step("test", "Run tests");
Expand All @@ -180,7 +198,7 @@ pub fn build(b: *std.Build) void {
},
}),
});
integration_test_server.linkLibrary(zlib_lib);
integration_test_server.root_module.linkLibrary(zlib_lib);

const install_integration_test = b.addInstallArtifact(integration_test_server, .{});
const integration_test_step = b.step("integration_test", "Build integration test server");
Expand Down
6 changes: 3 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
.fingerprint = 0xbcaa61f2a4ef59ec,
.dependencies = .{
.spice = .{
.url = "https://github.com/judofyr/spice",
.hash = "spice-0.0.0-3FtxfEq9AAASAwEKfAmQ4uDhc9vkuwHrRkjEeEYx1aCP",
.url = "git+https://github.com/judofyr/spice#39186ac9da2b851f558a33926836e605e238694d",
.hash = "spice-0.0.0-3FtxfKu_AADWhWk0I60jjBhn7y-d3K_49oefAeOWwx_S",
},
.zlib = .{
.url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz",
.hash = "1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb",
.hash = "N-V-__8AAB0eQwD-0MdOEBmz7intriBReIsIDNlukNVoNu6o",
},
},
.paths = .{
Expand Down
12 changes: 6 additions & 6 deletions examples/auth.zig
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const std = @import("std");
const auth = @import("features/auth.zig");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
var safe_allocator = std.heap.SafeAllocator.init(init.gpa, .{});
defer _ = safe_allocator.deinit();
const allocator = safe_allocator.allocator();

var auth_handler = auth.Auth.init(allocator, "my-secret-key");
var auth_handler = auth.Auth.init(allocator, init.io, "my-secret-key");

// Generate a token
const token = try auth_handler.generateToken("user123", 3600);
Expand All @@ -22,4 +22,4 @@ pub fn main() !void {
auth_handler.verifyToken("invalid.token.here") catch |err| {
std.debug.print("Expected error: {}\n", .{err});
};
}
}
10 changes: 5 additions & 5 deletions examples/basic_client.zig
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const std = @import("std");
const GrpcClient = @import("grpcclient").GrpcClient;

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
var safe_allocator = std.heap.SafeAllocator.init(init.gpa, .{});
defer _ = safe_allocator.deinit();
const allocator = safe_allocator.allocator();

var client = try GrpcClient.init(allocator, "localhost", 50051);
var client = try GrpcClient.init(allocator, init.io, "localhost", 50051);
defer client.deinit();

// Set authentication
Expand Down
16 changes: 8 additions & 8 deletions examples/basic_server.zig
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const std = @import("std");
const GrpcServer = @import("grpc").GrpcServer;

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
pub fn main(init: std.process.Init) !void {
var safe_allocator = std.heap.SafeAllocator.init(init.gpa, .{});
defer _ = safe_allocator.deinit();
const allocator = safe_allocator.allocator();

var server = try GrpcServer.init(allocator, 50051, "secret-key");
var server = try GrpcServer.init(allocator, init.io, 50051, "secret-key");
defer server.deinit();

// Register handlers
Expand All @@ -30,13 +30,13 @@ pub fn main() !void {
try server.start();
}

fn sayHello(request: []const u8, allocator: std.mem.Allocator) ![]u8 {
fn sayHello(request: []const u8, allocator: std.mem.Allocator, _: std.Io) ![]u8 {
_ = request;
return allocator.dupe(u8, "Hello from gRPC!");
}

fn benchmarkHandler(request: []const u8, allocator: std.mem.Allocator) ![]u8 {
fn benchmarkHandler(request: []const u8, allocator: std.mem.Allocator, io: std.Io) ![]u8 {
// Echo the request back with a timestamp for benchmarking
const response = try std.fmt.allocPrint(allocator, "Echo: {s} (processed at {d})", .{ request, std.time.milliTimestamp() });
const response = try std.fmt.allocPrint(allocator, "Echo: {s} (processed at {d})", .{ request, std.Io.Timestamp.now(io, .real).toMilliseconds() });
return response;
}
8 changes: 4 additions & 4 deletions examples/compression.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const std = @import("std");
const compression = @import("features/compression.zig");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var safe_allocator = std.heap.SafeAllocator.init(init.gpa, .{});
defer _ = safe_allocator.deinit();
const allocator = safe_allocator.allocator();

var comp = compression.Compression.init(allocator);
const data = "Hello, this is some test data for compression!";
Expand All @@ -26,4 +26,4 @@ pub fn main() !void {
std.debug.print("Original size: {}\n", .{data.len});
std.debug.print("Gzip size: {}\n", .{compressed_gzip.len});
std.debug.print("Deflate size: {}\n", .{compressed_deflate.len});
}
}
8 changes: 4 additions & 4 deletions examples/health.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const std = @import("std");
const health = @import("features/health.zig");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var safe_allocator = std.heap.SafeAllocator.init(init.gpa, .{});
defer _ = safe_allocator.deinit();
const allocator = safe_allocator.allocator();

var health_checker = health.HealthCheck.init(allocator);
defer health_checker.deinit();
Expand All @@ -25,4 +25,4 @@ pub fn main() !void {
health_checker.check("unknown.service") catch |err| {
std.debug.print("Expected error for unknown service: {}\n", .{err});
};
}
}
8 changes: 4 additions & 4 deletions examples/http2_transport.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ const http2 = struct {
};

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var safe_allocator = std.heap.SafeAllocator.init(init.gpa, .{});
defer _ = safe_allocator.deinit();
const allocator = safe_allocator.allocator();

// Create a TCP connection (simplified for example)
const stream = try std.net.tcpConnectToHost(allocator, "localhost", 50051);
Expand All @@ -27,4 +27,4 @@ pub fn main() !void {
defer allocator.free(response);

std.debug.print("Received: {s}\n", .{response});
}
}
10 changes: 5 additions & 5 deletions examples/streaming.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const std = @import("std");
const streaming = @import("features/streaming.zig");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var safe_allocator = std.heap.SafeAllocator.init(init.gpa, .{});
defer _ = safe_allocator.deinit();
const allocator = safe_allocator.allocator();

// Create a message stream with buffer size of 5
var stream = streaming.MessageStream.init(allocator, 5);
Expand All @@ -17,7 +17,7 @@ pub fn main() !void {

// Read messages
while (stream.pop()) |msg| {
std.debug.print("Message: {s}, End: {}\n", .{msg.data, msg.is_end});
std.debug.print("Message: {s}, End: {}\n", .{ msg.data, msg.is_end });
allocator.free(msg.data);
}
}
}
21 changes: 17 additions & 4 deletions integration_test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,46 +125,59 @@ def run_tests():
print("=" * 60)
print()

client = SimpleGrpcClient()

# Test 1: Connection
print("Test 1: Server Connection")
print("-" * 60)
client = SimpleGrpcClient()
if not client.connect():
print("\n✗ FAILED: Could not connect to server")
print(" Make sure the server is running on localhost:50052")
return False
client.close()
print()

# Test 2: Echo handler
print("Test 2: Echo Handler")
print("-" * 60)
client = SimpleGrpcClient()
if not client.connect():
print("✗ FAILED: Echo test failed")
return False
test_message = "Hello from Python test client!"
if not client.send_message("Echo", test_message):
print("✗ FAILED: Echo test failed")
client.close()
return False
client.close()
print()

# Test 3: CompressedEcho handler
print("Test 3: CompressedEcho Handler")
print("-" * 60)
client = SimpleGrpcClient()
if not client.connect():
print("✗ FAILED: CompressedEcho test failed")
return False
if not client.send_message("CompressedEcho", "Test compression"):
print("✗ FAILED: CompressedEcho test failed")
client.close()
return False
client.close()
print()

# Test 4: HealthCheck handler
print("Test 4: HealthCheck Handler")
print("-" * 60)
client = SimpleGrpcClient()
if not client.connect():
print("✗ FAILED: HealthCheck test failed")
return False
if not client.send_message("HealthCheck", ""):
print("✗ FAILED: HealthCheck test failed")
client.close()
return False
print()

client.close()
print()

print("=" * 60)
print("✓ All tests passed!")
Expand Down
Loading
Loading