Skip to content

Commit

Permalink
headers: make sure to forward most target response headers (#19)
Browse files Browse the repository at this point in the history
This commit forwards all headers from the target server response, but
removes:
- the `set-cookie` header to avoid transfering authentication data via
the proxy (it's not its role to authenticate any clients)
- the `transfer-encoding` header, because it's a [“hop-by-hop”
header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding)
and not supposed to be forwarded. The proxy reads the response (and
should thus consume the `transfer-encoding` header). If one day the
proxy supports streaming responses, we will have to manually add this
header as per the HTTP spec.
  • Loading branch information
paulRbr authored Dec 12, 2024
1 parent 80c8100 commit 075220a
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 8 deletions.
40 changes: 33 additions & 7 deletions proxy_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ class ProxyServer < Sinatra::Base
ENV.fetch("JWT_SIGNING_PUBLIC_KEY").gsub("\\n", "\n")
).freeze

# Remove some headers from proxied requests.
HEADERS_SKIP_FORWARD = [
"set-cookie", # Don't forward authenticated cookies data
"transfer-encoding" # Don't forward transfer-encoding as this is a
# “hop-by-hop” header which needs to be
# consumed by the proxy when reading the
# target response.
].freeze

TOKEN_HEADER = ENV.fetch(
"CORS_TOUJOURS_TOKEN_HEADER_NAME",
"x-cors-toujours-token"
Expand Down Expand Up @@ -119,6 +128,12 @@ def path_matches_pattern?(actual_path, pattern_path)
Regexp.new(pattern_regex).match?(actual_path)
end

def skip_header?(key)
HEADERS_SKIP_FORWARD.any? do |header|
key.downcase.start_with?(header)
end
end

def forward_request(method)
target_url = request.fullpath[1..].gsub(":/", "://")
uri = URI.parse(target_url)
Expand Down Expand Up @@ -160,18 +175,29 @@ def forward_request(method)

# Execute the request to the target server
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
# This has the effect to .read the response body directly. In
# case we want to stream the response we might want to use a
# block with http.request(..) do |response| at some
# point. Especially when we will want to proxy file download
# requests.
response = http.request(target_request)

# Pass the target server response back to the client
# Forward the raw target response back to the client.

# RESPONSE CODE
status response.code
headers "Content-Type" => response.content_type

content_encoding = response.get_fields "content-encoding"
if content_encoding && content_encoding.include?("gzip")
body Zlib::GzipReader.new(StringIO.new(response.body)).read
else
body response.body
forwarded_headers = {}
response.each_header do |key, value|
next if skip_header?(key)

forwarded_headers[key] = value
end
# RESPONSE HEADERS
headers forwarded_headers

# RESPONSE BODY
body response.body
end
end
end
Expand Down
16 changes: 15 additions & 1 deletion spec/proxy_server_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@ def expect_json_body(k, v)
before(:each) do
stub_request(:get, "https://jsonplaceholder.typicode.com/posts")
.with(headers: {"x-foo": "bar"})
.to_return(status: 200, body: "", headers: {})
.to_return(
status: 200,
body: "",
headers: {
"X-Target-Resp-Header": "Custom",
"Set-Cookie": "session=remove",
"Transfer-Encoding": "chunked"
}
)
end

context "preflight request" do
Expand Down Expand Up @@ -96,6 +104,12 @@ def expect_json_body(k, v)
expect(last_response.status).to eq(200)
end

it "fowards most headers but skips cookies and transfer encoding" do
expect(last_response["X-Target-Resp-Header"]).to eq("Custom")
expect(last_response["Set-Cookie"]).to be_nil
expect(last_response["Transfer-Encoding"]).to be_nil
end

context "when header name is changed via configuration" do
before(:each) do
stub_const('ProxyServer::TOKEN_HEADER', "x-custom-proxy")
Expand Down

0 comments on commit 075220a

Please sign in to comment.