Skip to content

Commit

Permalink
feat: allow payload compression in POST requests
Browse files Browse the repository at this point in the history
- only done if custom_http_headers config includes {'Content-Encoding' => 'gzip'}
- only allow gzip.
- 'Content-Encoding' header will get dropped in GET requests.
- undocumented feature.
  • Loading branch information
rarruda committed Mar 27, 2024
1 parent 6b372f6 commit a4189de
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 1 deletion.
14 changes: 13 additions & 1 deletion lib/unleash/util/http.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require 'net/http'
require 'uri'
require 'zlib'
require 'stringio'

module Unleash
module Util
Expand All @@ -8,6 +10,7 @@ def self.get(uri, etag = nil, headers_override = nil)
http = http_connection(uri)

request = Net::HTTP::Get.new(uri.request_uri, http_headers(etag, headers_override))
request.delete('Content-Encoding')

http.request(request)
end
Expand All @@ -16,7 +19,15 @@ def self.post(uri, body)
http = http_connection(uri)

request = Net::HTTP::Post.new(uri.request_uri, http_headers)
request.body = body
request_body =
if request['Content-Encoding'] == 'gzip'
request_body_writer = Zlib::GzipWriter.new(StringIO.new)
request_body_writer << body
request_body_writer.close.string
else
body
end
request.body = request_body

http.request(request)
end
Expand All @@ -32,6 +43,7 @@ def self.http_connection(uri)

# @param etag [String, nil]
# @param headers_override [Hash, nil]
# @return [Hash]
def self.http_headers(etag = nil, headers_override = nil)
Unleash.logger.debug "ETag: #{etag}" unless etag.nil?

Expand Down
104 changes: 104 additions & 0 deletions spec/unleash/client_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
require 'stringio'
require 'zlib'

RSpec.describe Unleash::Client do
after do
WebMock.reset!
Expand Down Expand Up @@ -108,6 +111,107 @@
).to have_been_made.once
end

it "The compress http header compresses post requests when initializing client" do
WebMock.stub_request(:post, "http://test-url/client/register")
.with(
headers: {
'Accept' => '*/*',
'Content-Type' => 'application/json',
'Content-Encoding' => 'gzip',
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'User-Agent' => 'Ruby',
'X-Api-Key' => '123'
}
)
.to_return(status: 200, body: "", headers: {})
WebMock.stub_request(:post, "http://test-url/client/metrics")
.with(
headers: {
'Accept' => '*/*',
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Content-Type' => 'application/json',
'Content-Encoding' => 'gzip',
'User-Agent' => 'Ruby'
}
)
.to_return(status: 200, body: "", headers: {})

simple_features = {
"version": 1,
"features": [
{
"name": "Feature.A",
"description": "Enabled toggle",
"enabled": true,
"strategies": [{ "name": "default" }]
}
]
}
WebMock.stub_request(:get, "http://test-url/client/features")
.with(
headers: {
'Accept' => '*/*',
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Content-Type' => 'application/json',
'Unleash-Appname' => 'my-test-app',
'Unleash-Instanceid' => 'rspec/test',
'User-Agent' => 'Ruby',
'X-Api-Key' => '123'
}
)
.to_return(status: 200, body: simple_features.to_json, headers: {})

unleash_client = Unleash::Client.new(
url: 'http://test-url/',
app_name: 'my-test-app',
instance_id: 'rspec/test',
custom_http_headers: { 'X-API-KEY' => '123', 'Content-Encoding' => 'gzip' }
)

expect(unleash_client).to be_a(Unleash::Client)

expect(
a_request(:post, "http://test-url/client/register")
.with(headers: {
'Content-Type': 'application/json',
'Content-Encoding': 'gzip',
'X-API-KEY': '123',
'UNLEASH-APPNAME': 'my-test-app',
'UNLEASH-INSTANCEID': 'rspec/test'
})
).to have_been_made.once

expect(
a_request(:get, "http://test-url/client/features")
.with(headers: {
'Content-Type': 'application/json',
'X-API-KEY': '123',
'UNLEASH-APPNAME': 'my-test-app',
'UNLEASH-INSTANCEID': 'rspec/test'
})
).to have_been_made.once

# Test now sending of metrics
# Sending metrics, if they have been evaluated:
unleash_client.is_enabled?("Feature.A")
unleash_client.get_variant("Feature.A")
Unleash.reporter.post
expect(
a_request(:post, "http://test-url/client/metrics")
.with(headers: {
'Content-Type': 'application/json',
'Content-Encoding': 'gzip',
'X-API-KEY': '123',
'UNLEASH-APPNAME': 'my-test-app',
'UNLEASH-INSTANCEID': 'rspec/test'
})
.with do |request|
uncompressed_request_body = Zlib::GzipReader.wrap(StringIO.new(request.body), &:read)
JSON.parse(uncompressed_request_body)['bucket']['toggles']['Feature.A']['yes'] == 2
end
).to have_been_made.once
end

it "should load/use correct variants from the unleash server" do
WebMock.stub_request(:post, "http://test-url/client/register")
.with(
Expand Down

0 comments on commit a4189de

Please sign in to comment.