Draft proposal for TLS Service APIs (please review)

How about TLSExchange which sound more like what discussion is about?

···

On Tuesday, April 4, 2017, Georgios Moschovitis via swift-server-dev < swift-server-dev@swift.org> wrote:

On 3 Apr 2017, at 5:33 PM, Gelareh Taban <gtaban@us.ibm.com > <javascript:_e(%7B%7D,'cvml','gtaban@us.ibm.com');>> wrote:

There are 2 hard problems in computer science: cache invalidation, **naming
things**, and off-by-1 errors. -- Leon Bambrick

:-)

> > * These are essentially delegate methods notifying the TLS engine of
an event.
> > `onAccept` says that the system has accepted a connection and the TLS
engine should
> > do what it needs to do with it, `onSend` means the system is about to
send data and
> > it needs the TLS engine to modify it, etc. If so, Swift APIs more
often use words like
> > `should`, `will`, or `did` than `on`, particularly since they're more
precise about the
> > timing of the delegate method compared to the actual event.
>
> +1 onXXX methods look definitely non-Swifty to me.

Fair enough. What do you recommend?

what about:

onAccept -> didAccept
onDestroy -> didDestroy / wasDestroyed
onClientCreate -> clientWasCreated
onServerCreate -> serverWasCreated

etc, you get the point.

I agree that Service is not a particularly good name but that was the best
I could think of. I liked Engine but (1) we are not implementing anything,
just overlaying and (2) I didnt want any confusion with with the SSL Engine
of OpenSSL.

So again, I would be interested in your recommendations!

(1) `Service` has the same problem
      if it’s just overlaying, maybe you should name it ‘TLSOverlay’,
‘TLSMiddleware’ or something.
      I still think, `Engine` is a pretty good name though, but will keep
thinking for better ones.

I think perhaps there is misunderstanding about the role of TransportManager (perhaps naming is the problem after all!). This is not a singleton type of manager - rather it is a per connection manager and it "manages" for example the system socket calls. An instance of transport manager handles one connection. An instance of transport manager has one TLS delegate associated with that connection. Therefore, the delegate is associated with only one connection at a time.

Oh, wow! You're right, I completely misunderstood that; I was under the impression that all of the instances in your diagram were long-lived, but it sounds like they're actually used for one connection/request and then thrown away (or perhaps reused in a pool—doesn't matter much either way).

The use of "management" in these names definitely was part of why I misunderstood the proposal, because in Cocoa, a "manager" is almost always long-lived. Often it's a singleton or is frequently used as a singleton (`Foundation.FileManager`, `CoreLocation.CLLocationManager`, `Photos.PHImageManager`.) Sometimes it's associated with a long-lived object like a document (`Foundation.UndoManager`) or a text editing view (`AppKit.NSLayoutManager`). Very occasionally it's short-lived and manages some particular activity (`CoreData.NSMigrationManager`), but even that class is only short-lived because the activity it manages is relatively brief.

Given that the transport manager and `SSLService` are both dedicated to a single connection, I suppose the idea is that the call to `onAccept` or `onConnect` permanently associates the `SSLService` with the `IORef`, an instance that allows it to communicate with the actual socket/connection. When `onReceive` wants to receive data, it should get it from the `IORef`; when `onSend` wants to send data, it should give it to the `IORef`.

One more architectural question: What is the ownership relationship between the application, web server, HTTP management, and transport management layers? That is, is transport management the layer that drives everything else, or is it more of a utility type used by the other layers?

For the moment, I will assume that the transport manager layer is subordinate to the application or web server.

If we assume the above, then it is not hard to see that once a context is created for a specific TLS connection, and stored by the TLS service object, the context can be passed between the various OpenSSL or Secure Transport calls.

Yes, I do see that.

With that in mind, I have a question about the commands: Why are `onServerCreate` and `onAccept` separate, and why are `onClientCreate` and `onConnect` separate? I understand that Secure Transport and OpenSSL do those tasks with separate calls, but is there a reason to perform them at different times? I would think that, by the time a `TLSService` has been instantiated, we have already received a connection and want to start reading data from it. (BlueSocket does appear to separate initialization from handshaking, but it also appears to initialize an `SSLService` before it begins listening, so I'm not sure that makes sense for us.) Eliminating `onServerCreate` and `onClientCreate` would therefore simply remove an opportunity for mistakes: it would no longer be possible to accidentally call `onAccept` on a client `TLSService` or `onConnect` on a server `TLSService`, or to forget one of the two initialization setps. (It also means fewer APIs to name. :^) )

As for naming issues, here's what I would suggest.

* * *

I think the best way to think of the transport manager and the IORef is as two parts of a "connection". What you are calling the transport manager—or at least the relevant piece of it for this discussion—is a `Connection` object. A `Connection` object primarily exists to manage a `RawConnection` instance (equivalent to your `IORef`/`TransportManagerDelegate`), which represents an OS-level network connection. `Connection` tracks the `RawConnection`'s lifetime and adds various smart behaviors to it, including, of course, TLS.

I will now briefly sketch `Connection` and `RawConnection`. Keep in mind that these are not exact specifications and aren't being proposed; they're just meant to generally describe the capabilities of these types. If you ask whether `Connection` should be `final` or argue that `RawConnection` should use traditional Unix socket function names, you're sort of missing the point.

  // A ConnectionConfiguration is some object from a higher layer in the application's stack that can
  // provide configuration info for Connection. Most importantly, it provides the TLSService.
  // In your diagram, the HTTP management, web server, and application layers might all conform.
  protocol ConnectionConfiguration {
    ...
    var tlsService: TLSService?
    ...
  }

  class Connection {
    // A bit about how I imagine a Connection would usually be created:
    //
    // For a client connection, convenience initializers (not shown here) would be used to specify
    // a destination address, protocol, etc. These would create a system-level socket, convert
    // it into a `RawConnection`, and pass that to `init(toServerVia:configuration:)`.
    //
    // For a server connection, a separate `Listener` type would listen to a socket, accept
    // any connections, and call `init(fromClientVia:configuration:)` for them.
    
    // A Connection may be created as a result of connecting to a server as a client.
    // If so, it goes through this initializer.
    init(toServerVia rawConnection: RawConnection, configuration: ConnectionConfiguration) throws
    
    // A Connection may be created as a result of a client connecting to us as a server.
    // If so, it goes through this initializer.
    init(fromClientVia rawConnection: RawConnection, configuration: ConnectionConfiguration) throws
    
    // The Connection will hold on to its RawConnection and to its TLSService, if it has one.
    var rawConnection: RawConnection { get }
    var tlsService: TLSService? { get }
    
    // Synchronously sends data to the other endpoint. The provided data will all be sent before this
    // method returns, unless an error occurs during sending. If the underlying connection
    // closes before all data has been sent, this will throw an error. (The error might include
    // unsent data; that's something to be designed later.)
    func sendBytes(_ data: Data) throws
    
    // Synchronously receives data from the other endpoint. This call will attempt to receive up to the
    // indicated number of bytes. If the underlying connection closes before data has
    // been received, this will throw an error. (Again, the error might include any data that
    // was received; that's something to be designed later.)
    //
    // Note: I am not deciding whether this is blocking or nonblocking. It shouldn't affect the
    // implementation of the TLSService either way—the underlying RawConnection will
    // either block or it won't.
    func receiveBytes(_ count: Int) throws -> Data
    
    // Closes the connection, notifying the other side if possible. Throws if the connection is
    // already closed or if another error occurs.
    func close() throws
    
    // Indicates whether, to the `Connection`'s knowledge, the underlying connection has closed.
    // If `true`, all other calls will throw; if `false`, they *may* throw, but it isn't certain.
    var hasClosed: Bool { get {…} }

    // Cleans up if the connection hasn't been closed yet.
    deinit
  }

  // A RawConnection represents whatever low-level OS resource is used to
  // communicate with the network.
  protocol RawConnection {
    // These calls all just do the underlying thing that the `Connection` call does.
    func sendBytes(_ data: Data) throws
    func receiveBytes(_ count: Int) throws -> Data
    func close() throws
    
    // Some RawConnections may simply wrap an OS file descriptor; if so, this
    // property will return it so you can access and use it directly.
    //
    // Note: We may prefer a design where *all* RawConnections are *always*
    // file descriptors. If so, we would eliminate the Optional on this type, but we'd
    // still have the above methods. They basically wrap code that both `Connection`
    // and a `TLSService` that wrapped a library like Secure Transport which didn't
    // directly do I/O would call.
    var fileDescriptor: Int32? { get }
  }

So, given these rough designs, here's what I propose for `TLSService`. To the extent that names from the above rough design are present in `TLSService`, they would eventually be adjusted to match names in the larger system—so if `RawConnection` turns out to be called `SocketSink` eventually, we would change both the parameter type and the parameter label for the first two methods.

  protocol TLSService {
    // I'll talk about these two method names, and discuss alternatives, in the prose.
    
    /// Called to indicate that the connection has connected to a server, and that it should
    /// begin negotiating a TLS connection.
    ///
    /// -Parameter rawConnection: The underlying OS-level connection. The `TLSService` should
    /// negotiate over this raw connection during this call, and then hold onto the raw
    /// connection for future calls to other methods.
    /// -Throws: If the connection closes, TLS negotiation fails, or any other error occurs.
    /// -Precondition: Neither `didConnectToServer` nor `didConnectFromClient` has previously been
    /// called.
    ///
    /// -Note: This will typically be called from `Connection(toServerVia:configuration:)`.
    func didConnectToServer(via rawConnection: RawConnection) throws
    
    /// Called to indicate that the connection has connected to a client, and that it should
    /// wait for that client to begin negotiating a TLS connection, and then negotiate as a server.
    ///
    /// -Parameter rawConnection: The underlying OS-level connection. The `TLSService` should
    /// negotiate over this raw connection during this call, and then hold onto the raw
    /// connection for future calls to other methods.
    /// -Throws: If the connection closes, TLS negotiation fails, or any other error occurs.
    /// -Precondition: Neither `didConnectToServer` nor `didConnectFromClient` has previously been
    /// called.
    ///
    /// -Note: This will typically be called from `Connection(fromClientVia:configuration:)`.
    func didConnectFromClient(via rawConnection: RawConnection) throws
    
    // The next three calls do not have `will` or `did` prefixes because they are imperative:
    // they do not merely tell the `TLSService` that something happened—they actually *cause*
    // it to happen.
    
    /// Called when the connection wants to send the indicated plaintext. This call should
    /// encipher it, wrap it in TLS framing and protocol data, and send it through the raw
    /// connection.
    ///
    /// -Parameter data: The plaintext data to send.
    /// -Throws: If the connection closes, a TLS error occurs, or any other error occurs.
    /// -Precondition: Either `didConnectToServer` or `didConnectFromClient` has been called.
    ///
    /// -Note: This will typically be called from `Connection.sendBytes(_:)`.
    func sendBytes(_ data: Data) throws

    /// Called when the connection wants to receive data. This call should attempt to receive
    /// the indicated number of bytes of TLS data, then interpret all TLS protocol messages,
    /// extract any plaintext from it, and return it.
    ///
    /// -Parameter count: The number of bytes to attempt to read.
    /// -Throws: If the connection closes, a TLS error occurs, or any other error occurs.
    /// -Precondition: Either `didConnectToServer` or `didConnectFromClient` has been called.
    ///
    /// -Note: This will typically be called from `Connection.receiveBytes(_:)`.
    func receiveBytes(_ count: Int) throws -> Data
    
    /// Called when the connection wants to close. This call should notify the other side of
    /// the shutdown through a TLS alert and then close the connection.
    ///
    /// -Throws: If the connection closes before it finishes, a TLS error occurs, or any other error occurs.
    /// -Precondition: Either `didConnectToServer` or `didConnectFromClient` has been called.
    ///
    /// -Note: This will typically be called from `Connection.close`.
    func close() throws

    /// Called when the `Connection` that owns this `TLSService` is about to deinitialize itself.
    /// This is a good opportunity to tear down any state associated with the `TLSService`.
    func willDeinitConnection()
  }

A few points to talk about here:

* I am using `Data` here because you mentioned that you're interested in switching to that, but I'm actually pretty agnostic about whether we should use `Data`, `Unsafe(Mutable)RawBufferPointer`, `Unsafe(Mutable)BufferPointer<UInt8>`, or anything else out there. We might even use `Data` on the user-facing `Connection` interfaces, but use `Unsafe(Mutable)RawBufferPointer` in the `TLSService` and `RawConnection` interfaces for speed. My only concern with the buffer pointer design is that, if we try to `receiveBytes(_:)` and after de-TLSing it find that we have more data than will fit in the buffer, I'm not sure how we'd get the excess data back to the caller.

* The first two method names, for the methods used after accepting or connecting.

I chose these names because they read well, describe when they are called clearly, and match the names in the `Connection` sketch. However, they're a little bit...out there. I have two alternative suggestions:

  * Traditional/conservative: `didConnect(via:)` and `didAccept(via:)`.

  * Imperative: `negotiateAsClient(via:)` and `negotiateAsServer(via:)`.

* `sendBytes`, `receiveBytes`, and `close`: These three calls are imperative because, at least in this design, they actually *cause* the thing indicated to happen—`sendBytes` actually sends the data, `receiveBytes` actually goes and gets the data, and `close()` actually causes a disconnect. If this weren't the case, and these calls were more like filters—for instance, `sendBytes` returned data for the caller to send over the connection, instead of actually calling the raw connection and asking it to be sent—then I would suggest `willSendBytes(_:)`, `didReceiveBytes(_:)`, and `willClose()`.

* `willDeinitConnection()` here is defined as happening at connection deinit time, which may be slightly later than would be ideal. However, it seems like the logic necessary to make it happen as soon as possible would be a lot more complicated, so I'm not sure it's worth it to do so.

* And finally, an overall design question: Would we be better off doing this as a sort of generalized filter or layer on the `Connection` object? This is, for instance, how OpenSSL works: it wraps the underlying I/O primitive in a BIO structure, then adds an "object" of sorts to that BIO which transparently applies TLS as you read and write. A similar approach in Swift would probably imagine this protocol not as a TLS-specific hook, but rather as a way to capture *all* operations on a connection and do arbitrary things with them. I'm not necessarily advocating this approach, because it couples the TLS system pretty strongly to our future socket abstraction, but I'm wondering if it was considered or not.

* * *

Anyway, I hope this is helpful. It certainly is long. :^)

···

On Apr 4, 2017, at 9:32 AM, Gelareh Taban <gtaban@us.ibm.com> wrote:

--
Brent Royal-Gordon
Architechies

Hi Helge and Gelareh,

[...]

If you are talking about non-blocking by any chance, we have an implementation of this in BlueSSLService (https://github.com/IBM-Swift/BlueSSLService\) which interfaces with a socket management layer (BlueSocket - https://github.com/IBM-Swift/BlueSocket\).

Sounds good. Do you have an example demonstrating the use of BlueSocket in NIO mode? Like a simple client which just opens a socket connection to a TLS server and reads with NIO configured.

as far as I understand the source code BlueSocket is blocking IO only. But its API basically breaks the abstraction by giving you (publically!) direct access to the the file descriptor.

  ///
  /// The file descriptor representing this socket. (Readonly)
  ///
  public internal(set) var socketfd: Int32 = SOCKET_INVALID_DESCRIPTOR

(from: https://github.com/IBM-Swift/BlueSocket/blob/master/Sources/Socket.swift#L772 )

so what you could do is

fcntl(someBlueSocket.socketFD, O_NONBLOCK, 1)

and then put in in a kqueue/epoll/DispatchSource . I personally don't think that counts as non-blocking API but it's possible :)

[...]

Cheers,
  Johannes

> [g] Agreed about general efficiency, but with TLS, you would need a

different context for each of these streams.

I can’t follow you. Which different streams? What do you want to

coordinate?

Are we still talking about

  func tlsWritev(vectors: AnyCollection<iovec>)

?? What has been proposed is simply supporting `writev` semantics (man

writev).

I'll reword what my understanding is and let me know if I am incorrect. I
obviously haven't worked with vectored IO before so this is new for me.

I understand the benefit of iov and its single-copy properties when we call
readv or writev (efficiency + atomicity).
But this assumes we are using those functions in our implementation of TLS.

** In OpenSSL, there seems to be some support for it though I have not
tested it.

According to
[openssl-dev] [openssl.org #3729] Patch to add support for iovec-based IO in OpenSSL there
is a patch that went in OpenSSL that supports iovec.

Also possibly we can do this via BIO with BIO_f_buffer(). It seems there is
one additional copy but it's still more efficient than treating each buffer
separately.

(I would assume the latter technique would also work in LibreSSL and other
forks)

** In SecureTransport, AFAIK there is no support for iov though our friends
at Apple may give us a definitive answer.

If iov is not supported in the underlying TLS library, then we go back to
treating each buffer in iov as a separate buffer and calling the SSL_write
on each buffer individually.

Furthermore, if not all underyling TLS libraries support vectored IO, then
having that as input to onReceive/onSend doesn't make sense.

Perhaps we can define new procedures in TLSService protocol for vectored IO
that return NotImplemented if underlying library doesn't support them, but
we need a basic one that handles one buffer at a time and is supported by
all security libraries.

g.

I think that this would actually be fine for a low level object.

But I’d like to see this working in the real world :-) The key part being that the TLS layer needs to buffer already read data until the TLS frames are complete. I know that this is possible with OpenSSL, and it was said that it should be possible with the low level macOS stuff, but I’d like to see this actually working. No offence, lets just make sure we don’t miss something in here as this is IMO very important.

hh

···

On 28. Mar 2017, at 12:03, Johannes Weiß <johannesweiss@apple.com> wrote:

Hi Helge and Gelareh,

[...]

If you are talking about non-blocking by any chance, we have an implementation of this in BlueSSLService (https://github.com/IBM-Swift/BlueSSLService\) which interfaces with a socket management layer (BlueSocket - https://github.com/IBM-Swift/BlueSocket\).

Sounds good. Do you have an example demonstrating the use of BlueSocket in NIO mode? Like a simple client which just opens a socket connection to a TLS server and reads with NIO configured.

as far as I understand the source code BlueSocket is blocking IO only. But its API basically breaks the abstraction by giving you (publically!) direct access to the the file descriptor.

  public internal(set) var socketfd: Int32 = SOCKET_INVALID_DESCRIPTOR

(from: https://github.com/IBM-Swift/BlueSocket/blob/master/Sources/Socket.swift#L772 )

so what you could do is

fcntl(someBlueSocket.socketFD, O_NONBLOCK, 1)

and then put in in a kqueue/epoll/DispatchSource . I personally don't think that counts as non-blocking API but it's possible :)

This I can’t follow. If OpenSSL supports this, it is absolutely worth supporting that in the API? OpenSSL will almost always be in use in server deployments - if that gets faster it matters a lot?

For SecureTransport you would just need to merge the buffers before handing them over. That is the same you expect from your API consumer today.

hh

···

On 31 Mar 2017, at 16:55, Gelareh Taban <gtaban@us.ibm.com> wrote:

Furthermore, if not all underyling TLS libraries support vectored IO, then having that as input to onReceive/onSend doesn't make sense.

Just to be accurate, BlueSocket also provides non-blocking I/O as well…

  ///
  /// Set blocking mode for socket.
  ///
  /// - Parameter shouldBlock: True to block, false to not.
  ///
  public func setBlocking(mode shouldBlock: Bool) throws

The default setting is to block. However, you can use this API to change that...

-Bill Abt

···

On Mar 28, 2017, at 6:22 AM, Michael Chiu via swift-server-dev <swift-server-dev@swift.org> wrote:

Just a random thought. How about an eventfd or kqueue EVFILT_USER event that simulates the behavior of a socket? Such that the TLS side can trigger the event and write the data size through evenfd or kevent.udata. The implementation can be done by protocol abstraction that socket and TLSService confirms to:

protocol Pollable {
var ident: Int32 { get }
}

protocol Readable {
func read(…)
}

Michael

On Mar 28, 2017, at 3:09 AM, Helge Heß via swift-server-dev <swift-server-dev@swift.org> wrote:

On 28. Mar 2017, at 12:03, Johannes Weiß <johannesweiss@apple.com> wrote:

Hi Helge and Gelareh,

[...]

If you are talking about non-blocking by any chance, we have an implementation of this in BlueSSLService (https://github.com/IBM-Swift/BlueSSLService\) which interfaces with a socket management layer (BlueSocket - https://github.com/IBM-Swift/BlueSocket\).

Sounds good. Do you have an example demonstrating the use of BlueSocket in NIO mode? Like a simple client which just opens a socket connection to a TLS server and reads with NIO configured.

as far as I understand the source code BlueSocket is blocking IO only. But its API basically breaks the abstraction by giving you (publically!) direct access to the the file descriptor.

  public internal(set) var socketfd: Int32 = SOCKET_INVALID_DESCRIPTOR

(from: https://github.com/IBM-Swift/BlueSocket/blob/master/Sources/Socket.swift#L772 )

so what you could do is

fcntl(someBlueSocket.socketFD, O_NONBLOCK, 1)

and then put in in a kqueue/epoll/DispatchSource . I personally don't think that counts as non-blocking API but it's possible :)

I think that this would actually be fine for a low level object.

But I’d like to see this working in the real world :-) The key part being that the TLS layer needs to buffer already read data until the TLS frames are complete. I know that this is possible with OpenSSL, and it was said that it should be possible with the low level macOS stuff, but I’d like to see this actually working. No offence, lets just make sure we don’t miss something in here as this is IMO very important.

hh

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

Hi Helge and Gelareh,

[...]

If you are talking about non-blocking by any chance, we have an

implementation of this in BlueSSLService (
https://github.com/IBM-Swift/BlueSSLService\) which interfaces with a socket
management layer (BlueSocket - https://github.com/IBM-Swift/BlueSocket\).

Sounds good. Do you have an example demonstrating the use of BlueSocket

in NIO mode? Like a simple client which just opens a socket connection to a
TLS server and reads with NIO configured.

as far as I understand the source code BlueSocket is blocking IO only.

But its API basically breaks the abstraction by giving you (publically!)
direct access to the the file descriptor.

     public internal(set) var socketfd: Int32 =

SOCKET_INVALID_DESCRIPTOR

(from:

https://github.com/IBM-Swift/BlueSocket/blob/master/Sources/Socket.swift#L772
)

so what you could do is

fcntl(someBlueSocket.socketFD, O_NONBLOCK, 1)

and then put in in a kqueue/epoll/DispatchSource . I personally don't

think that counts as non-blocking API but it's possible :)

I think that this would actually be fine for a low level object.

But I’d like to see this working in the real world :-) The key part being
that the TLS layer needs to buffer already read data until the TLS frames
are complete. I know that this is possible with OpenSSL, and it was said
that it should be possible with the low level macOS stuff, but I’d like to
see this actually working. No offence, lets just make sure we don’t miss
something in here as this is IMO very important.

[gelareh]

@hh: Kitura (which uses Kitura-net) uses non-blocking sockets so that code
path is exercised everytime we run Kitura-net on both mac and Linux.

I think that covers all the possibilities, but let me know if I'm missing
something.

@Johannes: in BlueSocket, go to
https://github.com/IBM-Swift/BlueSocket/blob/master/Sources/Socket.swift
and search for isBlocking. That should give a bunch of places where we test
for blocking and where the EAGAIN percolation is managed.

g.

···

On 28. Mar 2017, at 12:03, Johannes Weiß <johannesweiss@apple.com> wrote:

> Furthermore, if not all underyling TLS libraries support vectored IO,

then having that

> as input to onReceive/onSend doesn't make sense.

This I can’t follow. If OpenSSL supports this, it is absolutely worth

supporting that in the API?

OpenSSL will almost always be in use in server deployments -
if that gets faster it matters a lot?

Fair point.

My suggestion is maybe we can overload the onReceive/onSend functions so
that they can receive both Data and iov.
That way we can start with the simple (known) implementation of Data and
then iterate over the protocol with support for iov.

Thoughts?

···

From: Helge Heß via swift-server-dev <swift-server-dev@swift.org>
To: swift-server-dev <swift-server-dev@swift.org>
Date: 03/31/2017 10:19 AM
Subject: Re: [swift-server-dev] Draft proposal for TLS Service APIs
            (pleasereview)
Sent by: swift-server-dev-bounces@swift.org

On 31 Mar 2017, at 16:55, Gelareh Taban <gtaban@us.ibm.com> wrote:

Furthermore, if not all underyling TLS libraries support vectored IO,

then having that as input to onReceive/onSend doesn't make sense.

This I can’t follow. If OpenSSL supports this, it is absolutely worth
supporting that in the API? OpenSSL will almost always be in use in server
deployments - if that gets faster it matters a lot?

For SecureTransport you would just need to merge the buffers before handing
them over. That is the same you expect from your API consumer today.

hh

[attachment "signature.asc" deleted by Gelareh Taban/Austin/IBM]
_______________________________________________
swift-server-dev mailing list
swift-server-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-server-dev

:+1:

···

On Mar 28, 2017, at 9:44 PM, Gelareh Taban <gtaban@us.ibm.com> wrote:

@hh: Kitura (which uses Kitura-net) uses non-blocking sockets so that code path is exercised everytime we run Kitura-net on both mac and Linux.
https://github.com/IBM-Swift/Kitura-net/blob/34176a224a3317be14d0b5f038a690b969095726/Sources/KituraNet/IncomingSocketManager.swift#L111