-
Notifications
You must be signed in to change notification settings - Fork 48
Move HTTPServer options from Start into init #81
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My opinions attached ;-)
Sources/HTTP/HTTPServer.swift
Outdated
/// Note: if port 0 is requested, a random port will be assigned and HTTPServer.port value will | ||
/// diverge from HTTPServer.Options.port | ||
public let port: Int | ||
public let handler: HTTPRequestHandler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would keep the handler out of the options. The options should describe the server environment. The handler does the handling.
Sources/HTTP/HTTPServer.swift
Outdated
private let server = PoCSocketSimpleServer() | ||
|
||
/// Create an instance of the server. This needs to be followed with a call to `start(port:handler:)` | ||
public init() { | ||
public init(with newOptions: Options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be init(handler: .., options:)
. The with
style is for method names, not for init (I think, not sure).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the names, I followed the patterns of dataTask.
dataTask(with:)
https://developer.apple.com/documentation/foundation/urlsession/1411554-datatask
dataTask(with:completionHandler:)
https://developer.apple.com/documentation/foundation/urlsession/1410330-datatask
I agree with moving to options, particularly as we might have a large set with TLS added. I'm wondering about the move of the handler into let server = HTTPServer(port: 0) {request, response in
response.writeHeader(status: .ok)
response.writeBody("Hello, World!")
response.done()
return .discardBody
} Of course, its a reasonable argument that we intentionally want to discourage the use of anonymous closures... |
I would say your example actually looks quite nice and matches what you'd do in Foundation. Part of my reasoning of moving the handler and such to init is that they can be (P.S./: don't do: |
Thanks @helje5 , @seabaylea, for your comments. I'll move handle out of options. |
@gtaban remember to keep the handler as the last option, so that its possible to define an anonymous closure (if you want to). For The kernel assign port is probably useful for testing, but is it required there (or for any other use cases)? |
Kernel assigned ports are very useful, I use them all the time for plenty of scenarios (backend servers, on-device per-app servers, etc.). |
Oh, and having said that. I have a small set of extensions for the Posix that make them really easy to use as-is (wildcard binding, address binding, all that). Feel free to integrate them: https://github.com/NozeIO/Noze.io/blob/master/Sources/net/SocketAddress.swift#L27 |
Both Node.js and Golang use the convention of Given that I assume Noze uses |
Hm, I guess I don't care too much. An optional port seems like the correct choice for Swift to me, but well. (in the end, sockaddr_in also uses 0 for the wildcard port ...) I think it is more important to not use a plain port, but a sockaddr. Because the same way like you want to do wildcard ports, you want to do wildcard addresses, or not, or different addresses. (not wanting to do non-wildcard addresses seems even more important than wildcard ports to me - aka bind to specific server interfaces) |
Agreed - we definitely need support for full addresses to be passed in via Options. |
An open question is, do we want to support multiple binds? I tend to say yes. This would modify the structure of the server a little. |
Sources/HTTP/HTTPServer.swift
Outdated
public class Options { | ||
/// Note: if port 0 is requested, a random port will be assigned and HTTPServer.port value will | ||
/// diverge from HTTPServer.Options.port | ||
public let port: Int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the type of port
be UInt16
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should not be a port in the first place but either a sockaddr
or [sockaddr]
, port
should only be a convenience initialiser.
In consequence your question would be answered (depends on the address type, protocol and sometimes the OS).
Sources/HTTP/HTTPServer.swift
Outdated
|
||
/// A subclass for HTTP Options | ||
/// HTTPServer to be created on the given `port`, using `handler` to process incoming requests | ||
public class Options { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be a struct
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, it should be a struct if the class is public
anyways. It should be open
instead. (Same maybe for HTTPServer
.)
Also: the comment is wrong /// A subclass for HTTP Options
. This is not a subclass but a root class.
Struct vs Class: Depends a little on whether we want to allow subclassing. The idea to use a class comes from my 2017-10-30 email:
class HTTPServer {
class Options { // a class because subclasses may want to add
It could be a struct, but this would require wrapping if you add fields to it (very common in JS style options
arrays). E.g. a framework on top, which wraps HTTPServer, could still use the same configuration, but add a few things (say a logger, or some template path).
Personally I don't care to much in this case. Either would be fine for me. But if it is a class, it needs to be open
, that is the whole point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of open class
for options (for example, it can be used by PoCSocketSimpleServer
) but then should Options
be defined within HTTPServer
? (we do get namespacing, but will it be cleaner if Options
is defined outside of HTTPServer
?
Also what use is open
Options but only public
HTTPServer?
Good pick up on the comment. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also what use is open Options but only public HTTPServer.
public class MyExpressServer {
open class Options : HTTPServer.Options {
let templatePath : String
}
let http : HTTPServer
}
But maybe it should be just a struct. Seems cleaner for such an isolated project.
P.S.: We could have a general discussion of whether we want the classes open
or closed. I'm generally in favour of open
, because you never know, but Swift style may be different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your example is exactly what I wanted to use for PoCSockerSimpleServer
that I mentioned.
And what sold me on making options open. :-P
Opened #91 to track the issue of multiple binds per server vs multiple servers for each port |
Updated based on comments. @helje5 can you verify? |
5c957c1
to
d6252ea
Compare
…on class and pass in init.
d6252ea
to
2e987e1
Compare
I agree with @helje5 that using an Optional (sockaddr wrapper) for kernel-assigned port feels like the right choice for Swift. |
Remove arguments from HTTPServer start function and put it in an option class and pass in init.