Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 191
WIP: SOCKS proxy support#51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Uh oh!
There was an error while loading. Please reload this page.
Closed
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
058c863
Add SOCKS proxy support
9d77f7a
Type annotations
9a8fb53
Add types to connection
9aab79f
Add some comments
f262577
Typing fixes and cleanup
f65a8ac
Use shared types in http_proxy
0effade
Support HTTPS
9119abe
Raise ProxyError instead of generic Exception
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -36,5 +36,7 @@ | ||
| "ReadError", | ||
| "WriteError", | ||
| "CloseError", | ||
| "ProtocolError", | ||
| "ProxyError", | ||
| ] | ||
| __version__ = "0.7.0" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,13 @@ | ||
| from enum import Enum | ||
| from ssl import SSLContext | ||
| from typing import Dict, List, Optional, Tuple | ||
| from typing import Tuple | ||
| from .._exceptions import ProxyError | ||
| from .base import AsyncByteStream, AsyncHTTPTransport | ||
| from .connection import AsyncHTTPConnection | ||
| from .._types import URL, Headers, Origin, TimeoutDict | ||
| from .base import AsyncByteStream | ||
| from .connection import AsyncHTTPConnection, AsyncSOCKSConnection | ||
| from .connection_pool import AsyncConnectionPool, ResponseByteStream | ||
| Origin = Tuple[bytes, bytes, int] | ||
| URL = Tuple[bytes, bytes, int, bytes] | ||
| Headers = List[Tuple[bytes, bytes]] | ||
| TimeoutDict = Dict[str, Optional[float]] | ||
| async def read_body(stream: AsyncByteStream) -> bytes: | ||
| try: | ||
| @@ -19,18 +16,33 @@ async def read_body(stream: AsyncByteStream) -> bytes: | ||
| await stream.aclose() | ||
| class ProxyModes(Enum): | ||
| DEFAULT = "DEFAULT" | ||
| FORWARD_ONLY = "FORWARD_ONLY" | ||
| TUNNEL_ONLY = "TUNNEL_ONLY" | ||
| SOCKS4 = "SOCKS4" | ||
| SOCKS4A = "SOCKS4A" | ||
| SOCKS5 = "SOCKS5" | ||
| class AsyncHTTPProxy(AsyncConnectionPool): | ||
| """ | ||
| A connection pool for making HTTP requests via an HTTP proxy. | ||
| **Parameters:** | ||
| * **proxy_origin** - `Tuple[bytes, bytes, int]` - The address of the proxy service as a 3-tuple of (scheme, host, port). | ||
| * **proxy_headers** - `Optional[List[Tuple[bytes, bytes]]]` - A list of proxy headers to include. | ||
| * **proxy_mode** - `str` - A proxy mode to operate in. May be "DEFAULT", "FORWARD_ONLY", or "TUNNEL_ONLY". | ||
| * **ssl_context** - `Optional[SSLContext]` - An SSL context to use for verifying connections. | ||
| * **max_connections** - `Optional[int]` - The maximum number of concurrent connections to allow. | ||
| * **max_keepalive** - `Optional[int]` - The maximum number of connections to allow before closing keep-alive connections. | ||
| * **proxy_origin** - `Tuple[bytes, bytes, int]` - The address of the proxy | ||
| service as a 3-tuple of (scheme, host, port). | ||
| * **proxy_headers** - `Optional[List[Tuple[bytes, bytes]]]` - A list of | ||
| proxy headers to include. | ||
| * **proxy_mode** - `str` - A proxy mode to operate in. One of "DEFAULT", | ||
| "FORWARD_ONLY", or "TUNNEL_ONLY". | ||
| * **ssl_context** - `Optional[SSLContext]` - An SSL context to use for | ||
| verifying connections. | ||
| * **max_connections** - `Optional[int]` - The maximum number of concurrent | ||
| connections to allow. | ||
| * **max_keepalive** - `Optional[int]` - The maximum number of connections | ||
| to allow before closing keep-alive connections. | ||
| * **http2** - `bool` - Enable HTTP/2 support. | ||
| """ | ||
| @@ -45,7 +57,7 @@ def __init__( | ||
| keepalive_expiry: float = None, | ||
| http2: bool = False, | ||
| ): | ||
| assert proxy_mode in ("DEFAULT", "FORWARD_ONLY", "TUNNEL_ONLY") | ||
| assert ProxyModes(proxy_mode) # TODO: use ProxyModes type of argument | ||
yeraydiazdiaz marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| self.proxy_origin = proxy_origin | ||
| self.proxy_headers = [] if proxy_headers is None else proxy_headers | ||
| @@ -76,6 +88,14 @@ async def request( | ||
| return await self._forward_request( | ||
| method, url, headers=headers, stream=stream, timeout=timeout | ||
| ) | ||
| elif self.proxy_mode == "SOCKS4": | ||
| return await self._socks4_request( | ||
| method, url, headers=headers, stream=stream, timeout=timeout | ||
| ) | ||
| elif self.proxy_mode == "SOCKS4A": | ||
| raise NotImplementedError | ||
| elif self.proxy_mode == "SOCKS5": | ||
| raise NotImplementedError | ||
| else: | ||
| # By default HTTPS should be tunnelled. | ||
| return await self._tunnel_request( | ||
| @@ -184,3 +204,36 @@ async def _tunnel_request( | ||
| response[4], connection=connection, callback=self._response_closed | ||
| ) | ||
| return response[0], response[1], response[2], response[3], wrapped_stream | ||
| async def _socks4_request( | ||
| self, | ||
| method: bytes, | ||
| url: URL, | ||
| headers: Headers = None, | ||
| stream: AsyncByteStream = None, | ||
| timeout: TimeoutDict = None, | ||
| ) -> Tuple[bytes, int, bytes, Headers, AsyncByteStream]: | ||
| """ | ||
| SOCKS4 requires negotiation with the proxy. | ||
| """ | ||
| origin = url[:3] | ||
| connection = await self._get_connection_from_pool(origin) | ||
| if connection is None: | ||
| connection = AsyncSOCKSConnection(origin, self.proxy_origin, "SOCKS4") | ||
| async with self._thread_lock: | ||
| self._connections.setdefault(origin, set()) | ||
| self._connections[origin].add(connection) | ||
| # Issue a forwarded proxy request... | ||
| # GET https://www.example.org/path HTTP/1.1 | ||
yeraydiazdiaz marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| # [proxy headers] | ||
| # [headers] | ||
| response = await connection.request( | ||
| method, url, headers=headers, stream=stream, timeout=timeout | ||
| ) | ||
| wrapped_stream = ResponseByteStream( | ||
| response[4], connection=connection, callback=self._response_closed | ||
| ) | ||
| return response[0], response[1], response[2], response[3], wrapped_stream | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is required by SOCKS4.
SOCKS5 will require username/password and likely defining acceptable authentication methods. Which makes me think we might want separate
AsyncSOCKS4ConnectionandAsyncSOCKS5Connection.