Skip to content

Commit 196a407

Browse files
committed
feat: allow gzip compression on request bodies
1 parent 27a6e7e commit 196a407

File tree

3 files changed

+73
-1
lines changed

3 files changed

+73
-1
lines changed

ibm_cloud_sdk_core/base_service.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from os.path import basename
2121
import platform
2222
import sys
23+
import zlib
2324
from typing import Dict, List, Optional, Tuple, Union
2425

2526
import requests
@@ -41,6 +42,8 @@
4142
# import http.client as http_client
4243
# http_client.HTTPConnection.debuglevel = 1
4344

45+
#pylint: disable=too-many-instance-attributes
46+
#pylint: disable=too-many-locals
4447
class BaseService:
4548
"""Common functionality shared by generated service classes.
4649
@@ -52,6 +55,7 @@ class BaseService:
5255
authenticator: Adds authentication data to service requests. Defaults to None.
5356
disable_ssl_verification: A flag that indicates whether verification of the server's SSL
5457
certificate should be disabled or not. Defaults to False.
58+
enable_gzip_compression: A flag that indicates whether to enable gzip compression on request bodies
5559
5660
Attributes:
5761
service_url (str): Url to the service endpoint.
@@ -61,6 +65,7 @@ class BaseService:
6165
default_headers (dict): A dictionary of headers to be sent with every HTTP request to the service endpoint.
6266
jar (http.cookiejar.CookieJar): Stores cookies received from the service.
6367
http_config (dict): A dictionary containing values that control the timeout, proxies, and etc of HTTP requests.
68+
enable_gzip_compression (bool): A flag that indicates whether to enable gzip compression on request bodies
6469
Raises:
6570
ValueError: If Authenticator is not provided or invalid type.
6671
"""
@@ -74,13 +79,15 @@ def __init__(self,
7479
*,
7580
service_url: str = None,
7681
authenticator: Authenticator = None,
77-
disable_ssl_verification: bool = False) -> None:
82+
disable_ssl_verification: bool = False,
83+
enable_gzip_compression: bool = False) -> None:
7884
self.set_service_url(service_url)
7985
self.http_config = {}
8086
self.jar = CookieJar()
8187
self.authenticator = authenticator
8288
self.disable_ssl_verification = disable_ssl_verification
8389
self.default_headers = None
90+
self.enable_gzip_compression = enable_gzip_compression
8491
self._set_user_agent_header(self._build_user_agent())
8592
if not self.authenticator:
8693
raise ValueError('authenticator must be provided')
@@ -124,6 +131,8 @@ def configure_service(self, service_name: str) -> None:
124131
self.set_disable_ssl_verification(
125132
bool(config.get('DISABLE_SSL'))
126133
)
134+
if config.get('ENABLE_GZIP') is not None:
135+
self.set_enable_gzip_compression(config.get('ENABLE_GZIP') == 'True')
127136

128137
def _set_user_agent_header(self, user_agent_string: str) -> None:
129138
self.user_agent_header = {'User-Agent': user_agent_string}
@@ -245,6 +254,14 @@ def send(self, request: requests.Request, **kwargs) -> DetailedResponse:
245254
logging.exception('Error in service call')
246255
raise
247256

257+
def set_enable_gzip_compression(self, should_enable_compression: bool = False) -> None:
258+
"""Set value to enable gzip compression on request bodies"""
259+
self.enable_gzip_compression = should_enable_compression
260+
261+
def get_enable_gzip_compression(self) -> bool:
262+
"""Get value for enabling gzip compression on request bodies"""
263+
return self.enable_gzip_compression
264+
248265
def prepare_request(self,
249266
method: str,
250267
url: str,
@@ -309,6 +326,16 @@ def prepare_request(self,
309326

310327
self.authenticator.authenticate(request)
311328

329+
# Compress the request body if applicable
330+
if (self.get_enable_gzip_compression() and
331+
'content-encoding' not in headers and
332+
request['data'] is not None):
333+
headers['content-encoding'] = 'gzip'
334+
uncompressed_data = request['data']
335+
request_body = zlib.compress(uncompressed_data)
336+
request['data'] = request_body
337+
request['headers'] = headers
338+
312339
# Next, we need to process the 'files' argument to try to fill in
313340
# any missing filenames where possible.
314341
# 'files' can be a dictionary (i.e { '<part-name>': (<tuple>)} )

resources/ibm-credentials-gzip.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
INCLUDE_EXTERNAL_CONFIG_APIKEY=mockkey
2+
INCLUDE_EXTERNAL_CONFIG_AUTH_TYPE=iam
3+
INCLUDE_EXTERNAL_CONFIG_URL=https://mockurl
4+
INCLUDE_EXTERNAL_CONFIG_ENABLE_GZIP=True

test/test_base_service.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
from shutil import copyfile
77
from typing import Optional
8+
import zlib
89
import pytest
910
import responses
1011
import requests
@@ -475,6 +476,46 @@ def test_get_authenticator():
475476
service = AnyServiceV1('2018-11-20', authenticator=auth)
476477
assert service.get_authenticator() is not None
477478

479+
def test_gzip_compression():
480+
# Should return uncompressed data when gzip is off
481+
service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator())
482+
assert not service.get_enable_gzip_compression()
483+
prepped = service.prepare_request('GET', url='', data=json.dumps({"foo": "bar"}))
484+
assert prepped['data'] == b'{"foo": "bar"}'
485+
assert prepped['headers'].get('content-encoding') != 'gzip'
486+
487+
# Should return compressed data when gzip is on
488+
service.set_enable_gzip_compression(True)
489+
assert service.get_enable_gzip_compression()
490+
prepped = service.prepare_request('GET', url='', data=json.dumps({"foo": "bar"}))
491+
assert prepped['data'] == zlib.compress(b'{"foo": "bar"}')
492+
assert prepped['headers'].get('content-encoding') == 'gzip'
493+
494+
# Should return compressed data when gzip is on for non-json data
495+
assert service.get_enable_gzip_compression()
496+
prepped = service.prepare_request('GET', url='', data=b'rawdata')
497+
assert prepped['data'] == zlib.compress(b'rawdata')
498+
assert prepped['headers'].get('content-encoding') == 'gzip'
499+
500+
# Should return uncompressed data when content-encoding is set
501+
assert service.get_enable_gzip_compression()
502+
prepped = service.prepare_request('GET', url='', headers={"content-encoding": "gzip"},
503+
data=json.dumps({"foo": "bar"}))
504+
assert prepped['data'] == b'{"foo": "bar"}'
505+
assert prepped['headers'].get('content-encoding') == 'gzip'
506+
507+
def test_gzip_compression_external():
508+
# Should set gzip compression from external config
509+
file_path = os.path.join(
510+
os.path.dirname(__file__), '../resources/ibm-credentials-gzip.env')
511+
os.environ['IBM_CREDENTIALS_FILE'] = file_path
512+
service = IncludeExternalConfigService('v1', authenticator=NoAuthAuthenticator())
513+
assert service.service_url == 'https://mockurl'
514+
assert service.get_enable_gzip_compression() is True
515+
prepped = service.prepare_request('GET', url='', data=json.dumps({"foo": "bar"}))
516+
assert prepped['data'] == zlib.compress(b'{"foo": "bar"}')
517+
assert prepped['headers'].get('content-encoding') == 'gzip'
518+
478519
@responses.activate
479520
def test_user_agent_header():
480521
service = AnyServiceV1('2018-11-20', authenticator=NoAuthAuthenticator())

0 commit comments

Comments
 (0)