Skip to content

Commit 6a32a57

Browse files
Merge pull request #9 from thewebscraping/bugs/fix-proxy
bugs: fix proxy
2 parents 4282b5f + 9923366 commit 6a32a57

File tree

9 files changed

+302
-139
lines changed

9 files changed

+302
-139
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,15 @@ Explore the full capabilities of TLS Requests in the documentation:
110110

111111

112112
Read the documentation: [**thewebscraping.github.io/tls-requests/**](https://thewebscraping.github.io/tls-requests/)
113+
114+
**Report Issues**
115+
-----------------
116+
117+
Found a bug? Please [open an issue](https://github.com/thewebscraping/tls-requests/issues/).
118+
119+
By reporting an issue you help improve the project.
120+
121+
**Credits**
122+
-----------------
123+
124+
Special thanks to [bogdanfinn](https://github.com/bogdanfinn/) for creating the awesome [tls-client](https://github.com/bogdanfinn/tls-client).

tests/test_proxy.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import tls_requests
2+
3+
4+
def test_http_proxy():
5+
proxy = tls_requests.Proxy("http://localhost:8080")
6+
assert proxy.scheme == "http"
7+
assert proxy.host == "localhost"
8+
assert proxy.port == '8080'
9+
assert proxy.url == "http://localhost:8080"
10+
11+
12+
def test_https_proxy():
13+
proxy = tls_requests.Proxy("https://localhost:8080")
14+
assert proxy.scheme == "https"
15+
assert proxy.host == "localhost"
16+
assert proxy.port == '8080'
17+
assert proxy.url == "https://localhost:8080"
18+
19+
20+
def test_socks5_proxy():
21+
proxy = tls_requests.Proxy("socks5://localhost:8080")
22+
assert proxy.scheme == "socks5"
23+
assert proxy.host == "localhost"
24+
assert proxy.port == '8080'
25+
assert proxy.url == "socks5://localhost:8080"
26+
27+
28+
def test_proxy_with_params():
29+
proxy = tls_requests.Proxy("http://localhost:8080?a=b", params={"foo": "bar"})
30+
assert proxy.scheme == "http"
31+
assert proxy.host == "localhost"
32+
assert proxy.port == '8080'
33+
assert proxy.url == "http://localhost:8080"
34+
35+
36+
def test_auth_proxy():
37+
proxy = tls_requests.Proxy("http://username:password@localhost:8080")
38+
assert proxy.scheme == "http"
39+
assert proxy.host == "localhost"
40+
assert proxy.port == '8080'
41+
assert proxy.auth == ("username", "password")
42+
assert proxy.url == "http://username:password@localhost:8080"
43+
44+
45+
def test_unsupported_proxy_scheme():
46+
try:
47+
_ = tls_requests.Proxy("unknown://localhost:8080")
48+
except Exception as e:
49+
assert isinstance(e, tls_requests.exceptions.ProxyError)

tests/test_redirects.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def test_missing_host_redirects(httpserver: HTTPServer):
1010
httpserver.expect_request("/redirects/ok").respond_with_data(b"OK")
1111
response = tls_requests.get(httpserver.url_for("/redirects/3"))
1212
assert response.status_code == 200
13+
assert response.history[0].status_code == 302
1314
assert len(response.history) == 3
1415

1516

@@ -20,9 +21,20 @@ def test_full_path_redirects(httpserver: HTTPServer):
2021
httpserver.expect_request("/redirects/ok").respond_with_data(b"OK")
2122
response = tls_requests.get(httpserver.url_for("/redirects/3"))
2223
assert response.status_code == 200
24+
assert response.history[0].status_code == 302
2325
assert len(response.history) == 3
2426

2527

28+
def test_fragment_redirects(httpserver: HTTPServer):
29+
httpserver.expect_request("/redirects/3").respond_with_data(b"OK", status=302, headers={"Location": httpserver.url_for("/redirects/ok#fragment")})
30+
httpserver.expect_request("/redirects/ok").respond_with_data(b"OK")
31+
response = tls_requests.get(httpserver.url_for("/redirects/3"))
32+
assert response.status_code == 200
33+
assert response.history[0].status_code == 302
34+
assert len(response.history) == 1
35+
assert response.request.url.fragment == "fragment"
36+
37+
2638
def test_too_many_redirects(httpserver: HTTPServer):
2739
httpserver.expect_request("/redirects/3").respond_with_data(b"OK", status=302, headers={"Location": "/redirects/1"})
2840
httpserver.expect_request("/redirects/1").respond_with_data(b"OK", status=302, headers={"Location": "/redirects/2"})
File renamed without changes.

tls_requests/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
__url__ = "https://github.com/thewebscraping/tls-requests"
44
__author__ = "Tu Pham"
55
__author_email__ = "thetwofarm@gmail.com"
6-
__version__ = "1.0.6"
6+
__version__ = "1.0.7"
77
__license__ = "MIT"

tls_requests/client.py

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import (Any, Callable, Literal, Mapping, Optional, Sequence,
99
TypeVar, Union)
1010

11-
from .exceptions import RemoteProtocolError, TooManyRedirects
11+
from .exceptions import ProxyError, RemoteProtocolError, TooManyRedirects
1212
from .models import (URL, Auth, BasicAuth, Cookies, Headers, Proxy, Request,
1313
Response, StatusCodes, TLSClient, TLSConfig, URLParams)
1414
from .settings import (DEFAULT_FOLLOW_REDIRECTS, DEFAULT_HEADERS,
@@ -105,7 +105,7 @@ def __init__(
105105
self._headers = Headers(headers)
106106
self._hooks = hooks if isinstance(hooks, dict) else {}
107107
self.auth = auth
108-
self.proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy
108+
self.proxy = self.prepare_proxy(proxy)
109109
self.timeout = timeout
110110
self.follow_redirects = follow_redirects
111111
self.max_redirects = max_redirects
@@ -194,27 +194,23 @@ def prepare_params(self, params: URLParamTypes = None) -> URLParams:
194194
merged_params = self.params.copy()
195195
return merged_params.update(params)
196196

197+
def prepare_proxy(self, proxy: ProxyTypes = None) -> Optional[Proxy]:
198+
if proxy is not None:
199+
if isinstance(proxy, (bytes, str, URL, Proxy)):
200+
return Proxy(proxy)
201+
202+
raise ProxyError("Invalid proxy.")
203+
197204
def prepare_config(self, request: Request):
198205
"""Prepare TLS Config"""
199206

200-
proxy = None
201-
if self.proxy and isinstance(self.proxy, Proxy):
202-
proxy = self.proxy.url
203-
if self.proxy.auth:
204-
proxy = "%s://%s@%s:%s" % (
205-
self.proxy.url.scheme,
206-
":".join(self.proxy.auth),
207-
self.proxy.url.host,
208-
self.proxy.url.port,
209-
)
210-
211207
config = self.config.copy_with(
212208
method=request.method,
213-
url=str(request.url),
209+
url=request.url,
214210
body=request.read(),
215211
headers=dict(request.headers),
216212
cookies=[dict(name=k, value=v) for k, v in request.cookies.items()],
217-
proxy=proxy,
213+
proxy=request.proxy.url if request.proxy else None,
218214
timeout=request.timeout,
219215
http2=True if self.http2 in ["auto", "http2", True, None] else False,
220216
verify=self.verify,
@@ -249,6 +245,7 @@ def build_request(
249245
params=self.prepare_params(params),
250246
headers=self.prepare_headers(headers),
251247
cookies=self.prepare_cookies(cookies),
248+
proxy=self.proxy,
252249
timeout=timeout or self.timeout,
253250
)
254251

@@ -313,15 +310,14 @@ def _rebuild_redirect_url(self, request: Request, response: Response) -> URL:
313310
except KeyError:
314311
raise RemoteProtocolError("Invalid URL in Location headers: %s" % e)
315312

316-
for missing_field in ["scheme", "host", "port", "fragment"]:
317-
private_field = "_%s" % missing_field
318-
if not getattr(url, private_field, None):
319-
setattr(url, private_field, getattr(request.url, private_field, ""))
313+
if not url.netloc:
314+
for missing_field in ["scheme", "host", "port"]:
315+
setattr(url, missing_field, getattr(request.url, missing_field, ""))
320316

321317
# TLS error transport between HTTP/1.x -> HTTP/2
322318
if url.scheme != request.url.scheme:
323319
if request.url.scheme == "http":
324-
url._scheme = request.url.scheme
320+
url.scheme = request.url.scheme
325321
else:
326322
if self.http2 in ["auto", None]:
327323
self.session.destroy_session(self.config.sessionId)
@@ -331,6 +327,7 @@ def _rebuild_redirect_url(self, request: Request, response: Response) -> URL:
331327
"Switching remote scheme from HTTP/2 to HTTP/1 is not supported. Please initialize Client with parameter `http2` to `auto`."
332328
)
333329

330+
setattr(url, "_url", None) # reset url
334331
if not url.url:
335332
raise RemoteProtocolError("Invalid URL in Location headers: %s" % e)
336333

tls_requests/models/request.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from tls_requests.models.cookies import Cookies
44
from tls_requests.models.encoders import StreamEncoder
55
from tls_requests.models.headers import Headers
6-
from tls_requests.models.urls import URL
6+
from tls_requests.models.urls import URL, Proxy
77
from tls_requests.settings import DEFAULT_TIMEOUT
88
from tls_requests.types import (CookieTypes, HeaderTypes, MethodTypes,
9-
RequestData, RequestFiles, TimeoutTypes,
10-
URLParamTypes, URLTypes)
9+
ProxyTypes, RequestData, RequestFiles,
10+
TimeoutTypes, URLParamTypes, URLTypes)
1111

1212
__all__ = ["Request"]
1313

@@ -24,13 +24,15 @@ def __init__(
2424
params: URLParamTypes = None,
2525
headers: HeaderTypes = None,
2626
cookies: CookieTypes = None,
27+
proxy: ProxyTypes = None,
2728
timeout: TimeoutTypes = None,
2829
) -> None:
2930
self._content = None
3031
self._session_id = None
3132
self.url = URL(url, params=params)
3233
self.method = method.upper()
3334
self.cookies = Cookies(cookies)
35+
self.proxy = Proxy(proxy) if proxy else None
3436
self.timeout = timeout if isinstance(timeout, (float, int)) else DEFAULT_TIMEOUT
3537
self.stream = StreamEncoder(data, files, json)
3638
self.headers = self._prepare_headers(headers)

0 commit comments

Comments
 (0)