Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP::Server#bind(URI) #6500

Conversation

straight-shoota
Copy link
Member

@straight-shoota straight-shoota commented Aug 7, 2018

This PR was originally included in #5776 but it had been removed as a separate feature. Now that #5959 and #5960 are merged, it is ready to be reviewed. The original description can be found in #2735 (comment)

The URI can be parsed from a string and contains the protocol and address to bind to (plus optional additional configuration for SSL context). This allows for a uniform and easily exchangeable specification in config files.

server.bind "tcp://127.0.0.1:80"
server.bind "unix:///tmp/server.sock"
server.bind "ssl://127.0.0.1:443?key=private.key&cert=certificate.cert&ca=ca.crt"

Along the way this also adds Socket::{IP,UNIX,}Address.parse and OpenSSL::SSL::Context::{Client,Server}.parse to implement the individual component parsers.

# It expects the URI to include `<scheme>://<path>` where `scheme` as well
# as any additional URI components (such as `fragment` or `query`) are ignored.
#
# If `host` is not empty, it will be prepended to `path` to form a relatve
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "relatve"

# Parses a `Socket::IPAddress` from an URI.
#
# It expects the URI to include `<scheme>://<host>:<port>` where `scheme` as
# well asa ny additional URI components (such as `path` or `query`) are ignored.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "asa ny"

@straight-shoota straight-shoota force-pushed the jm/feature/http-server-parse-address branch from 8ce0453 to 9e23371 Compare August 7, 2018 12:56
address = Socket::IPAddress.parse(uri)
context = OpenSSL::SSL::Context::Server.parse(HTTP::Params.parse(uri.query || ""))

bind_ssl(address, context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking for flag?(:without_openssl) is missing here. At least with a duplication of the Unsupported socket type message or something more specific.

#
# context = OpenSSL::SSL::Context::Client.parse({"key" => "private.key", "cert" => "certificate.crt", "ca" => "ca.pem"})
# ```
def self.parse(params) : self
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect T.parse to be from String -> T. I would suggest T.from_hash for this method.

bind(URI.parse(uri))
end

# ditto
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warn: mixed ditto and :ditto: in the PR

# Socket::IPAddress.parse("tcp://127.0.0.1:8080") # => Socket::IPAddress.new("127.0.0.1", 8080)
# Socket::IPAddress.parse("udp://[::1]:8080") # => Socket::IPAddress.new("::1", 8080)
# ```
def self.parse(uri : URI) : IPAddress
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow I'm ok with T.parse: URI -> T 😹 , but not with Hash.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URI is essentially a structured string...

@straight-shoota straight-shoota force-pushed the jm/feature/http-server-parse-address branch from 9e23371 to d517ed6 Compare August 7, 2018 14:10
#
# Params:
#
# * `key` *(required*): Path to private key file. See `#private_key=`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*(required*) -> (*required*)
ditto below

The URI can be parsed from a string and contains the protocol and address to bind to (plus optional additional configuration for SSL context). This allows for a uniform and easily exchangable specificitation in config files.
@straight-shoota straight-shoota force-pushed the jm/feature/http-server-parse-address branch from d517ed6 to 0620467 Compare August 7, 2018 21:24
Copy link
Member

@sdogruyol sdogruyol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @straight-shoota 👍

@sdogruyol sdogruyol merged commit 928a738 into crystal-lang:master Aug 8, 2018
@sdogruyol sdogruyol added this to the 0.26.0 milestone Aug 8, 2018
@straight-shoota straight-shoota deleted the jm/feature/http-server-parse-address branch August 8, 2018 08:28
@bcardiff
Copy link
Member

@straight-shoota

The sample in the PR description mention ssh:// as a protocol, but it should be ssl:// probably.
The port used is not the default 443, although any port is supported using defaults seems better for lazy copy/pasting.

server.bind "ssl://127.0.0.1:443?key=private.key&cert=certificate.cert&ca=ca.crt

Or am I missing something?

@straight-shoota
Copy link
Member Author

You're absolute right.

@jhass
Copy link
Member

jhass commented Aug 10, 2018

Bold proposal: Lets ditch ssl:// and only support tls://

@straight-shoota
Copy link
Member Author

All the classes in Crystal are called SSL... so I'd at least keep support for ssl://. But adding tls:// and using it as default (in examples) sounds good.

@jhass
Copy link
Member

jhass commented Aug 10, 2018

No, they're called OpenSSL, because they follow the name of the library they wrap. On the other hand we already renamed Http's ssl flags to tls at some point.

@straight-shoota
Copy link
Member Author

No, I mean inside the OpenSSL namespace (which is obviously named after the library), there is OpenSSL::SSL.

But I'd suggest to discuss this further in a new dedicated issue...

@jhass
Copy link
Member

jhass commented Aug 10, 2018

I wouldn't be too opposed to renaming that to TLS too :P

@RX14
Copy link
Contributor

RX14 commented Aug 11, 2018

OpenSSL namespace will eventually go and be replaced with a TLS namespace with a much better API - which can be supported by libraries which aren't openssl. So +1 to supporting tls but supporting ssl too is fine.

@straight-shoota
Copy link
Member Author

I'll work on that. Waiting for #6530 to be merged.

@crisward
Copy link
Contributor

I realise this has already been merged, but FWIW I would have expected to be able to bind to do server.bind "http://... and server.bind "https://... (as aliases for tcp and ssl). Apologies if this has already been discussed somewhere else. Just saw this in the 0.26 release post. Congrats BTW on the new release!

@straight-shoota
Copy link
Member Author

straight-shoota commented Aug 14, 2018

No, this wasn't discussed before explicitly. And I admit that this might look a bit surprising at first.

The reason is that HTTP and HTTPS are application protocols - and HTTP::Server naturally speaks HTTP(S) on all connections. server.bind "http://[...]" would only tell to use the HTTP protocol (which is obvious since this is an HTTP server).

The URIs used by bind are meant to specify the transport protocol (TCP, TCP+TLS or UNIX socket).
This is probably most clear when looking at tls://127.0.0.1:443?key=private.key&cert=certificate.cert&ca=ca.crt. This URI configures a TCP+TLS server and is not an HTTP resource.

The same transport protocol schemes are also used by similar applications such as Puma or Nginx.

@crisward
Copy link
Contributor

Wasn't aware of other servers using these protocols, makes sense. Thanks for the clarification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants