I tried to update a working SAFE stack program which uses Elmish.Bridge over Websockets to latest versions of nuget packages, node packages, and to .NET 8.0.
I now get an error on the server when I try to establish the connection:
Responses to 'CONNECT' requests with the success status code '200' cannot have a response body. Use the 'IHttpExtendedConnectFeature' to accept and write to the 'CONNECT' stream.
App set up here:
let configureHost (hostBuilder : IHostBuilder) =
System.Diagnostics.Trace.TraceInformation("Configure host...")
let defaults =
new System.Collections.Generic.Dictionary<string, string>()
defaults.Add(WebHostDefaults.EnvironmentKey, "Development")
let configuration =
(new ConfigurationBuilder())
.AddInMemoryCollection(defaults)
.AddEnvironmentVariables("ASPNETCORE_")
.Build()
hostBuilder.ConfigureWebHostDefaults(fun webHostBuilder ->
webHostBuilder
.UseConfiguration(configuration)
.ConfigureLogging(fun logging ->
logging
.ClearProviders()
.AddConsole()
.AddAzureWebAppDiagnostics()
|> ignore
)
|> ignore
)
let configureApp (app:IApplicationBuilder) =
app
.UseCookiePolicy()
.UseHsts() // See https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-3.1&tabs=visual-studio
.UseHttpsRedirection() // As above
.UseAuthentication()
.UseWebSockets()
let configureServices (services : IServiceCollection) =
let config = services.BuildServiceProvider().GetService<IConfiguration>()
services
.AddCookiePolicy(Action<_>(fun (options : CookiePolicyOptions)->
options.CheckConsentNeeded <- fun context -> true
options.MinimumSameSitePolicy <- SameSiteMode.Unspecified
options.HandleSameSiteCookieCompatibility() |> ignore
)
)
.AddMicrosoftIdentityWebAppAuthentication(config, openIdConnectScheme = Auth.authScheme)
|> ignore
services
.AddHostedService<Backup.RegularBackup>()
let endpointPipe = pipeline {
plug head
plug requestId
}
let appRouter =
router {
not_found_handler (htmlView NotFound.layout) //Use the default 404 webpage
get BridgeShared.serverEndpoint Socket.server
// other forward and get routers
}
let app = application {
pipe_through endpointPipe
error_handler (fun ex _ -> pipeline { render_html (InternalError.layout ex) })
host_config configureHost
service_config configureServices
app_config configureApp
use_router appRouter
url "https://0.0.0.0:8080/"
memory_cache
use_static "../../deploy/public/"
use_gzip
}
and Socket.server above is:
let server : HttpFunc -> Http.HttpContext -> HttpFuncResult =
Bridge.mkServer serverEndpoint init update
|> Bridge.withServerHub hub
|> Bridge.run Giraffe.server
Complete error:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2 CONNECT https://localhost:8080/api/socket - - -
fail: Giraffe.Middleware.GiraffeErrorHandlerMiddleware[0]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Responses to 'CONNECT' requests with the success status code '200' cannot have a response body. Use the 'IHttpExtendedConnectFeature' to accept and write to the 'CONNECT' stream.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.StatusCheckWriteStream.CheckStatus()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.StatusCheckWriteStream.WriteAsync(ReadOnlyMemory`1 source, CancellationToken cancellationToken)
at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionBody.WriteAsync(ReadOnlyMemory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionBody.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at <StartupCode$Giraffe>[email protected]()
at [email protected]()
at [email protected]()
at [email protected]()
at [email protected]()
at [email protected]()
at [email protected]()
at [email protected]()
at [email protected]()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionMiddleware.InvokeCore(HttpContext context)
at [email protected]()
fail: Giraffe.Middleware.GiraffeErrorHandlerMiddleware[0]
An exception was thrown attempting to handle the original exception.
System.InvalidOperationException: Responses to 'CONNECT' requests with the success status code '200' cannot have a response body. Use the 'IHttpExtendedConnectFeature' to accept and write to the 'CONNECT' stream.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.StatusCheckWriteStream.CheckStatus()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.StatusCheckWriteStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at System.IO.Stream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count)
at <StartupCode$Giraffe>[email protected]()
at [email protected]()
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/2 CONNECT https://localhost:8080/api/socket - 200 - text/html;+charset=utf-8 24.0712ms
info: Microsoft.AspNetCore.Server.Kestrel[32]
Connection id "0HN1DJDDEIH1G", Request id "0HN1DJDDEIH1G:00000011": the application completed without reading the entire request body.
All dependencies are the latest version. Saturn 0.16.1 Giraffe 6.2.0 Elmish.Bridge.Client 7.0.2 Elmish.Bridge.Server 7.0.0
It turns out I need to
forward
rather thanget
the socket server. I guess that's a consequence of the change outlined here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-8.0#add-http2-websockets-support-for-existing-controllersThus:
(The endpoint is already given in the initialisation of Socket.server so "" seems to be sufficient here)