- Added DefaultCryptoHmac class implementing ICryptoHmac interface. - Introduced purpose-based HMAC computation methods. - Implemented verification methods for HMACs with constant-time comparison. - Created HmacAlgorithms and HmacPurpose classes for well-known identifiers. - Added compliance profile support for HMAC algorithms. - Included asynchronous methods for HMAC computation from streams.
StellaOps Router Example
This example demonstrates the StellaOps Router, Gateway, and Microservice SDK working together.
Overview
The example includes:
- Examples.Gateway - HTTP gateway that routes requests to microservices
- Examples.Billing.Microservice - Sample billing service with typed and streaming endpoints
- Examples.Inventory.Microservice - Sample inventory service demonstrating multi-service routing
- Examples.Integration.Tests - End-to-end integration tests
Prerequisites
- .NET 10 SDK
- Docker and Docker Compose (for containerized deployment)
Project Structure
examples/router/
├── Examples.Router.sln
├── docker-compose.yaml
├── README.md
├── src/
│ ├── Examples.Gateway/
│ │ ├── Program.cs
│ │ ├── router.yaml
│ │ └── appsettings.json
│ ├── Examples.Billing.Microservice/
│ │ ├── Program.cs
│ │ ├── microservice.yaml
│ │ └── Endpoints/
│ │ ├── CreateInvoiceEndpoint.cs
│ │ ├── GetInvoiceEndpoint.cs
│ │ └── UploadAttachmentEndpoint.cs
│ └── Examples.Inventory.Microservice/
│ ├── Program.cs
│ └── Endpoints/
│ ├── ListItemsEndpoint.cs
│ └── GetItemEndpoint.cs
└── tests/
└── Examples.Integration.Tests/
Running Locally
Build the Solution
cd examples/router
dotnet build Examples.Router.sln
Run with Docker Compose
docker-compose up --build
This starts:
- Gateway on port 8080 (HTTP) and 5100 (TCP transport)
- Billing microservice
- Inventory microservice
- RabbitMQ (optional, for message-based transport)
Run Without Docker
Start each service in separate terminals:
# Terminal 1: Gateway
cd src/Examples.Gateway
dotnet run
# Terminal 2: Billing Microservice
cd src/Examples.Billing.Microservice
dotnet run
# Terminal 3: Inventory Microservice
cd src/Examples.Inventory.Microservice
dotnet run
Example API Calls
Billing Service
Create an invoice:
curl -X POST http://localhost:8080/invoices \
-H "Content-Type: application/json" \
-d '{"customerId": "CUST-001", "amount": 99.99, "description": "Service fee"}'
Get an invoice:
curl http://localhost:8080/invoices/INV-12345
Upload an attachment (streaming):
curl -X POST http://localhost:8080/invoices/INV-12345/attachments \
-H "Content-Type: application/octet-stream" \
--data-binary @document.pdf
Inventory Service
List items:
curl "http://localhost:8080/items?page=1&pageSize=20"
List items by category:
curl "http://localhost:8080/items?category=widgets"
Get a specific item:
curl http://localhost:8080/items/SKU-001
Adding New Endpoints
1. Create the Endpoint Class
using StellaOps.Microservice;
[StellaEndpoint("POST", "/orders", TimeoutSeconds = 30)]
public sealed class CreateOrderEndpoint : IStellaEndpoint<CreateOrderRequest, CreateOrderResponse>
{
public Task<CreateOrderResponse> HandleAsync(
CreateOrderRequest request,
CancellationToken cancellationToken)
{
// Implementation
return Task.FromResult(new CreateOrderResponse { OrderId = "ORD-123" });
}
}
2. Register in Program.cs
builder.Services.AddScoped<CreateOrderEndpoint>();
3. Update router.yaml (if needed)
Add routing rules for the new endpoint path.
Streaming Endpoints
For endpoints that handle large payloads (file uploads, etc.), implement IRawStellaEndpoint:
[StellaEndpoint("POST", "/files/{id}", SupportsStreaming = true)]
public sealed class UploadFileEndpoint : IRawStellaEndpoint
{
public async Task<RawResponse> HandleAsync(
RawRequestContext context,
CancellationToken cancellationToken)
{
var id = context.PathParameters["id"];
// Stream body directly without buffering
await using var stream = context.Body;
// Process stream...
return RawResponse.Ok("{}");
}
}
Cancellation Behavior
All endpoints receive a CancellationToken that is triggered when:
- The client disconnects
- The request timeout is exceeded
- The gateway shuts down
Always respect the cancellation token in long-running operations:
public async Task<Response> HandleAsync(Request request, CancellationToken ct)
{
// Check cancellation periodically
ct.ThrowIfCancellationRequested();
// Or pass to async operations
await SomeLongOperation(ct);
}
Payload Limits
Default limits are configured in router.yaml:
payloadLimits:
maxRequestBodySizeBytes: 10485760 # 10 MB
maxChunkSizeBytes: 65536 # 64 KB
For streaming endpoints, the body is not buffered so these limits apply per-chunk.
Running Tests
cd tests/Examples.Integration.Tests
dotnet test
The integration tests verify:
- End-to-end request routing
- Multi-service registration
- Streaming uploads
- Request cancellation
- Payload limit enforcement
Configuration
Gateway (router.yaml)
# Microservice routing rules
services:
billing:
routes:
- path: /invoices
methods: [GET, POST]
- path: /invoices/{id}
methods: [GET, PUT, DELETE]
- path: /invoices/{id}/attachments
methods: [POST]
inventory:
routes:
- path: /items
methods: [GET]
- path: /items/{sku}
methods: [GET]
Microservice (microservice.yaml)
service:
name: billing
version: 1.0.0
region: demo
endpoints:
- path: /invoices
method: POST
timeoutSeconds: 30
- path: /invoices/{id}
method: GET
timeoutSeconds: 10
routers:
- host: localhost
port: 5100
transportType: InMemory
Troubleshooting
Microservice not registering
Check that:
- Gateway is running and healthy
- Router host/port in microservice.yaml matches gateway
- Network connectivity between services
Request timeouts
Increase the timeout in the endpoint attribute:
[StellaEndpoint("POST", "/long-operation", TimeoutSeconds = 120)]
Streaming not working
Ensure the endpoint:
- Is marked with
SupportsStreaming = true - Implements
IRawStellaEndpoint - Does not buffer the entire body before processing
License
AGPL-3.0-or-later