[swift-server-dev] Draft proposal for TLS Service APIs (please review)

Michael Chiu hatsuneyuji at icloud.com
Sat Apr 1 02:38:26 CDT 2017


I also have a few thought on the TLS API, particularly on the stream side. I think we can leverage swift as a protocol oriented language, so instead of sending one / some buffers, we can do something like

protocol RawBytesRepresentable {
    var temporaryBytesBuffer: UnsafeRawBufferPointer { get } // allocation method provided in the user's side
    func bufferDestory() // deallocate method, provided by user and performed on the TLS’s side
}

func tlsWrite(contents: inout AnyCollection<RawBytesRepresentable>) {
    for buffer in contents {
        … copy to BIO …
        buffer.bufferDestory()
   }
   …omit….
}      

We will have the benefit on transmitting swift structs / classes (as long as they confirms the protocol) and minimize unnecessary memory allocations. (For example for some structure implicitly contain its storage there’s will be no more allocation at all and the destroy method can simply be an empty function).

Personally an idea TLS framework for me is that it returns a TLSContext object that I can somehow poll just like a socket and file descriptor (can be done by eventfd/kqueue), I mean normal you have to read from a socket, then it to the TLS, and then to check if the TLS has enough data to process and if not you feed it again. 

Having a customize reader (how to read from a source) and writer (how to write to a source) is just amazing since I can tell the TLS how to do that by themselves and hence reduce the complexity of the main event loop by a lot.

For example if I’m communicating to an embed device using TLS on top of I2C

public struct Little_device :  TransportManagementDelegate {
    // teach the TLS framework to write with i2c
    public func writer(contents: AnyCollection<RawBytesRepresentable>) {
	i2c_write(my_device, contents)
    }
    // teach the TLS how to read from an i2c bus
    public func reader() -> AnyCollection<RawBytesRepresentable> {
       var readStack = Stack<Payload>() 
       while (some_condition) {
           readStack.push(i2c_read(my_device,))
       }
       return readStack
    }
}

let tls_context = TLSServer(source: some_source_dont_have_2_be_fd)
my_event_loop.add(tls_context)

…omit….now some where in the event loop
var newConnection = triggered_server_context.accept() // tls accept performed here
my_event_loop.add(newConnection)

> Why do these methods all have "on" prefixes? I'm not totally sure I understand the intended usage here, but I see two possibilities:
> 
> * These are imperative commands. `onAccept` says that the TLS engine should accept a connection, `onSend` means it should send some data, etc. In that case, these should not have any prefix—they should just be `accept`, `send`, etc.
> 
> * 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.

I totally agree that the on-prefix is not quite swift like and the ‘will' and ‘did’ part. 
> I take it the parameter is some sort of abstraction wrapping e.g. a socket. So why is it called a `TransportManagementDelegate`? Shouldn't its name include words like `Connection` or `Socket` or `IOHandle` or something?
> 
> Do we want the parameter to be labeled `IORef`? That's not very idiomatic; it doesn't read well or follow the Swift naming guidelines.
> 
> You say this is for both clients and servers. When does a TLS client have a listening connection that it `accept`s connections on?
> 
> Is it called at different times or in different ways than `onServerCreate`?

I can understand this one, even on LiberalSSL you have to manually call tls_accept() after you accept() it from the socket, so it will indeed make some sense to include this method, especially usefully when the user want to store the TLS context somewhere. 
> 
> This sounds like the TLS engine owns the network connection (at least by this point) and is responsible for writing to it. Does that mean `accept` and `connect` take ownership of the connection and hold on to it? If you have several different simultaneous connections, how do you know which connection this should write to? Or does a given TLSService only own one connection at a time? If so, does the transport management layer create a new TLSService instance for each connection? How? If each TLSService is bound to one connection, shouldn't it be created already knowing the connection it's going to use?

The thing about TLS is that itself is a separate layer from the transport layer but it is act like an extra transport layer itself, where accept/connect need to do twice (one on the transport layer, one of the TLS layer), and I can understand the users might want to inject something in between each procedure. For some reasons, depends on how the user want to use TLS for it can be one (real) connection but multiple (tls) connection, for example wrapping TLS in an extra layer of protocol. In this case having an abstract TLSContext representing the TLS connection is more appropriate then have the TLS framework take over the control of the socket.

Michael

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-server-dev/attachments/20170401/28f5230f/attachment.html>


More information about the swift-server-dev mailing list