From 52588f7753cfeea589610ffcfd55d63b6d1fc59c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 1 Apr 2019 07:47:40 -0300 Subject: [PATCH] Add HTTP::Request#remote_address --- spec/std/http/server/server_spec.cr | 45 +++++++++++++++++++++++++++++ spec/std/http/spec_helper.cr | 1 + src/http/request.cr | 17 ++++++++++- src/openssl/ssl/socket.cr | 10 +++++++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/spec/std/http/server/server_spec.cr b/spec/std/http/server/server_spec.cr index 9e1444038a30..9a76a4fa6e3a 100644 --- a/spec/std/http/server/server_spec.cr +++ b/spec/std/http/server/server_spec.cr @@ -835,6 +835,51 @@ module HTTP end end + describe "#remote_address" do + it "for http server" do + remote_address = nil + + server = Server.new do |context| + remote_address = context.request.remote_address + end + + tcp_server = TCPServer.new("127.0.0.1", 0) + server.bind tcp_server + address1 = tcp_server.local_address + + run_server(server) do + HTTP::Client.new(URI.parse("http://#{address1}/")) do |client| + client.get("/") + + remote_address.should eq(client.@socket.as(IPSocket).local_address.to_s) + end + end + end + + it "for https server" do + remote_address = nil + + server = Server.new do |context| + remote_address = context.request.remote_address + end + + server_context, client_context = ssl_context_pair + + socket = OpenSSL::SSL::Server.new(TCPServer.new("127.0.0.1", 0), server_context) + server.bind socket + ip_address1 = server.bind_tls "127.0.0.1", 0, server_context + + run_server(server) do + HTTP::Client.new( + uri: URI.parse("https://#{ip_address1}"), + tls: client_context) do |client| + client.get("/") + remote_address.should eq(client.@socket.as(OpenSSL::SSL::Socket).local_address.to_s) + end + end + end + end + it "handles Errno" do processor = HTTP::Server::RequestProcessor.new { } input = RaiseErrno.new(Errno::ECONNRESET) diff --git a/spec/std/http/spec_helper.cr b/spec/std/http/spec_helper.cr index db1b3d8eb5f7..8fe39b8dcd07 100644 --- a/spec/std/http/spec_helper.cr +++ b/spec/std/http/spec_helper.cr @@ -1,4 +1,5 @@ require "spec" +require "../spec_helper" private def wait_for(timeout = 5.seconds) now = Time.monotonic diff --git a/src/http/request.cr b/src/http/request.cr index 8a7ee7cee8d9..5892f42be9ad 100644 --- a/src/http/request.cr +++ b/src/http/request.cr @@ -20,6 +20,15 @@ class HTTP::Request @query_params : Params? @uri : URI? + # The network address that sent the request to an HTTP server. + # + # `HTTP::Server` will try to fill this property, and its value + # will have a format like "IP:port", but this format is not guaranteed. + # Middlewares can overwrite this value. + # + # This property is not used by `HTTP::Client`. + property remote_address : String? + def initialize(@method : String, @resource : String, headers : Headers? = nil, body : String | Bytes | IO | Nil = nil, @version = "HTTP/1.1") @headers = headers.try(&.dup) || Headers.new self.body = body @@ -99,7 +108,13 @@ class HTTP::Request return BadRequest.new unless HTTP::SUPPORTED_VERSIONS.includes?(http_version) HTTP.parse_headers_and_body(io) do |headers, body| - return new method, resource, headers, body, http_version + request = new method, resource, headers, body, http_version + + if io.responds_to?(:remote_address) + request.remote_address = io.remote_address.try &.to_s + end + + return request end # Malformed or unexpectedly ended http request diff --git a/src/openssl/ssl/socket.cr b/src/openssl/ssl/socket.cr index 0f490882e811..7371a44c07f9 100644 --- a/src/openssl/ssl/socket.cr +++ b/src/openssl/ssl/socket.cr @@ -179,4 +179,14 @@ abstract class OpenSSL::SSL::Socket < IO String.new(host_name) end end + + def local_address + io = @bio.io + io.responds_to?(:local_address) ? io.local_address : nil + end + + def remote_address + io = @bio.io + io.responds_to?(:remote_address) ? io.remote_address : nil + end end