SuaveIO / suave

Suave is a simple web development F# library providing a lightweight web server and a set of combinators to manipulate route flow and task composition.
https://suave.io
Other
1.32k stars 198 forks source link

Socket error while running webpart #731

Closed kMutagene closed 5 years ago

kMutagene commented 5 years ago

I am using Suave on the server side of a SAFE stack application. I am sending a post request than contains an array of data, which is handled by the server. The server then applies a function to the data and returns the result. Depending on the size of the input array (an array of size 2 works charmless, an array of size 12 fails), The client reports the following error:

image

After making sure that the problem is not coming from fable.remoting, i turned on verbose logging in suave and get the following error displayed:

image

Is this problem related to some kind of timeout? I tried to look in the source code at ParsingAndControl.fs but had a hard time figuring out exactly why this error is thrown.

Any help would be greatly appreciated

ademar commented 5 years ago

Can we see your code? Or provide us with a POC?

I do see the logger is not outputting the exception details here which should be fixed and would help diagnosing this issue.

kMutagene commented 5 years ago

Hi @ademar, i am not sure which parts of the code are relevant here to avoid overflooding this thread with snippets. Maybe the first thing that could be of interest:

... which should be fixed and would help diagnosing this issue.

in which version is this fix located? i am currently running 2.5.0: paket.dependencies:


group Server
    source https://api.nuget.org/v3/index.json
    framework: netcoreapp2.2
    storage: none

    nuget FSharp.Core
    nuget Suave 2.5.0.0
    nuget Fable.Remoting.Suave
ademar commented 5 years ago

It is not fixed yet, I just noticed.

kMutagene commented 5 years ago

Ah, i see.

I was able to reproduce this error without the usage of my actual function:

Here is the API in Shared.fs :

type TargetPModel =
|NoModel
|Plant
|NonPlant

type TargetPResult = {
    Header      : string
    Sequence    : string
    Scores      : float array
    PlotHtml    : string
}

/// A type that specifies the communication protocol between client and server
/// to learn more, read the docs at https://zaid-ajaj.github.io/Fable.Remoting/src/basics.html

type ITargetPApi = {
    SingleSequenceRequest : TargetPModel -> string -> Async<TargetPResult>
    FastaFileRequest : TargetPModel -> string -> Async<TargetPResult array>
    DownloadRequestSingle: TargetPResult*System.Guid -> Async<unit>
    DownloadRequestMultiple: TargetPResult array * System.Guid -> Async<unit>
}

Now if i just use a Thread.Sleep in the implementation on the Server side:

let targetPApi = {
    SingleSequenceRequest = 
        fun model single -> 
            async {
                    ...
            }
    FastaFileRequest = 
        fun model file -> 
            async {
                    Thread.Sleep(360000)
                    let result : TargetPResult [] = [||]
                    return result
                    }
    DownloadRequestSingle = 
        fun (res,id)-> 
            async { 
                    ...
                    }

    DownloadRequestMultiple = 
        fun (res,id) -> 
            async {
                   ...
                    }
}

I get the same error, i hope that helps.

ademar commented 5 years ago

Now if i just use a Thread.Sleep

You shouldn't use Thread.Sleep in async code but Async.Sleep instead.

I'll make it so it outputs the exception information in the error message and push a new version. Just give me some time because Mondays are very busy.

kMutagene commented 5 years ago

No worries, thanks for the help!

ademar commented 5 years ago

@kMutagene Can you also update your Suave references; I did not see previously that you are using 2.5.0 which is very old.

I've just pushed a new version 2.5.5 that should output the actual error/exception information if it is still happens.

kMutagene commented 5 years ago

Thank you very much, i will try it tomorrow!

kMutagene commented 5 years ago

Sorry for the late reply, i managed to see the error with the updated package. It is a ConnectionAborted Error thrown by System.Net.Sockets. From the docs:

ConnectionAborted 10053 The connection was aborted by the .NET Framework or the underlying socket provider.
kMutagene commented 5 years ago

I did some more digging and the timeline of the error is the following:

So if i understand this right there is a max HTTP Connection timeout of two minutes? Can this be set in Suave (i only found listen timeout so far) or is this an issue that would be better to move to SAFE Stack?

ademar commented 5 years ago

Hi @kMutagene,

Difficult to know, these errors could be unrelated. ConnectionAborted is not necessarily a bad thing, it is usually shown when the client drops the connection.

To diagnose the issue I'll need a POC, the snippets do not show the full story and I am not familiar with the libraries you are using. If you could publish a repository with code reproducing the issue that will be great.

kMutagene commented 5 years ago

Hi @ademar ,

I will have to reproduce this error using another function then, because (to keep a rather long story short) i am accessing a docker container with the server side functions that need an academic licence. So i am afraid that i will need some time for this.

kMutagene commented 5 years ago

Sorry for letting this issue go stale for so long.

I found the problem, and it had nothing to do with suave. When using the SAFE-STACK, webpack is used to setup a dev server, which proxies requests to suave. Webpack has a default request timeout of 2 minutes. You can change it like this in the devServerProxy field of webpack.config:

    devServerProxy: {
        // redirect requests that start with /api/* to the server on port 8085
        '/api/*': {
            target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
            changeOrigin: true,
            proxyTimeout: 10 * 60 * 1000, // 10 minute timeout
            onProxyReq: (proxyReq, req, res) => req.setTimeout(10 * 60 * 1000) // 10 minute timeout
        }