Skip to content

Releases: SuaveIO/suave

v0.29.0 – Bug fix release.

10 Jun 21:11
Compare
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
Compare
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
Compare
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
Compare
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

v0.26.1 - Supporting serving fonts

19 Mar 13:41
@haf haf
Compare
Choose a tag to compare

In this release we have added MIME-types for fonts. That's it ;) - enjoy!

  • 4584a0 - support font MIME types

v0.26 - A couple of renames and a fix in the Razor module

07 Mar 12:30
Compare
Choose a tag to compare

We have renamed applicatives url, urlRegex and urlScan to path, pathRegex and pathScan respectively.

A bug was fixed in the Razor module where every call would miss the cached copy of the template file.

v0.25 - API review and new naming conventions

19 Feb 15:42
Compare
Choose a tag to compare

In this release we have obsoleted the camel case convention in favour of F# standard naming conventions.

Most functions have been given a new name and the old names have been deprecated with Obsolete attributes.

Notes on Renames

If you get error messages like: /a/b.fs(32,32): Error FS0039: The field, constructor or member 'mime_type' is not defined (FS0039) (a) - just rename the symbol to mimeType. Similarly Writers.set_mime_type to Writers.setMimeType.

Another example that might come up: SuaveConfig's home_folder has been renamed to homeFolder.

You get the idea.

v0.23 - Parse POST data by default.

02 Feb 04:18
Compare
Choose a tag to compare
  • Back by popular demand is POST parsing enabled by default.
  • A couple of bugs in the Cookie module impacting session state were fixed.

v0.21.0 - `request.url`, SuaveConfig change, Lens release

05 Jan 08:35
@haf haf
Compare
Choose a tag to compare

A release that improves the way the hostname and uris are handled inside suave. It goes from just looking at the path given in the first line of the HTTP request and running with it, to actually combining the Uri from the binding, the host header and the path.

This release also introduces lens values for all types, fitting this signature which is a step towards taking the goodness from Freya into Suave.

Breaking Changes

This release changes what request.url looks like, to be a Uri. You can access the same sort of value with .AbsolutePath, if you were using that.

Currently the url applicative still only looks at the absolute path, like before, but we plan to change that in the next release (that will be a deprecation).

The SuaveConfig's HttpBinding has changed from { scheme : Protocol, ip : IPAddress, port : Port } to

 {  scheme         : Protocol
    socket_binding : SocketBinding }

And the new type SocketBinding has been introduced:

type SocketBinding =
  { ip   : IPAddress
    port : Port }

However, you can easily now create a HttpBinding:

module HttpBinding =
  /// Create a HttpBinding for the given protocol, an IP address to bind to and a port
  /// to listen on.
  let mk scheme ip port =
    { scheme  = scheme
      socket_binding = SocketBinding.mk ip port }

So the actual code amount required for your config is reduced.

Host Handling

There's another new type, Host, which looks like this:

type Host =
  /// The Http.Applicatives.host function has ensured this value
  | ServerClient of string
  /// The client's Host header is this value
  | ClientOnly of string
  /// The
  | Forwarded of forwarded_for:string * Host
  member x.value =
    match x with
    | ServerClient v -> v
    | ClientOnly v -> v
    | Forwarded (forwarded_for, _) -> forwarded_for

with a corresponding field in HttpRequest: host : Host.

What this type does is make it explicit from where the truth about the host value is taken. From the HTTP RFC, each valid HTTP client needs to send a Host header, so that's one place we can get the value from. However, the client is free to set any value for this header, and currently, Suave doesn't enforce that it's the correct value; but you can enforce it if you care! We've added an applicative Suave.Http.Applicatives.host that work just like all the others.

This, like before, allows you to host multiple domain names with the same server.

This feature also interacts with how the current url is constructed; since you can have a single server for multiple domain names, we do this (runtime.matched_binding.uri path raw_query) to construct the uri - it finds the currently matched binding (ip address plus port) and constructs the uri out of that.

So you can create a UriBuilder and set its host to the request.host value if you wanted to reconstruct the link the client used.

By default, if you don't use the host applicative, only the ClientOnly case will be used. If you do use the host applicative, ServerClient will be used when the applicative succeeds. Forwarded is a recursive data type intends to take the X-Forwarded-For header(s) and make them easily available - a very common thing to fetch when you're behind a load balancer (which most people are when they run suave in production).

That rounds up the changes for this release. If you have comments or improvements, don't hesitate to open an issue or comment.

These changes will make it possible to support SNI in the near future, which means you'll be able to use different SSL certificates for different host names.

Log namespace -> Logging namespace

Basically this (from Logary.Adapters.Suave):

screen shot 2015-01-05 at 13 13 32

v0.20.0 - bugfix release

02 Jan 15:58
@haf haf
Compare
Choose a tag to compare

New release, yey!

Major highlights:

  • Suave.Testing - a new nuget for your testing needs; useful when writing suave applications and you want to test their APIs -- also related to no 164.
  • Suave.Experimental is its own nuget now
  • Suave.Razor is its own nuget now
  • Upgraded OpenSSL to j release on OS X and took dependency on openssl.libs from nuget for the Windows DLL
  • Module Log split into itself and namespace Logging. Update usages accordingly. Log still has 'info', 'verbose' etc functions
  • url_scan can now parse int64 values
  • BREAKING if you're using form data, you need to have ParsingAndControl.parse_post_data before your usage, in order to get it example here
  • HttpMethod moved to Suave.Types module.