Repository: devolo/ssllabs (language: python)

Issue:
Add params to use proxy
Hi,
Is possible to add method to make on request with proxy setting? (and add it on Readme)
Thanks, Kaelatt 




Discussion / hints:
Hi @Kaelatt , depending on how you are using this lib, I guess it is already possible. Could you tell me, if you rely on high level usage or the low level usage?
I use the librairy for check my domains and subdomains so yes it's for high level usage but i think the api can support it.

If you have a idea to make a request with proxy "proxy.com:80" can you tell me how to make it please.

Thanks a lot for your information @Shutgun 
So, on the low level usage you can specify your own httpx.Asynclient with configured proxy (https://www.python-httpx.org/advanced/#http-proxying) and pass it with the argument client. 

See https://github.com/devolo/ssllabs/blob/246b948ec936bdcb17bdb10e44a0a05359ea6125/ssllabs/api/_api.py#L14

However, the high level usage does not support that yet. I would have to add that.
Danke Schön für alle 

I try the solution and i make a feedback 👍

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d1732b1..37a078b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,14 +1,14 @@
 repos:
 -   repo: https://github.com/psf/black
-    rev: '22.3.0'
+    rev: '23.1.0'
     hooks:
     - id: black
 -   repo: https://github.com/pycqa/isort
-    rev: '5.7.0'
+    rev: '5.12.0'
     hooks:
         - id: isort
 -   repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: 'v3.4.0'
+    rev: 'v4.4.0'
     hooks:
         - id: end-of-file-fixer
         - id: trailing-whitespace
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6c3879..eec248d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [v1.1.0] - 2022/02/16
+
+### Added
+
+- Use own HTTP client, if special settings are needed
+
 ## [v1.0.2] - 2022/12/09
 
 ### Fixed
diff --git a/README.md b/README.md
index 16ff951..f5d5ace 100644
--- a/README.md
+++ b/README.md
@@ -138,6 +138,8 @@ async def get_grade():
     api = Endpoint()
     endpoint = await api.get(host="devolo.de", s="195.201.179.93")
     return endpoint.grade
+
+asyncio.run(get_grade())
 ```
 
 Classes are called like the [API call](https://github.com/ssllabs/ssllabs-scan/blob/master/ssllabs-api-docs-v3.md#protocol-calls) without the leading get. The get method will query the API. It will take the parameters like in the documentation and return a dataclass representing the object, the API describes. One exception in the naming: the getEndpointData call is implemented in the Endpoint class to be able to better distinguish it from its EndpointData result object.
@@ -145,3 +147,20 @@ Classes are called like the [API call](https://github.com/ssllabs/ssllabs-scan/b
 ## Exceptions
 
 Three types of exceptions might hit you, if the connection to SSL Labs' API is affected: ```httpx.ConnectTimeout``` or ```httpx.ReadTimeout``` appear, if the servers are down, and ```httpx.HTTPStatusError``` appears, if there is a client or server [error response](https://github.com/ssllabs/ssllabs-scan/blob/master/ssllabs-api-docs-v3.md#error-response-status-codes). In this cases, you are asked to wait 15 to 30 minutes before you try again.
+
+## Using an own HTTP client
+
+If you have special needs (e.g. what to use a proxy server), you can create an own HTTP client. Please read the [httpx documentation](https://www.python-httpx.org/advanced) to find out which possibilities you have.
+
+```python
+import asyncio
+
+from httpx import AsyncClient
+from ssllabs import Ssllabs
+
+async def analyze():
+    async with AsyncClient(proxies="http://localhost:8030") as client:
+        ssllabs = Ssllabs(client)
+        return await ssllabs.analyze(host="devolo.de")
+
+asyncio.run(analyze())
diff --git a/ssllabs/ssllabs.py b/ssllabs/ssllabs.py
index 412523f..3a6b347 100644
--- a/ssllabs/ssllabs.py
+++ b/ssllabs/ssllabs.py
@@ -1,7 +1,8 @@
 import asyncio
 import logging
+from typing import Optional
 
-from httpx import ConnectTimeout, HTTPStatusError, ReadTimeout
+from httpx import AsyncClient, ConnectTimeout, HTTPStatusError, ReadTimeout
 
 from .api import Analyze, Info, RootCertsRaw, StatusCodes
 from .data.host import HostData
@@ -12,7 +13,8 @@
 class Ssllabs:
     """Highlevel methods to interact with the SSL Labs Assessment APIs."""
 
-    def __init__(self):
+    def __init__(self, client: Optional[AsyncClient] = None):
+        self._client = client
         self._logger = logging.getLogger("ssllabs.Ssllabs")
         self._semaphore = asyncio.Semaphore(1)
 
@@ -22,7 +24,7 @@ async def availability(self) -> bool:
 
         See also: https://github.com/ssllabs/ssllabs-scan/blob/master/ssllabs-api-docs-v3.md#error-response-status-codes
         """
-        i = Info()
+        i = Info(self._client)
         try:
             await i.get()
             self._logger.info("SSL Labs servers are up an running.")
@@ -43,7 +45,7 @@ async def analyze(self, host: str, publish: bool = False, ignore_mismatch: bool
         """
         await self._semaphore.acquire()
         self._logger.info("Analyzing %s", host)
-        i = Info()
+        i = Info(self._client)
         info = await i.get()
 
         # Wait for a free slot, if all slots are in use
@@ -56,7 +58,7 @@ async def analyze(self, host: str, publish: bool = False, ignore_mismatch: bool
         if info.currentAssessments != 0:
             await asyncio.sleep(info.newAssessmentCoolOff / 1000)
 
-        a = Analyze()
+        a = Analyze(self._client)
         host_object = await a.get(
             host=host, startNew="on", publish="on" if publish else "off", ignoreMismatch="on" if ignore_mismatch else "off"
         )
@@ -73,7 +75,7 @@ async def info(self) -> InfoData:
 
         See also: https://github.com/ssllabs/ssllabs-scan/blob/master/ssllabs-api-docs-v3.md#info
         """
-        i = Info()
+        i = Info(self._client)
         return await i.get()
 
     async def root_certs(self, trust_store: int = 1) -> str:
@@ -89,7 +91,7 @@ async def root_certs(self, trust_store: int = 1) -> str:
                 """Trust store not found. Please choose on of the following:
             1-Mozilla, 2-Apple MacOS, 3-Android, 4-Java, 5-Windows"""
             )
-        rcr = RootCertsRaw()
+        rcr = RootCertsRaw(self._client)
         return await rcr.get(trustStore=trust_store)
 
     async def status_codes(self) -> StatusCodesData:
@@ -98,5 +100,5 @@ async def status_codes(self) -> StatusCodesData:
 
         See also: https://github.com/ssllabs/ssllabs-scan/blob/master/ssllabs-api-docs-v3.md#retrieve-known-status-codes
         """
-        sc = StatusCodes()
+        sc = StatusCodes(self._client)
         return await sc.get()


Test patch:
diff --git a/tests/test_api.py b/tests/test_api.py
index 8a88006..dee09d3 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -18,7 +18,6 @@
 
 
 class TestApi:
-
     API_CALLS = [
         (Endpoint, EndpointData, {"host": "devolo.de", "s": "195.201.179.93"}),
         (StatusCodes, StatusCodesData, {}),
diff --git a/tests/test_ssllabs.py b/tests/test_ssllabs.py
index 8e4b5a8..0a4a54f 100644
--- a/tests/test_ssllabs.py
+++ b/tests/test_ssllabs.py
@@ -19,7 +19,6 @@
 
 
 class TestSsllabs:
-
     API_CALLS: list = [("info.Info", InfoData, {}), ("status_codes.StatusCodes", StatusCodesData, {})]
 
     @pytest.mark.asyncio