11using 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
24611host . 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