1
1
using System . Buffers . Binary ;
2
2
using System . Diagnostics . CodeAnalysis ;
3
+ using System . Reflection . PortableExecutable ;
3
4
using System . Text ;
4
5
using System . Text . Json ;
6
+ using System . Text . Json . Serialization ;
5
7
using System . Text . Json . Serialization . Metadata ;
6
8
7
9
namespace Extism ;
@@ -42,6 +44,7 @@ public static byte[] GetInput()
42
44
return buffer ;
43
45
}
44
46
47
+
45
48
/// <summary>
46
49
/// Read the input data sent by the host as a UTF-8 encoded string.
47
50
/// </summary>
@@ -79,7 +82,7 @@ public static void SetOutput(MemoryBlock block)
79
82
/// Set the output data to be sent back to the host as a byte buffer.
80
83
/// </summary>
81
84
/// <param name="data">The byte buffer to set as output data.</param>
82
- public unsafe static void SetOutput ( ReadOnlySpan < byte > data )
85
+ public static unsafe void SetOutput ( ReadOnlySpan < byte > data )
83
86
{
84
87
fixed ( byte * ptr = data )
85
88
{
@@ -133,6 +136,10 @@ public static void SetError(string errorMessage)
133
136
public static MemoryBlock Allocate ( ulong length )
134
137
{
135
138
var offset = Native . extism_alloc ( length ) ;
139
+ if ( offset == 0 && length > 0 )
140
+ {
141
+ throw new InvalidOperationException ( "Failed to allocate memory block." ) ;
142
+ }
136
143
137
144
return new MemoryBlock ( offset , length ) ;
138
145
}
@@ -203,8 +210,17 @@ public static bool TryGetConfig(string key, [NotNullWhen(true)] out string value
203
210
/// <param name="block">The memory block containing the log message.</param>
204
211
public static void Log ( LogLevel level , MemoryBlock block )
205
212
{
213
+ if ( level < ( LogLevel ) Native . extism_get_log_level ( ) )
214
+ {
215
+ return ;
216
+ }
217
+
206
218
switch ( level )
207
219
{
220
+ case LogLevel . Trace :
221
+ Native . extism_log_trace ( block . Offset ) ;
222
+ break ;
223
+
208
224
case LogLevel . Info :
209
225
Native . extism_log_info ( block . Offset ) ;
210
226
break ;
@@ -230,6 +246,11 @@ public static void Log(LogLevel level, MemoryBlock block)
230
246
/// <param name="message"></param>
231
247
public static void Log ( LogLevel level , string message )
232
248
{
249
+ if ( level < ( LogLevel ) Native . extism_get_log_level ( ) )
250
+ {
251
+ return ;
252
+ }
253
+
233
254
var block = Allocate ( message ) ;
234
255
Log ( level , block ) ;
235
256
}
@@ -306,36 +327,31 @@ public static void RemoveVar(string key)
306
327
/// <returns>The HTTP response received from the host. The plugin takes ownership of the memory block and is expected to free it.</returns>
307
328
public static HttpResponse SendRequest ( HttpRequest request )
308
329
{
309
- using var stream = new MemoryStream ( ) ;
310
- using ( var writer = new Utf8JsonWriter ( stream ) )
311
- {
312
- writer . WriteStartObject ( ) ;
313
- writer . WriteString ( "url" , request . Url . AbsoluteUri ) ;
314
- writer . WriteString ( "method" , Enum . GetName ( typeof ( HttpMethod ) , request . Method ) ) ;
330
+ var requestJson = JsonSerializer . Serialize ( request , JsonContext . Default . HttpRequest ) ;
315
331
316
- if ( request . Headers . Count > 0 )
317
- {
318
- writer . WriteStartObject ( "headers" ) ;
319
- foreach ( var kvp in request . Headers )
320
- {
321
- writer . WriteString ( kvp . Key , kvp . Value ) ;
322
- }
323
- writer . WriteEndObject ( ) ;
324
- }
332
+ using var requestBlock = Allocate ( requestJson ) ;
333
+ using var bodyBlock = Allocate ( request . Body ) ;
325
334
326
- writer . WriteEndObject ( ) ;
335
+ var responseOffset = Native . extism_http_request ( requestBlock . Offset , bodyBlock . Offset ) ;
336
+ if ( responseOffset == 0 )
337
+ {
338
+ throw new InvalidOperationException ( "Failed to send HTTP request." ) ;
327
339
}
328
340
329
- var bytes = stream . ToArray ( ) ;
341
+ var responseBody = MemoryBlock . Find ( responseOffset ) ;
342
+ var status = Native . extism_http_status_code ( ) ;
343
+ var httpResponse = new HttpResponse ( responseBody , status ) ;
330
344
331
- var requestBlock = Allocate ( bytes ) ;
332
- var bodyBlock = Allocate ( request . Body ) ;
345
+ var headersOffset = Native . extism_http_headers ( ) ;
346
+ if ( headersOffset > 0 )
347
+ {
348
+ using var headersBlock = MemoryBlock . Find ( headersOffset ) ;
349
+ var headersJson = headersBlock . ReadString ( ) ;
333
350
334
- var offset = Native . extism_http_request ( requestBlock . Offset , bodyBlock . Offset ) ;
335
- var block = MemoryBlock . Find ( offset ) ;
336
- var status = Native . extism_http_status_code ( ) ;
351
+ httpResponse . Headers = JsonSerializer . Deserialize ( headersJson , JsonContext . Default . DictionaryStringString ) ?? [ ] ;
352
+ }
337
353
338
- return new HttpResponse ( block , status ) ;
354
+ return httpResponse ;
339
355
}
340
356
}
341
357
@@ -345,24 +361,29 @@ public static HttpResponse SendRequest(HttpRequest request)
345
361
public enum LogLevel
346
362
{
347
363
/// <summary>
348
- /// Information
364
+ /// Trace
349
365
/// </summary>
350
- Info ,
366
+ Trace = 0 ,
351
367
352
368
/// <summary>
353
369
/// Debug
354
370
/// </summary>
355
- Debug ,
371
+ Debug = 1 ,
372
+
373
+ /// <summary>
374
+ /// Information
375
+ /// </summary>
376
+ Info = 2 ,
356
377
357
378
/// <summary>
358
379
/// Warning
359
380
/// </summary>
360
- Warn ,
381
+ Warn = 3 ,
361
382
362
383
/// <summary>
363
384
/// Error
364
385
/// </summary>
365
- Error
386
+ Error = 4 ,
366
387
}
367
388
368
389
/// <summary>
@@ -391,21 +412,26 @@ public HttpRequest(string url)
391
412
/// <summary>
392
413
/// HTTP URL
393
414
/// </summary>
415
+ [ JsonPropertyName ( "url" ) ]
394
416
public Uri Url { get ; set ; }
395
417
396
418
/// <summary>
397
419
/// HTTP Headers
398
420
/// </summary>
421
+ [ JsonPropertyName ( "headers" ) ]
399
422
public Dictionary < string , string > Headers { get ; } = new ( ) ;
400
423
401
424
/// <summary>
402
425
/// HTTP method
403
426
/// </summary>
427
+ [ JsonPropertyName ( "method" ) ]
428
+ [ JsonConverter ( typeof ( JsonStringEnumConverter < HttpMethod > ) ) ]
404
429
public HttpMethod Method { get ; set ; } = HttpMethod . GET ;
405
430
406
431
/// <summary>
407
432
/// An optional body.
408
433
/// </summary>
434
+ [ JsonIgnore ]
409
435
public byte [ ] Body { get ; set ; } = Array . Empty < byte > ( ) ;
410
436
}
411
437
@@ -471,6 +497,11 @@ public HttpResponse(MemoryBlock memory, ushort status)
471
497
/// </summary>
472
498
public ushort Status { get ; set ; }
473
499
500
+ /// <summary>
501
+ /// HTTP Headers. Make sure HTTP response headers are enabled in the host.
502
+ /// </summary>
503
+ public Dictionary < string , string > Headers { get ; set ; } = new ( ) ;
504
+
474
505
/// <summary>
475
506
/// Frees the current memory block.
476
507
/// </summary>
@@ -534,6 +565,8 @@ public MemoryBlock(ulong offset, ulong length)
534
565
/// <exception cref="InvalidOperationException"></exception>
535
566
public unsafe void CopyTo ( Span < byte > buffer )
536
567
{
568
+ CheckDisposed ( ) ;
569
+
537
570
if ( ( ulong ) buffer . Length < Length )
538
571
{
539
572
throw new InvalidOperationException ( $ "Buffer must be at least ${ Length } bytes.") ;
@@ -562,7 +595,9 @@ public void WriteString(string text)
562
595
/// <exception cref="IndexOutOfRangeException"></exception>
563
596
public unsafe void WriteBytes ( ReadOnlySpan < byte > bytes )
564
597
{
565
- if ( ( ulong ) bytes . Length > Length )
598
+ CheckDisposed ( ) ;
599
+
600
+ if ( ( ulong ) bytes . Length > Length )
566
601
{
567
602
throw new IndexOutOfRangeException ( "Memory block is not big enough." ) ;
568
603
}
@@ -579,6 +614,8 @@ public unsafe void WriteBytes(ReadOnlySpan<byte> bytes)
579
614
/// <returns></returns>
580
615
public byte [ ] ReadBytes ( )
581
616
{
617
+ CheckDisposed ( ) ;
618
+
582
619
var buffer = new byte [ Length ] ;
583
620
CopyTo ( buffer ) ;
584
621
@@ -606,6 +643,15 @@ public static MemoryBlock Find(ulong offset)
606
643
return new MemoryBlock ( offset , length ) ;
607
644
}
608
645
646
+ private void CheckDisposed ( )
647
+ {
648
+ if ( _disposed )
649
+ {
650
+ throw new ObjectDisposedException ( nameof ( MemoryBlock ) ) ;
651
+ }
652
+ }
653
+
654
+ private bool _disposed ;
609
655
/// <summary>
610
656
/// Frees the current memory block.
611
657
/// </summary>
@@ -617,6 +663,13 @@ public void Dispose()
617
663
618
664
private void Dispose ( bool disposing )
619
665
{
666
+ if ( _disposed )
667
+ {
668
+ return ;
669
+ }
670
+
671
+ _disposed = true ;
672
+
620
673
if ( disposing )
621
674
{
622
675
// free managed resources
@@ -628,3 +681,10 @@ private void Dispose(bool disposing)
628
681
}
629
682
}
630
683
}
684
+
685
+ [ JsonSerializable ( typeof ( HttpRequest ) ) ]
686
+ [ JsonSerializable ( typeof ( Dictionary < string , string > ) ) ]
687
+ internal partial class JsonContext : JsonSerializerContext
688
+ {
689
+
690
+ }
0 commit comments