Skip to content

Releases: SuaveIO/suave

v0.31.2

04 Sep 07:44
@haf haf

Choose a tag to compare

This release downgrades FSharp.Core to 3.1.2.1. For the reasons behind this please review this issue.

  • [infra] downgrade to F# 3.1
  • logFormat writes size of the object returned to the client
  • A little bit more documentation about formData

Thanks to @wallymathieu @xkrt for your contributions to this release.

v0.32.0-owin

04 Sep 07:35
@haf haf

Choose a tag to compare

v0.32.0-owin Pre-release
Pre-release

This release is a 0.32 pre-release with owin support built in. The owin support is currently not complete, and progress can be tracked in #142

v0.31.1

18 Aug 16:46

Choose a tag to compare

  • Ignore invalid cookies in requests (#287)
  • Allows subsequent opening of files for reading or writing (5f4d18a)
  • Fix a bug in the EventSource module (#286)

v0.31.0

13 Aug 15:40

Choose a tag to compare

  • Include Vary: Accept-Encoding in responses by default (#277)
  • Don't send content for HEAD requests (#285)

v0.30.0

06 Jul 16:03

Choose a tag to compare

  • Globals.random replaced with ThreadSafeRandom (c2a6168).
  • Removed obsoletes.

v0.29.1

21 Jun 04:24

Choose a tag to compare

Fixes a Websocket bug handling large payloads.

v0.29.0 – Bug fix release.

10 Jun 21:11

Choose a tag to compare

  • bug fix: trim double quotes in multipart boundary
  • [http] support source maps
  • handle expect 100-continue
  • [http] JS mime type, fixes #262

v0.28.1 - Bug fix release.

21 May 02:45

Choose a tag to compare

We fix a file upload bug introduced in the previous release.

v0.28.0 - Quality Release

20 May 08:34
@haf haf

Choose a tag to compare

This release contains a number of improvements to how bad input to Suave is handled. Previously we've done it the happy path. This has meant that bad input; especially so when it's input that's not parsed by a WebPart, but by the server, could cause a 'failwith' to throw an exception, cancelling the job (that serves the request) and leaving the Tcp connection hanging in an ever-connected state. By extension, this has meant an attacker could send many malformed requests that hang the TCP socket (requiring an equal number of sockets to be open from the attacker's machine(s)).

No more, we say, and with v0.28, Suave returns 400 Bad Request when it for example gets malforms HTML forms or requests without the Host-header.

More Improvements

We've improved how the WebSocket-code handles under different error cases, which means a more stable implementation.

SocketOp

There have been a number of breaking changes, mostly in the Suave.Utils namespace and in Suave.Sockets. One of the larger changes is that the socket workflow builder is now brought into scope through open Suave.Sockets.Control. The value that monad/workflow-builder operates on, SocketOp has been recognised to be pretty versatile for error handling and control flow with asynchronous interleaved sockets with error handling (that's a mouthful!), so it's gotten its own module SocketOp in Suave.Sockets. A few of the utility functions around lifting async/task to socket have been moved here: { ofAsync, ofTask }.

The special part about this monad is that Suave uses its Error value in the Choice2Of2-case -- if its Case is SocketError then the connection is terminated without further ado, but if it's InputDataError Suave now takes care to ferry the error all the way to the HTTP response body to tell the caller about the error in its ways. You can do the same if you're using SocketOp, e.g. like exemplified in examples/WebSocket, examples/Example/CounterDemo.fs; so it's a really nice abstraction for writing safe code with.

This monad is structured very similar to Choice, so you have functions like:

  • mreturn - create a new successful value
  • abort - create a new unsuccessful value
  • orInputError - says that something is wrong with the input on a protocol level and that it's therefore a bad request (user input error) -- the error already present is overwritten with the errorMsg parameter.
  • orInputErrorf - same as the above, but let's you do something with the existing error message through the callback function passed
  • bind - Bind the result successful result of the SocketOp to fCont
  • bindError - Bind the error result of the SocketOp to fCont
  • map - Map f over the contained successful value in SocketOp
  • mapError - Map f over the error value in SocketOp
  • ofAsync - lift a Async<'a> type to the SocketOp
  • ofTask - lift a Task type to the SocketOp

You can also open SocketOpOperators in this same namespace, to gain access to:

  • (@|!) - SocketOp.orInputError
  • (@|!!) - SocketOp.orInputErrorf
  • (@|>) - SocketOp.bindError

For example, here's how you write to a Server-Sent Event Stream and sleep async 100 ms before writing again:

  let write i =
    socket {
      let msg = { id = i; data = string i; ``type`` = None }
      do! msg |> send out
      return! SocketOp.ofAsync (Async.Sleep 100)
    }

More Changes

  • There were a few straggling snake_case that were switched over to camelCase. If you get a compilation error for one of these, just try camelCase.
  • The web server can now handle multipart/mixed content types that are nested in multipart/form when a form field has multiple values. This is a nice way to handle multiple values instead of using value[] keys and parsing in your own code - the main benefactor of this is form files.
  • You'll find a few utility methods return Choice1Of2 or Choice2Of2 instead of Option -- this is part of the major refactor towards providing contextual error messages everywhere and failing gracefully.

v0.27.0 - Websocket support

14 May 15:36

Choose a tag to compare

In this release we include preliminary Websocket support; feedback is most welcome.

let echo (webSocket : WebSocket) =
  fun cx -> async{
    let loop = ref true
    while !loop do
      let! msg = webSocket.read()
      match msg with
      | (Text, data, true) ->
        let str = UTF8.toString data
        do! webSocket.send Text data true
      | (Ping, _, _) ->
        do! webSocket.send Pong [||] true
      | (Close, _, _) ->
        loop := false
      | _ -> ()
    return! Control.CLOSE cx
  }

let app : WebPart =
  choose [
    path "/websocket" >>= handShake echo
    GET >>= choose [ path "/" >>= file "index.html"; browseHome ];
    NOT_FOUND "Found no handlers."
    ]

We also fixed a bug in the Razor module d295582