Skip to content

Commit 569223c

Browse files
authored
feat: add proxy_headers to configuration options (#301)
1 parent cf21862 commit 569223c

File tree

8 files changed

+128
-7
lines changed

8 files changed

+128
-7
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
### Features
44
1. [#281](https://github.com/influxdata/influxdb-client-python/pull/281): `FluxTable`, `FluxColumn` and `FluxRecord` objects have helpful reprs
55
1. [#293](https://github.com/influxdata/influxdb-client-python/pull/293): `dataframe_serializer` supports batching
6+
1. [#301](https://github.com/influxdata/influxdb-client-python/pull/301): Add `proxy_headers` to configuration options
7+
8+
### Documentation
9+
1. [#301](https://github.com/influxdata/influxdb-client-python/pull/301): How to configure proxy
610

711
### Bug Fixes
812
1. [#283](https://github.com/influxdata/influxdb-client-python/pull/283): Set proxy server in config file

README.rst

+36
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ InfluxDB 2.0 client features
7676
- `How to use Jupyter + Pandas + InfluxDB 2`_
7777
- Advanced Usage
7878
- `Gzip support`_
79+
- `Proxy configuration`_
7980
- `Delete data`_
8081

8182
Installation
@@ -1059,6 +1060,41 @@ Gzip support
10591060
10601061
.. marker-gzip-end
10611062
1063+
Proxy configuration
1064+
^^^^^^^^^^^^^^^^^^^
1065+
.. marker-proxy-start
1066+
1067+
You can configure the client to tunnel requests through an HTTP proxy.
1068+
The following proxy options are supported:
1069+
1070+
- ``proxy`` - Set this to configure the http proxy to be used, ex. ``http://localhost:3128``
1071+
- ``proxy_headers`` - A dictionary containing headers that will be sent to the proxy. Could be used for proxy authentication.
1072+
1073+
.. code-block:: python
1074+
1075+
from influxdb_client import InfluxDBClient
1076+
1077+
with InfluxDBClient(url="http://localhost:8086",
1078+
token="my-token",
1079+
org="my-org",
1080+
proxy="http://localhost:3128") as client:
1081+
1082+
.. note::
1083+
1084+
If your proxy notify the client with permanent redirect (``HTTP 301``) to **different host**.
1085+
The client removes ``Authorization`` header, because otherwise the contents of ``Authorization`` is sent to third parties
1086+
which is a security vulnerability.
1087+
1088+
You can change this behaviour by:
1089+
1090+
.. code-block:: python
1091+
1092+
from urllib3 import Retry
1093+
Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset()
1094+
Retry.DEFAULT.remove_headers_on_redirect = Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT
1095+
1096+
.. marker-proxy-end
1097+
10621098
Delete data
10631099
^^^^^^^^^^^
10641100
.. marker-delete-start

docs/usage.rst

+12-6
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ Query
1010
:start-after: marker-query-start
1111
:end-before: marker-query-end
1212

13-
Pandas DataFrame
14-
^^^^^^^^^^^^^^^^
15-
.. include:: ../README.rst
16-
:start-after: marker-pandas-start
17-
:end-before: marker-pandas-end
18-
1913
Write
2014
^^^^^
2115
.. include:: ../README.rst
2216
:start-after: marker-writes-start
2317
:end-before: marker-writes-end
2418

19+
Pandas DataFrame
20+
^^^^^^^^^^^^^^^^
21+
.. include:: ../README.rst
22+
:start-after: marker-pandas-start
23+
:end-before: marker-pandas-end
24+
2525
Delete data
2626
^^^^^^^^^^^
2727
.. include:: ../README.rst
@@ -34,6 +34,12 @@ Gzip support
3434
:start-after: marker-gzip-start
3535
:end-before: marker-gzip-end
3636

37+
Proxy configuration
38+
^^^^^^^^^^^^^^^^^^^
39+
.. include:: ../README.rst
40+
:start-after: marker-proxy-start
41+
:end-before: marker-proxy-end
42+
3743
Debugging
3844
^^^^^^^^^
3945

influxdb_client/client/influxdb_client.py

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ def __init__(self, url, token, debug=None, timeout=10_000, enable_gzip=False, or
3838
:key bool verify_ssl: Set this to false to skip verifying SSL certificate when calling API from https server.
3939
:key str ssl_ca_cert: Set this to customize the certificate file to verify the peer.
4040
:key str proxy: Set this to configure the http proxy to be used (ex. http://localhost:3128)
41+
:key str proxy_headers: A dictionary containing headers that will be sent to the proxy. Could be used for proxy
42+
authentication.
4143
:key int connection_pool_maxsize: Number of connections to save that can be reused by urllib3.
4244
Defaults to "multiprocessing.cpu_count() * 5".
4345
:key urllib3.util.retry.Retry retries: Set the default retry strategy that is used for all HTTP requests
@@ -63,6 +65,7 @@ def __init__(self, url, token, debug=None, timeout=10_000, enable_gzip=False, or
6365
conf.verify_ssl = kwargs.get('verify_ssl', True)
6466
conf.ssl_ca_cert = kwargs.get('ssl_ca_cert', None)
6567
conf.proxy = kwargs.get('proxy', None)
68+
conf.proxy_headers = kwargs.get('proxy_headers', None)
6669
conf.connection_pool_maxsize = kwargs.get('connection_pool_maxsize', conf.connection_pool_maxsize)
6770
conf.timeout = timeout
6871

influxdb_client/configuration.py

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def __init__(self):
112112

113113
# Proxy URL
114114
self.proxy = None
115+
# A dictionary containing headers that will be sent to the proxy
116+
self.proxy_headers = None
115117
# Safe chars for path_param
116118
self.safe_chars_for_path_param = ''
117119

influxdb_client/rest.py

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ def __init__(self, configuration, pools_size=4, maxsize=None, retries=False):
107107
cert_file=configuration.cert_file,
108108
key_file=configuration.key_file,
109109
proxy_url=configuration.proxy,
110+
proxy_headers=configuration.proxy_headers,
110111
**addition_pool_args
111112
)
112113
else:

tests/test_InfluxDBClient.py

+46-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import unittest
66

77
from influxdb_client import InfluxDBClient, Point
8-
from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions, WriteType
8+
from influxdb_client.client.write_api import WriteOptions, WriteType
9+
10+
from tests.base_test import BaseTest
911

1012

1113
class InfluxDBClientTest(unittest.TestCase):
@@ -167,6 +169,49 @@ def test_write_context_manager(self):
167169
self.assertIsNone(api_client._pool)
168170
self.assertIsNone(self.client.api_client)
169171

172+
173+
class InfluxDBClientTestIT(BaseTest):
174+
httpRequest = []
175+
176+
def tearDown(self) -> None:
177+
super(InfluxDBClientTestIT, self).tearDown()
178+
if hasattr(self, 'httpd'):
179+
self.httpd.shutdown()
180+
if hasattr(self, 'httpd_thread'):
181+
self.httpd_thread.join()
182+
InfluxDBClientTestIT.httpRequest = []
183+
184+
def test_proxy(self):
185+
self._start_proxy_server()
186+
187+
self.client.close()
188+
self.client = InfluxDBClient(url=self.host,
189+
token=self.auth_token,
190+
proxy=f"http://localhost:{self.httpd.server_address[1]}",
191+
proxy_headers={'ProxyHeader': 'Val'})
192+
ready = self.client.ready()
193+
self.assertEqual(ready.status, "ready")
194+
self.assertEqual(1, len(InfluxDBClientTestIT.httpRequest))
195+
self.assertEqual('Val', InfluxDBClientTestIT.httpRequest[0].headers.get('ProxyHeader'))
196+
197+
def _start_proxy_server(self):
198+
import http.server
199+
import urllib.request
200+
201+
class ProxyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
202+
203+
def do_GET(self):
204+
InfluxDBClientTestIT.httpRequest.append(self)
205+
self.send_response(200)
206+
self.send_header('Content-type', 'application/json')
207+
self.end_headers()
208+
self.copyfile(urllib.request.urlopen(self.path), self.wfile)
209+
210+
self.httpd = http.server.HTTPServer(('localhost', 0), ProxyHTTPRequestHandler)
211+
self.httpd_thread = threading.Thread(target=self.httpd.serve_forever)
212+
self.httpd_thread.start()
213+
214+
170215
class ServerWithSelfSingedSSL(http.server.SimpleHTTPRequestHandler):
171216
def _set_headers(self):
172217
self.send_response(200)

tests/test_WriteApi.py

+24
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,30 @@ def test_writes_default_tags_dict_without_tag(self):
515515
self.assertEqual(1, len(requests))
516516
self.assertEqual("h2o,customer=California\\ Miner,id=132-987-655 level=1 1", requests[0].parsed_body)
517517

518+
def test_redirect(self):
519+
from urllib3 import Retry
520+
Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset()
521+
Retry.DEFAULT.remove_headers_on_redirect = Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT
522+
self.influxdb_client.close()
523+
524+
self.influxdb_client = InfluxDBClient(url="http://localhost", token="my-token", org="my-org")
525+
526+
httpretty.register_uri(httpretty.POST, uri="http://localhost2/api/v2/write", status=204)
527+
httpretty.register_uri(httpretty.POST, uri="http://localhost/api/v2/write", status=301,
528+
adding_headers={'Location': 'http://localhost2/api/v2/write'})
529+
530+
self.write_client = self.influxdb_client.write_api(write_options=SYNCHRONOUS)
531+
532+
self.write_client.write("my-bucket", "my-org", {"measurement": "h2o", "fields": {"level": 1.0}, "time": 1})
533+
534+
requests = httpretty.httpretty.latest_requests
535+
self.assertEqual(2, len(requests))
536+
self.assertEqual('Token my-token', requests[0].headers['Authorization'])
537+
self.assertEqual('Token my-token', requests[1].headers['Authorization'])
538+
539+
from urllib3 import Retry
540+
Retry.DEFAULT.remove_headers_on_redirect = Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT
541+
518542

519543
class AsynchronousWriteTest(BaseTest):
520544

0 commit comments

Comments
 (0)