Skip to content

Commit a5cf6cf

Browse files
committed
Enable SSL/TLS support
1 parent c91cbb6 commit a5cf6cf

File tree

15 files changed

+302
-184
lines changed

15 files changed

+302
-184
lines changed

examples/Example/Example.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project Sdk="Microsoft.NET.Sdk">
33
<PropertyGroup>
4-
<TargetFrameworks>net9.0</TargetFrameworks>
4+
<TargetFramework>net9.0</TargetFramework>
55
<OutputType>Exe</OutputType>
66
<AssemblyName>Example</AssemblyName>
77
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>

examples/Example/Program.fs

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,13 @@ let app =
155155
] //>=> logStructured logger logFormatStructured
156156

157157
open System.Security.Cryptography.X509Certificates
158+
let cert = X509CertificateLoader.LoadPkcs12FromFile("suave.p12", "easy")
158159

159160
[<EntryPoint>]
160161
let main argv =
161162
startWebServer
162-
{ bindings = [ HttpBinding.createSimple HTTP "127.0.0.1" 8082
163-
]
163+
{ bindings = [ HttpBinding.createSimple HTTP "127.0.0.1" 8082;
164+
HttpBinding.createSimple (HTTPS cert) "127.0.0.1" 8443 ]
164165
serverKey = Utils.Crypto.generateKey HttpRuntime.ServerKeyLength
165166
errorHandler = defaultErrorHandler
166167
listenTimeout = TimeSpan.FromMilliseconds 2000.
@@ -170,29 +171,8 @@ let main argv =
170171
mimeTypesMap = mimeTypes
171172
homeFolder = None
172173
compressedFilesFolder = None
173-
//logger = logger
174174
cookieSerialiser = new BinaryFormatterSerialiser()
175175
hideHeader = false
176176
maxContentLength = 1000000 }
177177
app
178-
0
179-
180-
(*
181-
// using Suave.OpenSSL
182-
// also see https://github.com/SuaveIO/suave/issues/291
183-
// and https://github.com/exira/static-mailer/blob/72fdebf37bafc48ea7277ee4a6b2a758df5c3b3d/src/Program.fs#L28-L31
184-
open Suave.OpenSSL
185-
open OpenSSL.Core
186-
open System.Security.Cryptography.X509Certificates
187-
188-
let cert =
189-
let bio = BIO.MemoryBuffer()
190-
let cert = System.IO.File.ReadAllBytes "example.pem"
191-
bio.Write cert
192-
OpenSSL.X509.X509Certificate.FromDER bio
193-
194-
Or using the built-in SSL support:
195-
let cert = new X509Certificate2("suave.p12","easy")
196-
HttpBinding.createSimple (HTTPS cert) "127.0.0.1" 8443
197-
198-
*)
178+
0

src/Suave/Combinators.fs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,8 +784,12 @@ module EventSource =
784784
let (<<.) (out : Connection) (data : string) =
785785
out.asyncWriteBytes (Encoding.UTF8.GetBytes data)
786786

787-
let dispatch (out : Connection) : ValueTask<int> =
788-
send out ES_EOL_S
787+
let dispatch (out : Connection) =
788+
task {
789+
match! out.transport.write ES_EOL_S with
790+
| Ok () -> return ()
791+
| Result.Error _ -> return ()
792+
}
789793

790794
let comment (out : Connection) (cmt : string) =
791795
out <<. ": " + cmt + ES_EOL

src/Suave/Combinators.fsi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,7 +1609,7 @@ module EventSource =
16091609

16101610
/// "If the line is empty (a blank line) - dispatch the event."
16111611
/// Dispatches the event properly to the browser.
1612-
val dispatch : out:Connection -> ValueTask<int>
1612+
val dispatch : out:Connection -> Task<unit>
16131613

16141614
/// "If the line starts with a U+003A COLON character (:) - Ignore the line."
16151615
/// Writes a comment to the stream
@@ -1646,7 +1646,7 @@ module EventSource =
16461646
static member createType : id:string -> data:string -> typ:string -> Message
16471647

16481648
/// send a message containing data to the output stream
1649-
val send : out:Connection -> msg:Message -> Task<int >
1649+
val send : out:Connection -> msg:Message -> Task<unit>
16501650

16511651
/// This function composes the passed function f with the hand-shake required
16521652
/// to start a new event-stream protocol session with the browser.

src/Suave/ConnectionFacade.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ type ConnectionFacade(connection: Connection, runtime: HttpRuntime, connectionPo
312312

313313
member this.shutdown() =
314314
connection.lineBufferCount <- 0
315-
connection.transport.shutdown()
315+
Connection.shutdown connection
316316
connectionPool.Push(this)
317317

318318
/// The request loop initialises a request with a processor to handle the

src/Suave/DefaultTlsProvider.fs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/Suave/Runtime.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ module Runtime =
3737

3838
type Protocol =
3939
| HTTP
40-
| HTTPS of obj
40+
| HTTPS of System.Security.Cryptography.X509Certificates.X509Certificate
4141

4242
member x.secure =
4343
match x with
@@ -111,4 +111,4 @@ module Runtime =
111111
{ fieldName : string
112112
fileName : string
113113
mimeType : string
114-
tempFilePath : string }
114+
tempFilePath : string }

src/Suave/Runtime.fsi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module Runtime =
2929
/// The HTTP protocol is the core protocol
3030
| HTTP
3131
/// The HTTP protocol tunneled in a TLS tunnel
32-
| HTTPS of obj
32+
| HTTPS of System.Security.Cryptography.X509Certificates.X509Certificate
3333

3434
member secure : bool
3535

@@ -87,4 +87,4 @@ module Runtime =
8787
{ fieldName : string
8888
fileName : string
8989
mimeType : string
90-
tempFilePath : string }
90+
tempFilePath : string }

src/Suave/Sockets/Connection.fs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ open Suave
1313
/// and that can be closed.
1414
type Connection =
1515
{ mutable socketBinding : SocketBinding
16-
transport : TcpTransport
16+
transport : ITransport
1717
reader : HttpReader
1818
pipe : Pipe
1919
lineBuffer : byte array
@@ -30,7 +30,9 @@ type Connection =
3030
member inline this.flush () =
3131
task {
3232
if this.lineBufferCount> 0 then
33-
let! _ = this.transport.write (new Memory<_>(this.lineBuffer,0,this.lineBufferCount))
33+
match! this.transport.write (new Memory<_>(this.lineBuffer,0,this.lineBufferCount)) with
34+
| Ok () -> ()
35+
| Result.Error _ -> ()
3436
// Clear the buffer to prevent information leakage
3537
Array.Clear(this.lineBuffer, 0, this.lineBufferCount)
3638
this.lineBufferCount <- 0
@@ -45,7 +47,9 @@ type Connection =
4547
if maxByteCount > this.lineBuffer.Length then
4648
// Flush current buffer first
4749
if this.lineBufferCount > 0 then
48-
let! _ = this.transport.write (new Memory<_>(this.lineBuffer, 0, this.lineBufferCount))
50+
match! this.transport.write (new Memory<_>(this.lineBuffer, 0, this.lineBufferCount)) with
51+
| Ok () -> ()
52+
| Result.Error _ -> ()
4953
this.lineBufferCount <- 0
5054

5155
// Use ArrayPool for large strings
@@ -55,14 +59,18 @@ type Connection =
5559
let mutable bytesUsed = 0
5660
let mutable completed = false
5761
this.utf8Encoder.Convert(str.ToCharArray(), 0, str.Length, tempBuffer, 0, maxByteCount, true, &charsUsed, &bytesUsed, &completed)
58-
let! _ = this.transport.write (new Memory<_>(tempBuffer, 0, bytesUsed))
62+
match! this.transport.write (new Memory<_>(tempBuffer, 0, bytesUsed)) with
63+
| Ok () -> ()
64+
| Result.Error _ -> ()
5965
return ()
6066
finally
6167
ArrayPool<byte>.Shared.Return(tempBuffer)
6268

6369
elif this.lineBufferCount + maxByteCount > this.lineBuffer.Length then
6470
// Flush buffer and encode into it
65-
let! _ = this.transport.write (new Memory<_>(this.lineBuffer, 0, this.lineBufferCount))
71+
match! this.transport.write (new Memory<_>(this.lineBuffer, 0, this.lineBufferCount)) with
72+
| Ok () -> ()
73+
| Result.Error _ -> ()
6674
let mutable charsUsed = 0
6775
let mutable bytesUsed = 0
6876
let mutable completed = false
@@ -89,19 +97,23 @@ type Connection =
8997
member inline this.asyncWriteBytes (b : byte[]) =
9098
task {
9199
if b.Length > 0 then
92-
let! _ = this.transport.write (new Memory<_>(b, 0, b.Length))
93-
()
100+
match! this.transport.write (new Memory<_>(b, 0, b.Length)) with
101+
| Ok () -> ()
102+
| Result.Error _ -> ()
94103
}
95104

96105
member inline this.asyncWriteBufferedBytes (b : byte[]) =
97106
task {
98107
if this.lineBufferCount + b.Length > this.lineBuffer.Length then
99108
// flush lineBuffer
100109
if this.lineBufferCount > 0 then
101-
let! _ = this.transport.write (new Memory<_>(this.lineBuffer, 0, this.lineBufferCount))
102-
()
110+
match! this.transport.write (new Memory<_>(this.lineBuffer, 0, this.lineBufferCount)) with
111+
| Ok () -> ()
112+
| Result.Error _ -> ()
103113
// don't waste time buffering here
104-
let! _ = this.transport.write (new Memory<_>(b, 0, b.Length))
114+
match! this.transport.write (new Memory<_>(b, 0, b.Length)) with
115+
| Ok () -> ()
116+
| Result.Error _ -> ()
105117
this.lineBufferCount <- 0
106118
else
107119
Buffer.BlockCopy(b, 0, this.lineBuffer,this.lineBufferCount, b.Length)
@@ -143,4 +155,15 @@ module Connection =
143155
cn.transport.read buf
144156

145157
let inline send (cn :Connection) (buf : ByteSegment) =
146-
cn.transport.write buf
158+
task {
159+
match! cn.transport.write buf with
160+
| Ok () -> return Ok ()
161+
| Result.Error e -> return Result.Error e
162+
}
163+
164+
let inline shutdown (cn : Connection) =
165+
task {
166+
match! cn.transport.shutdown() with
167+
| Ok () -> ()
168+
| Result.Error _ -> ()
169+
}

src/Suave/Sockets/HttpReader.fs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,14 @@ module Aux =
5050

5151
// this guy can live inside the connection actually
5252
[<AllowNullLiteral>]
53-
type HttpReader(transport : TcpTransport, lineBuffer : byte array, pipe: Pipe, cancellationToken) =
53+
type HttpReader(transportObj : obj, lineBuffer : byte array, pipe: Pipe, cancellationToken) =
54+
55+
let transport =
56+
match transportObj with
57+
| :? ITransport as t -> t
58+
| :? TcpTransport as tcp -> tcp :> ITransport
59+
| :? SslTransport as ssl -> ssl :> ITransport
60+
| _ -> failwith "Invalid transport type"
5461

5562
let mutable running : bool = true
5663
let mutable dirty : bool = false
@@ -67,13 +74,16 @@ type HttpReader(transport : TcpTransport, lineBuffer : byte array, pipe: Pipe, c
6774

6875
member x.readMoreData () = task {
6976
let buff = pipe.Writer.GetMemory()
70-
let! x = transport.read buff
71-
if x > 0 then
72-
pipe.Writer.Advance(x)
73-
let! flushResult = pipe.Writer.FlushAsync(cancellationToken)
74-
return Ok()
75-
else
76-
return Result.Error (Error.ConnectionError "no more data")
77+
match! transport.read buff with
78+
| Ok bytesRead ->
79+
if bytesRead > 0 then
80+
pipe.Writer.Advance(bytesRead)
81+
let! flushResult = pipe.Writer.FlushAsync(cancellationToken)
82+
return Ok()
83+
else
84+
return Result.Error (Error.ConnectionError "no more data")
85+
| Result.Error e ->
86+
return Result.Error e
7787
}
7888

7989
member x.getData () = task{

0 commit comments

Comments
 (0)