Added support for X-Forwarded-For .., and Forwarded header, for the simple proxy implementation.
Also added the possibility to "keep" the original host name. Updated related example.
This commit is contained in:
@@ -9,41 +9,98 @@ class
|
|||||||
inherit
|
inherit
|
||||||
WSF_EXECUTION
|
WSF_EXECUTION
|
||||||
|
|
||||||
WSF_URI_REWRITER
|
|
||||||
rename
|
|
||||||
uri as proxy_uri
|
|
||||||
end
|
|
||||||
|
|
||||||
create
|
create
|
||||||
make
|
make
|
||||||
|
|
||||||
feature -- Basic operations
|
feature -- Basic operations
|
||||||
|
|
||||||
execute
|
execute
|
||||||
|
local
|
||||||
|
l_forwarded: BOOLEAN
|
||||||
do
|
do
|
||||||
-- NOTE: please enter the target server uri here
|
-- NOTE: please edit the proxy.conf file
|
||||||
-- replace "http://localhost:8080/foobar"
|
across
|
||||||
send_proxy_response ("http://localhost:8080/foobar", Current)
|
proxy_map as ic
|
||||||
|
until
|
||||||
|
l_forwarded
|
||||||
|
loop
|
||||||
|
if request.path_info.starts_with_general (ic.key) then
|
||||||
|
l_forwarded := True
|
||||||
|
send_proxy_response (ic.key, ic.item, agent proxy_uri (ic.key, ?))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not l_forwarded then
|
||||||
|
response.send (create {WSF_PAGE_RESPONSE}.make_with_body ("EiffelWeb proxy: not forwarded!"))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
send_proxy_response (a_remote: READABLE_STRING_8; a_rewriter: detachable WSF_URI_REWRITER)
|
proxy_map: HASH_TABLE [STRING, STRING]
|
||||||
|
-- location => target
|
||||||
|
local
|
||||||
|
f: PLAIN_TEXT_FILE
|
||||||
|
l_line: STRING
|
||||||
|
p: INTEGER
|
||||||
|
once ("thread")
|
||||||
|
create Result.make (1)
|
||||||
|
-- Load proxy.conf
|
||||||
|
create f.make_with_name ("proxy.conf")
|
||||||
|
if f.exists and then f.is_access_readable then
|
||||||
|
f.open_read
|
||||||
|
from
|
||||||
|
until
|
||||||
|
f.end_of_file or f.exhausted
|
||||||
|
loop
|
||||||
|
f.read_line
|
||||||
|
l_line := f.last_string
|
||||||
|
if l_line.starts_with ("#") then
|
||||||
|
-- ignore
|
||||||
|
else
|
||||||
|
-- Format:
|
||||||
|
-- path%Tserver
|
||||||
|
p := l_line.index_of ('%T', 1)
|
||||||
|
if p > 0 then
|
||||||
|
Result.force (l_line.substring (p + 1, l_line.count), l_line.head (p - 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send_proxy_response (a_location, a_remote: READABLE_STRING_8; a_rewriter: detachable FUNCTION [WSF_REQUEST, STRING])
|
||||||
local
|
local
|
||||||
h: WSF_SIMPLE_REVERSE_PROXY_HANDLER
|
h: WSF_SIMPLE_REVERSE_PROXY_HANDLER
|
||||||
do
|
do
|
||||||
create h.make (a_remote)
|
create h.make (a_remote)
|
||||||
h.set_uri_rewriter (a_rewriter)
|
if a_rewriter /= Void then
|
||||||
h.set_uri_rewriter (create {WSF_AGENT_URI_REWRITER}.make (agent proxy_uri))
|
h.set_uri_rewriter (create {WSF_AGENT_URI_REWRITER}.make (a_rewriter))
|
||||||
h.set_timeout (30) -- 30 seconds
|
end
|
||||||
|
h.set_timeout_ns (10_000_000_000) -- 10 seconds
|
||||||
h.set_connect_timeout (5_000) -- milliseconds = 5 seconds
|
h.set_connect_timeout (5_000) -- milliseconds = 5 seconds
|
||||||
|
|
||||||
|
-- Uncomment following, if you want to provide proxy information
|
||||||
|
-- h.set_header_via (True)
|
||||||
|
-- h.set_header_forwarded (True)
|
||||||
|
-- h.set_header_x_forwarded (True)
|
||||||
|
-- Uncomment following line to keep the original Host value.
|
||||||
|
-- h.keep_proxy_host (True)
|
||||||
|
|
||||||
h.execute (request, response)
|
h.execute (request, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Helpers
|
feature -- Helpers
|
||||||
|
|
||||||
proxy_uri (a_request: WSF_REQUEST): STRING
|
proxy_uri (a_location: READABLE_STRING_8; a_request: WSF_REQUEST): STRING
|
||||||
-- Request uri rewriten as url.
|
-- Request uri rewriten as url.
|
||||||
do
|
do
|
||||||
Result := a_request.request_uri
|
Result := a_request.request_uri
|
||||||
|
-- If related proxy setting is
|
||||||
|
-- a_location=/foo -> http://foo.com
|
||||||
|
-- and if request was http://example.com/foo/bar, it will use http://foo.com/bar
|
||||||
|
-- so the Result here, is "/bar"
|
||||||
|
if Result.starts_with (a_location) then
|
||||||
|
Result.remove_head (a_location.count)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
2
examples/proxy/proxy.conf
Normal file
2
examples/proxy/proxy.conf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/google/ http://www.google.com/search?q=eiffel
|
||||||
|
/ http://localhost:8080/testproxy
|
||||||
@@ -6,6 +6,14 @@ note
|
|||||||
class
|
class
|
||||||
WSF_SIMPLE_REVERSE_PROXY_HANDLER
|
WSF_SIMPLE_REVERSE_PROXY_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_TIMEOUT_UTILITIES
|
||||||
|
export
|
||||||
|
{NONE} all
|
||||||
|
end
|
||||||
|
|
||||||
|
ANY
|
||||||
|
|
||||||
create
|
create
|
||||||
make
|
make
|
||||||
|
|
||||||
@@ -14,9 +22,11 @@ feature {NONE} -- Initialization
|
|||||||
make (a_remote_uri: READABLE_STRING_8)
|
make (a_remote_uri: READABLE_STRING_8)
|
||||||
do
|
do
|
||||||
create remote_uri.make_from_string (a_remote_uri)
|
create remote_uri.make_from_string (a_remote_uri)
|
||||||
timeout := 30 -- seconds. See {NETWORK_SOCKET}.default_timeout
|
timeout_ns := 30_000_000_000 -- seconds. See {NETWORK_SOCKET}.default_timeout
|
||||||
connect_timeout := 5_000 -- 5 seconds.
|
connect_timeout := 5_000 -- 5 seconds.
|
||||||
is_via_header_supported := True
|
recv_timeout_ns := 5_000_000_000 -- 5 seconds
|
||||||
|
send_timeout_ns := 5_000_000_000 -- 5 seconds
|
||||||
|
is_forwarded_header_supported := True
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
@@ -33,13 +43,36 @@ feature -- Settings
|
|||||||
connect_timeout: INTEGER assign set_connect_timeout
|
connect_timeout: INTEGER assign set_connect_timeout
|
||||||
-- In milliseconds.
|
-- In milliseconds.
|
||||||
|
|
||||||
timeout: INTEGER assign set_timeout
|
timeout_ns,
|
||||||
-- In seconds.
|
recv_timeout_ns,
|
||||||
|
send_timeout_ns: NATURAL_64
|
||||||
|
|
||||||
is_via_header_supported: BOOLEAN
|
is_via_header_supported: BOOLEAN assign set_header_via
|
||||||
-- Via: header supported.
|
-- "Via:" header supported.
|
||||||
|
-- See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Via
|
||||||
-- Default: True.
|
-- Default: True.
|
||||||
|
|
||||||
|
is_forwarded_header_supported: BOOLEAN assign set_header_forwarded
|
||||||
|
-- "Forwarded:" header supported (standard)
|
||||||
|
-- Forwarded: by=<identifier>;for=<identifier>;host=<host>;proto=<http|https>
|
||||||
|
-- See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded
|
||||||
|
-- and https://tools.ietf.org/html/rfc7239#section-4
|
||||||
|
-- Default: False
|
||||||
|
|
||||||
|
is_x_forwarded_header_supported: BOOLEAN assign set_header_x_forwarded
|
||||||
|
-- "X-Forwarded-For:" header supported (XFF), and related ...
|
||||||
|
-- See: de-facto standard https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
|
||||||
|
-- https://en.wikipedia.org/wiki/X-Forwarded-For
|
||||||
|
-- Default: False
|
||||||
|
|
||||||
|
is_using_proxy_host: BOOLEAN assign keep_proxy_host
|
||||||
|
-- Do not change the HTTP_HOST.
|
||||||
|
-- Default: False
|
||||||
|
|
||||||
|
feature -- Forwarded header settings
|
||||||
|
|
||||||
|
header_forwarded_by: detachable READABLE_STRING_8 assign set_header_forwarded_by
|
||||||
|
|
||||||
feature -- Change
|
feature -- Change
|
||||||
|
|
||||||
set_uri_rewriter (a_rewriter: like uri_rewriter)
|
set_uri_rewriter (a_rewriter: like uri_rewriter)
|
||||||
@@ -47,10 +80,30 @@ feature -- Change
|
|||||||
uri_rewriter := a_rewriter
|
uri_rewriter := a_rewriter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
feature -- Timeout Change
|
||||||
|
|
||||||
set_timeout (a_timeout_in_seconds: INTEGER)
|
set_timeout (a_timeout_in_seconds: INTEGER)
|
||||||
-- in seconds.
|
-- in seconds.
|
||||||
do
|
do
|
||||||
timeout := a_timeout_in_seconds
|
set_timeout_ns (seconds_to_nanoseconds (a_timeout_in_seconds))
|
||||||
|
end
|
||||||
|
|
||||||
|
set_timeout_ns (a_timeout_ns: NATURAL_64)
|
||||||
|
-- in nanoseconds.
|
||||||
|
do
|
||||||
|
timeout_ns := a_timeout_ns
|
||||||
|
end
|
||||||
|
|
||||||
|
set_recv_timeout_ns (ns: NATURAL_64)
|
||||||
|
-- in nanoseconds.
|
||||||
|
do
|
||||||
|
recv_timeout_ns := ns
|
||||||
|
end
|
||||||
|
|
||||||
|
set_send_timeout_ns (ns: NATURAL_64)
|
||||||
|
-- in nanoseconds.
|
||||||
|
do
|
||||||
|
send_timeout_ns := ns
|
||||||
end
|
end
|
||||||
|
|
||||||
set_connect_timeout (a_timeout_in_milliseconds: INTEGER)
|
set_connect_timeout (a_timeout_in_milliseconds: INTEGER)
|
||||||
@@ -59,12 +112,37 @@ feature -- Change
|
|||||||
connect_timeout := a_timeout_in_milliseconds
|
connect_timeout := a_timeout_in_milliseconds
|
||||||
end
|
end
|
||||||
|
|
||||||
set_is_via_header_supported (b: BOOLEAN)
|
feature -- Header Change
|
||||||
|
|
||||||
|
set_header_forwarded (b: BOOLEAN)
|
||||||
|
-- Set `is_forwarded_header_supported` to `b`.
|
||||||
|
do
|
||||||
|
is_forwarded_header_supported := b
|
||||||
|
end
|
||||||
|
|
||||||
|
set_header_forwarded_by (a_id: like header_forwarded_by)
|
||||||
|
do
|
||||||
|
header_forwarded_by := a_id
|
||||||
|
end
|
||||||
|
|
||||||
|
set_is_via_header_supported,
|
||||||
|
set_header_via (b: BOOLEAN)
|
||||||
-- Set `is_via_header_supported' to `b'.
|
-- Set `is_via_header_supported' to `b'.
|
||||||
do
|
do
|
||||||
is_via_header_supported := b
|
is_via_header_supported := b
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_header_x_forwarded (b: BOOLEAN)
|
||||||
|
-- Set `is_x_forwarded_header_supported` to `b`.
|
||||||
|
do
|
||||||
|
is_x_forwarded_header_supported := b
|
||||||
|
end
|
||||||
|
|
||||||
|
keep_proxy_host (b: BOOLEAN)
|
||||||
|
do
|
||||||
|
is_using_proxy_host := b
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Execution
|
feature -- Execution
|
||||||
|
|
||||||
proxy_uri (request: WSF_REQUEST): STRING
|
proxy_uri (request: WSF_REQUEST): STRING
|
||||||
@@ -90,20 +168,31 @@ feature -- Execution
|
|||||||
l_completed: BOOLEAN
|
l_completed: BOOLEAN
|
||||||
l_remote_uri: like remote_uri
|
l_remote_uri: like remote_uri
|
||||||
l_socket_factory: WSF_PROXY_SOCKET_FACTORY
|
l_socket_factory: WSF_PROXY_SOCKET_FACTORY
|
||||||
|
l_forwarded: STRING
|
||||||
|
s: READABLE_STRING_8
|
||||||
do
|
do
|
||||||
l_remote_uri := remote_uri
|
l_remote_uri := remote_uri
|
||||||
create l_socket_factory
|
create l_socket_factory
|
||||||
if not l_socket_factory.is_uri_supported (l_remote_uri) then
|
if not l_socket_factory.is_uri_supported (l_remote_uri) then
|
||||||
send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, l_remote_uri.scheme + " is not supported! [for remote " + l_remote_uri.string + "]")
|
send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, l_remote_uri.scheme + " is not supported! [for remote " + l_remote_uri.string + "]")
|
||||||
elseif attached l_socket_factory.socket_from_uri (l_remote_uri) as l_socket then
|
elseif attached {NETWORK_STREAM_SOCKET} l_socket_factory.socket_from_uri (l_remote_uri) as l_socket then
|
||||||
l_socket.set_connect_timeout (connect_timeout) -- milliseconds
|
l_socket.set_connect_timeout (connect_timeout) -- milliseconds
|
||||||
l_socket.set_timeout (timeout) -- seconds
|
l_socket.set_timeout_ns (timeout_ns)
|
||||||
|
l_socket.set_recv_timeout_ns (recv_timeout_ns)
|
||||||
|
l_socket.set_send_timeout_ns (send_timeout_ns)
|
||||||
|
|
||||||
l_socket.connect
|
l_socket.connect
|
||||||
if l_socket.is_connected then
|
if
|
||||||
|
l_socket.is_connected and then
|
||||||
|
attached l_socket.peer_address as l_socket_peer_address
|
||||||
|
then
|
||||||
create l_http_query.make_from_string (request.request_method)
|
create l_http_query.make_from_string (request.request_method)
|
||||||
l_http_query.append_character (' ')
|
l_http_query.append_character (' ')
|
||||||
l_http_query.append (l_remote_uri.path)
|
l_http_query.append (l_remote_uri.path)
|
||||||
|
if attached l_remote_uri.query as q then
|
||||||
|
l_http_query.append_character ('?')
|
||||||
|
l_http_query.append (q)
|
||||||
|
end
|
||||||
l_http_query.append (proxy_uri (request))
|
l_http_query.append (proxy_uri (request))
|
||||||
l_http_query.append_character (' ')
|
l_http_query.append_character (' ')
|
||||||
l_http_query.append (request.server_protocol)
|
l_http_query.append (request.server_protocol)
|
||||||
@@ -115,15 +204,22 @@ feature -- Execution
|
|||||||
else
|
else
|
||||||
create h.make_from_raw_header_data (l_raw_header.to_string_8)
|
create h.make_from_raw_header_data (l_raw_header.to_string_8)
|
||||||
end
|
end
|
||||||
if attached l_remote_uri.host as l_remote_host then
|
s := Void
|
||||||
|
if is_using_proxy_host then
|
||||||
|
if attached request.http_host as l_request_host then
|
||||||
|
s := l_request_host
|
||||||
|
end
|
||||||
|
elseif attached l_remote_uri.host as l_remote_host then
|
||||||
|
s := l_remote_host
|
||||||
if l_remote_uri.port > 0 then
|
if l_remote_uri.port > 0 then
|
||||||
h.put_header_key_value ("Host", l_remote_host + ":" + l_remote_uri.port.out)
|
s := s + ":" + l_remote_uri.port.out
|
||||||
else
|
|
||||||
h.put_header_key_value ("Host", l_remote_host)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if s /= Void then
|
||||||
|
h.put_header_key_value ("Host", s)
|
||||||
|
end
|
||||||
|
|
||||||
-- Via header
|
-- Proxy related headers
|
||||||
if is_via_header_supported then
|
if is_via_header_supported then
|
||||||
if attached h.item ("Via") as v then
|
if attached h.item ("Via") as v then
|
||||||
l_via := v
|
l_via := v
|
||||||
@@ -131,9 +227,51 @@ feature -- Execution
|
|||||||
else
|
else
|
||||||
create l_via.make_empty
|
create l_via.make_empty
|
||||||
end
|
end
|
||||||
l_via.append (request.server_protocol + " " + request.server_name + " (PROXY-" + request.server_software + ")")
|
l_via.append (request.server_protocol)
|
||||||
|
l_via.append_character (' ')
|
||||||
|
l_via.append (request.server_name)
|
||||||
|
l_via.append (" (PROXY-")
|
||||||
|
l_via.append (request.server_software)
|
||||||
|
l_via.append_character (')')
|
||||||
h.put_header_key_value ("Via", l_via)
|
h.put_header_key_value ("Via", l_via)
|
||||||
end
|
end
|
||||||
|
if is_forwarded_header_supported then
|
||||||
|
-- Forwarded: for=<identifier>;host=<host>;proto=<http|https>
|
||||||
|
create l_forwarded.make (50)
|
||||||
|
l_forwarded.append ("for=")
|
||||||
|
l_forwarded.append (request.remote_addr)
|
||||||
|
if attached request.http_host as l_host then
|
||||||
|
l_forwarded.append (";host=")
|
||||||
|
l_forwarded.append (l_host)
|
||||||
|
end
|
||||||
|
l_forwarded.append (";proto=")
|
||||||
|
if request.is_https then
|
||||||
|
l_forwarded.append ("https")
|
||||||
|
else
|
||||||
|
l_forwarded.append ("http")
|
||||||
|
end
|
||||||
|
if attached header_forwarded_by as l_id then
|
||||||
|
l_forwarded.append (";by=")
|
||||||
|
l_forwarded.append (l_id)
|
||||||
|
end
|
||||||
|
if attached request.meta_string_variable ("HTTP_FORWARDED") as l_req_forwarded then
|
||||||
|
l_forwarded := l_req_forwarded + ", " + l_forwarded
|
||||||
|
end
|
||||||
|
h.put_header_key_value ("Forwarded", l_forwarded)
|
||||||
|
end
|
||||||
|
if is_x_forwarded_header_supported then
|
||||||
|
s := request.remote_addr
|
||||||
|
if attached request.meta_string_variable ("HTTP_X_FORWARDED_FOR") as l_xff then
|
||||||
|
s := l_xff + ", " + s
|
||||||
|
end
|
||||||
|
h.put_header_key_value ("X-Forwarded-For", s)
|
||||||
|
h.put_header_key_value ("X-Forwarded-Port", request.server_port.out)
|
||||||
|
if request.is_https then
|
||||||
|
h.put_header_key_value ("X-Forwarded-Proto", "https")
|
||||||
|
else
|
||||||
|
h.put_header_key_value ("X-Forwarded-Proto", "http")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Max-Forwards header handling
|
-- Max-Forwards header handling
|
||||||
if attached h.item ("Max-Forwards") as h_max_forward then
|
if attached h.item ("Max-Forwards") as h_max_forward then
|
||||||
|
|||||||
Reference in New Issue
Block a user