Skip to content

Commit

Permalink
Add HTTP::Request#remote_address
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite committed Apr 1, 2019
1 parent 05e2a10 commit 9107eb5
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
50 changes: 50 additions & 0 deletions spec/std/http/server/server_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,56 @@ 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

spawn { server.listen }

client = HTTP::Client.new(URI.parse("http://#{address1}/"))
client.get("/")

remote_address.should eq(client.@socket.as(IPSocket).local_address.to_s)

client.close
server.close
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

spawn server.listen

client = HTTP::Client.new(
uri: URI.parse("https://#{ip_address1}"),
tls: client_context)
client.get("/")

remote_address.should eq(client.@socket.as(OpenSSL::SSL::Socket).local_address.to_s)

client.close
server.close
end
end

it "handles Errno" do
processor = HTTP::Server::RequestProcessor.new { }
input = RaiseErrno.new(Errno::ECONNRESET)
Expand Down
17 changes: 16 additions & 1 deletion src/http/request.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions src/openssl/ssl/socket.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 9107eb5

Please sign in to comment.