11namespace SqlStreamStore . HAL
22{
33 using System ;
4+ using System . IO ;
45 using System . Linq ;
6+ using System . Threading ;
57 using System . Threading . Tasks ;
68 using Halcyon . HAL ;
79 using Microsoft . AspNetCore . Builder ;
@@ -57,6 +59,13 @@ private static MidFunc MethodsNotAllowed(params string[] methods) => (context, n
5759 406 ) ) ;
5860 } ;
5961
62+ private static MidFunc HeadRequests => async ( context , next ) =>
63+ {
64+ using ( new OptionalHeadRequestWrapper ( context ) )
65+ {
66+ await next ( ) ;
67+ }
68+ } ;
6069 public static IApplicationBuilder UseSqlStreamStoreHal (
6170 this IApplicationBuilder builder ,
6271 IStreamStore streamStore )
@@ -67,9 +76,10 @@ public static IApplicationBuilder UseSqlStreamStoreHal(
6776 throw new ArgumentNullException ( nameof ( streamStore ) ) ;
6877
6978 return builder
70- . Use ( ExceptionHandlingMiddleware . HandleExceptions )
79+ . UseExceptionHandling ( )
7180 . Use ( CaseSensitiveQueryStrings )
7281 . Use ( AcceptHalJson )
82+ . Use ( HeadRequests )
7383 . UseIndex ( )
7484 . Map ( "/stream" , UseAllStream ( streamStore ) )
7585 . Map ( "/streams" , UseStream ( streamStore ) ) ;
@@ -91,5 +101,56 @@ private static Action<IApplicationBuilder> UseAllStream(IStreamStore streamStore
91101 . Use ( MethodsNotAllowed ( "POST" , "PUT" , "DELETE" , "TRACE" , "PATCH" ) ) ;
92102
93103 private static bool IsOptions ( HttpContext context ) => context . IsOptions ( ) ;
104+
105+ private class OptionalHeadRequestWrapper : IDisposable
106+ {
107+ private readonly HttpContext _context ;
108+ private readonly Stream _originalBody ;
109+
110+ public OptionalHeadRequestWrapper ( HttpContext context )
111+ {
112+ _context = context ;
113+ _originalBody = _context . Response . Body ;
114+ if ( context . Request . Method == "HEAD" )
115+ {
116+ context . Response . Body = new HeadRequestStream ( ) ;
117+ }
118+ }
119+
120+ public void Dispose ( )
121+ {
122+ _context . Response . Body = _originalBody ;
123+ }
124+
125+ private class HeadRequestStream : Stream
126+ {
127+ private long _length ;
128+
129+ public override void Flush ( ) => FlushAsync ( CancellationToken . None ) . Wait ( ) ;
130+ public override int Read ( byte [ ] buffer , int offset , int count ) => throw new NotImplementedException ( ) ;
131+ public override long Seek ( long offset , SeekOrigin origin ) => throw new NotImplementedException ( ) ;
132+ public override void SetLength ( long value ) => throw new NotImplementedException ( ) ;
133+ public override void Write ( byte [ ] buffer , int offset , int count ) => throw new NotImplementedException ( ) ;
134+
135+ public override Task WriteAsync (
136+ byte [ ] buffer ,
137+ int offset ,
138+ int count ,
139+ CancellationToken cancellationToken )
140+ {
141+ _length += count ;
142+ Position += count ;
143+ return Task . CompletedTask ;
144+ }
145+
146+ public override Task FlushAsync ( CancellationToken cancellationToken ) => Task . CompletedTask ;
147+
148+ public override bool CanRead { get ; } = false ;
149+ public override bool CanSeek { get ; } = false ;
150+ public override bool CanWrite { get ; } = true ;
151+ public override long Length => _length ;
152+ public override long Position { get ; set ; }
153+ }
154+ }
94155 }
95156}
0 commit comments