Skip to content

Commit 9ddacad

Browse files
authored
Merge pull request #13 from Kaliumhexacyanoferrat/genhttp-adjustments
[genhttp] Cleanup and optimize code
2 parents ef75559 + ebed9ca commit 9ddacad

11 files changed

Lines changed: 396 additions & 294 deletions

File tree

frameworks/genhttp/Dockerfile

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:10.0-preview-alpine AS build
1+
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build
22
WORKDIR /source
3-
COPY genhttp.csproj .
3+
4+
COPY genhttp.csproj ./
45
RUN dotnet restore -r linux-musl-x64
5-
COPY Program.cs .
6+
7+
COPY . .
8+
69
RUN dotnet publish -c Release -r linux-musl-x64 --self-contained -o /app
710

8-
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-preview-alpine
11+
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine
912
WORKDIR /app
1013
COPY --from=build /app .
14+
1115
RUN apk add --no-cache libmsquic
16+
1217
EXPOSE 8080 8443/tcp 8443/udp
13-
ENTRYPOINT ["./genhttp"]
18+
ENTRYPOINT ["./genhttp"]

frameworks/genhttp/Model.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
namespace genhttp;
2+
3+
public class DatasetItem
4+
{
5+
public int Id { get; set; }
6+
public string Name { get; set; } = "";
7+
public string Category { get; set; } = "";
8+
public double Price { get; set; }
9+
public int Quantity { get; set; }
10+
public bool Active { get; set; }
11+
public List<string> Tags { get; set; } = new();
12+
public RatingInfo Rating { get; set; } = new();
13+
}
14+
15+
public class ProcessedItem
16+
{
17+
public int Id { get; set; }
18+
public string Name { get; set; } = "";
19+
public string Category { get; set; } = "";
20+
public double Price { get; set; }
21+
public int Quantity { get; set; }
22+
public bool Active { get; set; }
23+
public List<string> Tags { get; set; } = new();
24+
public RatingInfo Rating { get; set; } = new();
25+
public double Total { get; set; }
26+
}
27+
28+
public class RatingInfo
29+
{
30+
public double Score { get; set; }
31+
public int Count { get; set; }
32+
}
33+
34+
public class ListWithCount<T>(List<T> items)
35+
{
36+
37+
public List<T> Items => items;
38+
39+
public int Count => items.Count;
40+
41+
}

frameworks/genhttp/Program.cs

Lines changed: 5 additions & 280 deletions
Original file line numberDiff line numberDiff line change
@@ -1,288 +1,13 @@
11
using System.Net;
2-
using System.Security.Cryptography.X509Certificates;
3-
using System.Text.Json;
4-
using GenHTTP.Api.Content;
5-
using GenHTTP.Api.Protocol;
6-
using GenHTTP.Engine.Kestrel;
7-
using GenHTTP.Modules.Compression;
8-
using GenHTTP.Modules.Functional;
9-
using GenHTTP.Modules.IO;
10-
using GenHTTP.Modules.Layouting;
11-
using Microsoft.Data.Sqlite;
122

13-
// JSON options
14-
var jsonOptions = new JsonSerializerOptions
15-
{
16-
PropertyNameCaseInsensitive = true,
17-
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
18-
};
3+
using genhttp;
194

20-
// Load small dataset — keep raw items for per-request processing
21-
var datasetPath = Environment.GetEnvironmentVariable("DATASET_PATH") ?? "/data/dataset.json";
22-
List<DatasetItem>? datasetItems = null;
23-
if (File.Exists(datasetPath))
24-
{
25-
datasetItems = JsonSerializer.Deserialize<List<DatasetItem>>(File.ReadAllText(datasetPath), jsonOptions);
26-
}
5+
using GenHTTP.Engine.Internal;
276

28-
// Load large dataset for compression — pre-serialize to bytes
29-
byte[]? largeJsonBytes = null;
30-
var largePath = "/data/dataset-large.json";
31-
if (File.Exists(largePath))
32-
{
33-
var largeItems = JsonSerializer.Deserialize<List<DatasetItem>>(File.ReadAllText(largePath), jsonOptions);
34-
if (largeItems != null)
35-
{
36-
var processed = largeItems.Select(d => new ProcessedItem
37-
{
38-
Id = d.Id, Name = d.Name, Category = d.Category,
39-
Price = d.Price, Quantity = d.Quantity, Active = d.Active,
40-
Tags = d.Tags, Rating = d.Rating,
41-
Total = Math.Round(d.Price * d.Quantity, 2)
42-
}).ToList();
43-
largeJsonBytes = JsonSerializer.SerializeToUtf8Bytes(new { items = processed, count = processed.Count }, jsonOptions);
44-
}
45-
}
7+
var app = Project.Create();
468

47-
// Pre-load static files
48-
var staticFileMap = new Dictionary<string, (byte[] Data, string ContentType)>();
49-
var staticDir = "/data/static";
50-
if (Directory.Exists(staticDir))
51-
{
52-
var mimeTypes = new Dictionary<string, string>
53-
{
54-
{".css", "text/css"}, {".js", "application/javascript"}, {".html", "text/html"},
55-
{".woff2", "font/woff2"}, {".svg", "image/svg+xml"}, {".webp", "image/webp"}, {".json", "application/json"}
56-
};
57-
foreach (var file in Directory.GetFiles(staticDir))
58-
{
59-
var name = Path.GetFileName(file);
60-
var ext = Path.GetExtension(file);
61-
var ct = mimeTypes.GetValueOrDefault(ext, "application/octet-stream");
62-
staticFileMap[name] = (File.ReadAllBytes(file), ct);
63-
}
64-
}
65-
66-
// Open SQLite database
67-
SqliteConnection? dbConn = null;
68-
var dbPath = "/data/benchmark.db";
69-
if (File.Exists(dbPath))
70-
{
71-
dbConn = new SqliteConnection($"Data Source={dbPath};Mode=ReadOnly");
72-
dbConn.Open();
73-
using var pragma = dbConn.CreateCommand();
74-
pragma.CommandText = "PRAGMA mmap_size=268435456";
75-
pragma.ExecuteNonQuery();
76-
}
77-
78-
// Helper: sum query parameters
79-
static int SumQuery(IRequest request)
80-
{
81-
int sum = 0;
82-
foreach (var (_, value) in request.Query)
83-
{
84-
if (int.TryParse(value, out int n))
85-
sum += n;
86-
}
87-
return sum;
88-
}
89-
90-
// Helper: build a response from a byte array
91-
static IResponse ByteResponse(IRequest request, byte[] data, string contentType)
92-
{
93-
return request.Respond()
94-
.Content(new MemoryStream(data))
95-
.Type(new FlexibleContentType(contentType))
96-
.Length((ulong)data.Length)
97-
.Header("Server", "genhttp")
98-
.Build();
99-
}
100-
101-
// Build the handler tree
102-
var api = Inline.Create()
103-
.Get("/pipeline", (IRequest request) =>
104-
{
105-
return request.Respond()
106-
.Content(Resource.FromString("ok").Build())
107-
.Type(new FlexibleContentType("text/plain"))
108-
.Header("Server", "genhttp")
109-
.Build();
110-
})
111-
.Get("/baseline11", (IRequest request) =>
112-
{
113-
int sum = SumQuery(request);
114-
return request.Respond()
115-
.Content(Resource.FromString(sum.ToString()).Build())
116-
.Type(new FlexibleContentType("text/plain"))
117-
.Header("Server", "genhttp")
118-
.Build();
119-
})
120-
.Post("/baseline11", async (IRequest request) =>
121-
{
122-
int sum = SumQuery(request);
123-
if (request.Content != null)
124-
{
125-
using var reader = new StreamReader(request.Content);
126-
var body = await reader.ReadToEndAsync();
127-
if (int.TryParse(body.Trim(), out int b))
128-
sum += b;
129-
}
130-
return request.Respond()
131-
.Content(Resource.FromString(sum.ToString()).Build())
132-
.Type(new FlexibleContentType("text/plain"))
133-
.Header("Server", "genhttp")
134-
.Build();
135-
})
136-
.Get("/baseline2", (IRequest request) =>
137-
{
138-
int sum = SumQuery(request);
139-
return request.Respond()
140-
.Content(Resource.FromString(sum.ToString()).Build())
141-
.Type(new FlexibleContentType("text/plain"))
142-
.Header("Server", "genhttp")
143-
.Build();
144-
})
145-
.Get("/json", (IRequest request) =>
146-
{
147-
if (datasetItems == null)
148-
return request.Respond().Status(500, "No dataset").Build();
149-
var processed = new List<ProcessedItem>(datasetItems.Count);
150-
foreach (var d in datasetItems)
151-
{
152-
processed.Add(new ProcessedItem
153-
{
154-
Id = d.Id, Name = d.Name, Category = d.Category,
155-
Price = d.Price, Quantity = d.Quantity, Active = d.Active,
156-
Tags = d.Tags, Rating = d.Rating,
157-
Total = Math.Round(d.Price * d.Quantity, 2)
158-
});
159-
}
160-
var json = JsonSerializer.Serialize(new { items = processed, count = processed.Count }, jsonOptions);
161-
return request.Respond()
162-
.Content(Resource.FromString(json).Build())
163-
.Type(new FlexibleContentType("application/json"))
164-
.Header("Server", "genhttp")
165-
.Build();
166-
})
167-
.Get("/compression", (IRequest request) =>
168-
{
169-
if (largeJsonBytes == null)
170-
return request.Respond().Status(500, "No dataset").Build();
171-
return ByteResponse(request, largeJsonBytes, "application/json");
172-
})
173-
.Post("/upload", async (IRequest request) =>
174-
{
175-
using var ms = new MemoryStream();
176-
if (request.Content != null)
177-
await request.Content.CopyToAsync(ms);
178-
return request.Respond()
179-
.Content(Resource.FromString(ms.Length.ToString()).Build())
180-
.Type(new FlexibleContentType("text/plain"))
181-
.Header("Server", "genhttp")
182-
.Build();
183-
})
184-
.Get("/db", (IRequest request) =>
185-
{
186-
if (dbConn == null)
187-
return request.Respond().Status(500, "DB not available").Build();
188-
189-
double min = 10, max = 50;
190-
if (request.Query.TryGetValue("min", out var minStr) && double.TryParse(minStr, out double pmin))
191-
min = pmin;
192-
if (request.Query.TryGetValue("max", out var maxStr) && double.TryParse(maxStr, out double pmax))
193-
max = pmax;
194-
195-
using var cmd = dbConn.CreateCommand();
196-
cmd.CommandText = "SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN @min AND @max LIMIT 50";
197-
cmd.Parameters.AddWithValue("@min", min);
198-
cmd.Parameters.AddWithValue("@max", max);
199-
using var reader = cmd.ExecuteReader();
200-
var items = new List<object>();
201-
while (reader.Read())
202-
{
203-
items.Add(new
204-
{
205-
id = reader.GetInt32(0),
206-
name = reader.GetString(1),
207-
category = reader.GetString(2),
208-
price = reader.GetDouble(3),
209-
quantity = reader.GetInt32(4),
210-
active = reader.GetInt32(5) == 1,
211-
tags = JsonSerializer.Deserialize<List<string>>(reader.GetString(6)),
212-
rating = new { score = reader.GetDouble(7), count = reader.GetInt32(8) },
213-
});
214-
}
215-
var json = JsonSerializer.Serialize(new { items, count = items.Count }, jsonOptions);
216-
return request.Respond()
217-
.Content(Resource.FromString(json).Build())
218-
.Type(new FlexibleContentType("application/json"))
219-
.Header("Server", "genhttp")
220-
.Build();
221-
});
222-
223-
// Static file handler — register each file as a sub-route in a layout
224-
var staticLayout = Layout.Create();
225-
foreach (var (name, (data, contentType)) in staticFileMap)
226-
{
227-
var fileData = data;
228-
var fileContentType = contentType;
229-
staticLayout.Add(name, Inline.Create()
230-
.Get((IRequest request) => ByteResponse(request, fileData, fileContentType)));
231-
}
232-
233-
var layout = Layout.Create()
234-
.Add(api)
235-
.Add("static", staticLayout);
236-
237-
// TLS configuration
238-
var certPath = Environment.GetEnvironmentVariable("TLS_CERT") ?? "/certs/server.crt";
239-
var keyPath = Environment.GetEnvironmentVariable("TLS_KEY") ?? "/certs/server.key";
240-
var hasCert = File.Exists(certPath) && File.Exists(keyPath);
241-
242-
var host = Host.Create()
243-
.Handler(layout)
244-
.Compression(CompressedContent.Default());
9+
var host = Host.Create().Handler(app);
24510

24611
host.Bind(IPAddress.Any, 8080);
24712

248-
if (hasCert)
249-
{
250-
var cert = X509Certificate2.CreateFromPemFile(certPath, keyPath);
251-
host.Bind(IPAddress.Any, 8443, cert, enableQuic: true);
252-
}
253-
254-
await host.RunAsync();
255-
256-
// --- Data models ---
257-
258-
class DatasetItem
259-
{
260-
public int Id { get; set; }
261-
public string Name { get; set; } = "";
262-
public string Category { get; set; } = "";
263-
public double Price { get; set; }
264-
public int Quantity { get; set; }
265-
public bool Active { get; set; }
266-
public List<string> Tags { get; set; } = new();
267-
public RatingInfo Rating { get; set; } = new();
268-
}
269-
270-
class ProcessedItem
271-
{
272-
public int Id { get; set; }
273-
public string Name { get; set; } = "";
274-
public string Category { get; set; } = "";
275-
public double Price { get; set; }
276-
public int Quantity { get; set; }
277-
public bool Active { get; set; }
278-
public List<string> Tags { get; set; } = new();
279-
public RatingInfo Rating { get; set; } = new();
280-
public double Total { get; set; }
281-
}
282-
283-
class RatingInfo
284-
{
285-
public double Score { get; set; }
286-
public int Count { get; set; }
287-
}
288-
13+
await host.RunAsync();

0 commit comments

Comments
 (0)