Repository: encode/httpx

Issue #203: Implement SOCKS v4, v5 Proxy
Created: 2019-08-13T17:45:47Z
Related: #36

Duplicate issue #957: http proxy get ProxyError: 403 Forbidden port
Created: 2020-05-16T02:42:25Z
I found sometime when i used http proxy (convert from socks5 by polipo) i got ProxyError: 403 Forbidden port .
But when i directly use socks5 proxy by #203 (comment) all is OK.
proxy_location = "127.0.0.1:8123"

proxies = {
    "http": f"http://{proxy_location}",
    "https": f"http://{proxy_location}"
}


File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 733, in get
    722  def get(
    723      self,
    724      url: URLTypes,
    725      *,
    726      params: QueryParamTypes = None,
    727      headers: HeaderTypes = None,
    728      cookies: CookieTypes = None,
    729      auth: AuthTypes = None,
    730      allow_redirects: bool = True,
    731      timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
    732  ) -> Response:
--> 733      return self.request(
    734          "GET",
    ..................................................
     self = <httpx._client.Client object at 0x7fef70ffbd60>
     url = 'https://xxxxnetworkk:55001/h/xxxxxxxxxxx
            xxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-1500-jpg/xxxxxxxx
            p=xxxxxxxxxxxxxxxxxxxxxxxxxxx;xxxxxxxxxxxxxxxx_
            02.jpg'
     URLTypes = typing.Union[ForwardRef('URL'), str]
     params = None
     QueryParamTypes = typing.Union[ForwardRef('QueryParams'), typing.Mapping[str,
                        typing.Union[str, int, float, bool, NoneType, typing.Sequenc
                        e[typing.Union[str, int, float, bool, NoneType]]]], typing.L
                        ist[typing.Tuple[str, typing.Union[str, int, float, bool, No
                        neType]]], str]
     headers = None
     HeaderTypes = typing.Union[ForwardRef('Headers'), typing.Dict[typing.Union
                    [str, bytes], typing.Union[str, bytes]], typing.Sequence[typ
                    ing.Tuple[typing.Union[str, bytes], typing.Union[str, bytes]
                    ]]]
     cookies = None
     CookieTypes = typing.Union[ForwardRef('Cookies'), http.cookiejar.CookieJar
                    , typing.Dict[str, str]]
     auth = None
     AuthTypes = typing.Union[typing.Tuple[typing.Union[str, bytes], typing.U
                  nion[str, bytes]], typing.Callable[[ForwardRef('Request')],
                  ForwardRef('Request')], ForwardRef('Auth')]
     allow_redirects = True
     timeout = 60
     typing.Union = typing.Union
     TimeoutTypes = typing.Union[NoneType, float, typing.Tuple[float, float, flo
                     at, float], ForwardRef('Timeout')]
     UnsetType = <class 'httpx._config.UnsetType'>
     UNSET = <httpx._config.UnsetType object at 0x7fefb2868370>
     Response = <class 'httpx._models.Response'>
     self.request = <method 'Client.request' of <httpx._client.Client object at
                     0x7fef70ffbd60> _client.py:553>
    ..................................................

File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 578, in request
    553  def request(
    554      self,
    555      method: str,
    556      url: URLTypes,
    557      *,
    558      data: RequestData = None,
    559      files: RequestFiles = None,
    560      json: typing.Any = None,
    561      params: QueryParamTypes = None,
    562      headers: HeaderTypes = None,
    563      cookies: CookieTypes = None,
    564      auth: AuthTypes = None,
    565      allow_redirects: bool = True,
    566      timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
    567  ) -> Response:
 (...)
    574          params=params,
    575          headers=headers,
    576          cookies=cookies,
    577      )
--> 578      return self.send(
    579          request, auth=auth, allow_redirects=allow_redirects, timeout=timeout,
    ..................................................
     request = <Request('GET', 'https://xxxxnetworkk:5
                5001/h/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-
                1500-jpg/xxxxxxxxp=xxxxxxxxxxxxxxxxxxxxxxxxxxx;x
                xxxxxxxx/xxxxxx_02.jpg')>
     self = <httpx._client.Client object at 0x7fef70ffbd60>
     method = 'GET'
     url = 'https://xxxxnetworkk:55001/h/xxxxxxxxxxx
            xxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-1500-jpg/xxxxxxxx
            p=xxxxxxxxxxxxxxxxxxxxxxxxxxx;xxxxxxxxxxxxxxxx_
            02.jpg'
     URLTypes = typing.Union[ForwardRef('URL'), str]
     data = None
     RequestData = typing.Union[dict, str, bytes, typing.Iterator[bytes], typin
                    g.AsyncIterator[bytes]]
     files = None
     RequestFiles = typing.Dict[str, typing.Union[typing.IO[str], typing.IO[byte
                     s], str, bytes, typing.Tuple[typing.Union[str, NoneType], ty
                     ping.Union[typing.IO[str], typing.IO[bytes], str, bytes]], t
                     yping.Tuple[typing.Union[str, NoneType], typing.Union[typing
                     .IO[str], typing.IO[bytes], str, bytes], typing.Union[str, N
                     oneType]]]]
     json = None
     typing.Any = typing.Any
     params = None
     QueryParamTypes = typing.Union[ForwardRef('QueryParams'), typing.Mapping[str,
                        typing.Union[str, int, float, bool, NoneType, typing.Sequenc
                        e[typing.Union[str, int, float, bool, NoneType]]]], typing.L
                        ist[typing.Tuple[str, typing.Union[str, int, float, bool, No
                        neType]]], str]
     headers = None
     HeaderTypes = typing.Union[ForwardRef('Headers'), typing.Dict[typing.Union
                    [str, bytes], typing.Union[str, bytes]], typing.Sequence[typ
                    ing.Tuple[typing.Union[str, bytes], typing.Union[str, bytes]
                    ]]]
     cookies = None
     CookieTypes = typing.Union[ForwardRef('Cookies'), http.cookiejar.CookieJar
                    , typing.Dict[str, str]]
     auth = None
     AuthTypes = typing.Union[typing.Tuple[typing.Union[str, bytes], typing.U
                  nion[str, bytes]], typing.Callable[[ForwardRef('Request')],
                  ForwardRef('Request')], ForwardRef('Auth')]
     allow_redirects = True
     timeout = 60
     typing.Union = typing.Union
     TimeoutTypes = typing.Union[NoneType, float, typing.Tuple[float, float, flo
                     at, float], ForwardRef('Timeout')]
     UnsetType = <class 'httpx._config.UnsetType'>
     UNSET = <httpx._config.UnsetType object at 0x7fefb2868370>
     Response = <class 'httpx._models.Response'>
     self.send = <method 'Client.send' of <httpx._client.Client object at 0x7
                  fef70ffbd60> _client.py:582>
    ..................................................

File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 598, in send
    582  def send(
    583      self,
    584      request: Request,
    585      *,
    586      stream: bool = False,
    587      auth: AuthTypes = None,
    588      allow_redirects: bool = True,
    589      timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
    590  ) -> Response:
 (...)
    594      timeout = self.timeout if isinstance(timeout, UnsetType) else Timeout(timeout)
    595
    596      auth = self.build_auth(request, auth)
    597
--> 598      response = self.send_handling_redirects(
    599          request, auth=auth, timeout=timeout, allow_redirects=allow_redirects,
    ..................................................
     self = <httpx._client.Client object at 0x7fef70ffbd60>
     request = <Request('GET', 'https://xxxxnetworkk:5
                5001/h/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-
                1500-jpg/xxxxxxxxp=xxxxxxxxxxxxxxxxxxxxxxxxxxx;x
                xxxxxxxx/xxxxxx_02.jpg')>
     Request = <class 'httpx._models.Request'>
     stream = False
     auth = <httpx._auth.Auth object at 0x7fef70d94040>
     AuthTypes = typing.Union[typing.Tuple[typing.Union[str, bytes], typing.U
                  nion[str, bytes]], typing.Callable[[ForwardRef('Request')],
                  ForwardRef('Request')], ForwardRef('Auth')]
     allow_redirects = True
     timeout = Timeout(timeout=60)
     typing.Union = typing.Union
     TimeoutTypes = typing.Union[NoneType, float, typing.Tuple[float, float, flo
                     at, float], ForwardRef('Timeout')]
     UnsetType = <class 'httpx._config.UnsetType'>
     UNSET = <httpx._config.UnsetType object at 0x7fefb2868370>
     Response = <class 'httpx._models.Response'>
     self.timeout = Timeout(timeout=60)
     Timeout = <class 'httpx._config.Timeout'>
     self.build_auth = <method 'BaseClient.build_auth' of <httpx._client.Client obj
                        ect at 0x7fef70ffbd60> _client.py:256>
     self.send_handling_redirects = <method 'Client.send_handling_redirects' of <httpx._client.C
                                     lient object at 0x7fef70ffbd60> _client.py:610>
    ..................................................

File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 625, in send_handling_redirects
    610  def send_handling_redirects(
    611      self,
    612      request: Request,
    613      auth: Auth,
    614      timeout: Timeout,
    615      allow_redirects: bool = True,
    616      history: typing.List[Response] = None,
    617  ) -> Response:
 (...)
    621      while True:
    622          if len(history) > self.max_redirects:
    623              raise TooManyRedirects()
    624
--> 625          response = self.send_handling_auth(
    626              request, auth=auth, timeout=timeout, history=history
    ..................................................
     self = <httpx._client.Client object at 0x7fef70ffbd60>
     request = <Request('GET', 'https://xxxxnetworkk:5
                5001/h/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-
                1500-jpg/xxxxxxxxp=xxxxxxxxxxxxxxxxxxxxxxxxxxx;x
                xxxxxxxx/xxxxxx_02.jpg')>
     Request = <class 'httpx._models.Request'>
     auth = <httpx._auth.Auth object at 0x7fef70d94040>
     Auth = <class 'httpx._auth.Auth'>
     timeout = Timeout(timeout=60)
     Timeout = <class 'httpx._config.Timeout'>
     allow_redirects = True
     history = []
     typing.List = typing.List
     Response = <class 'httpx._models.Response'>
     self.max_redirects = 20
     TooManyRedirects = <class 'httpx._exceptions.TooManyRedirects'>
     self.send_handling_auth = <method 'Client.send_handling_auth' of <httpx._client.Client
                                 object at 0x7fef70ffbd60> _client.py:649>
    ..................................................

File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 662, in send_handling_auth
    649  def send_handling_auth(
    650      self,
    651      request: Request,
    652      history: typing.List[Response],
    653      auth: Auth,
    654      timeout: Timeout,
    655  ) -> Response:
 (...)
    658
    659      auth_flow = auth.auth_flow(request)
    660      request = next(auth_flow)
    661      while True:
--> 662          response = self.send_single_request(request, timeout)
    663          if auth.requires_response_body:
    ..................................................
     self = <httpx._client.Client object at 0x7fef70ffbd60>
     request = <Request('GET', 'https://xxxxnetworkk:5
                5001/h/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-
                1500-jpg/xxxxxxxxp=xxxxxxxxxxxxxxxxxxxxxxxxxxx;x
                xxxxxxxx/xxxxxx_02.jpg')>
     Request = <class 'httpx._models.Request'>
     history = []
     typing.List = typing.List
     Response = <class 'httpx._models.Response'>
     auth = <httpx._auth.Auth object at 0x7fef70d94040>
     Auth = <class 'httpx._auth.Auth'>
     timeout = Timeout(timeout=60)
     Timeout = <class 'httpx._config.Timeout'>
     auth_flow = <generator object Auth.auth_flow at 0x7fef70d9c0b0>
     auth.auth_flow = <method 'Auth.auth_flow' of <httpx._auth.Auth object at 0x7f
                       ef70d94040> _auth.py:22>
     self.send_single_request = <method 'Client.send_single_request' of <httpx._client.Clien
                                 t object at 0x7fef70ffbd60> _client.py:678>
     auth.requires_response_body = False
    ..................................................

File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 692, in send_single_request
    678  def send_single_request(self, request: Request, timeout: Timeout) -> Response:
 (...)
    688              status_code,
    689              reason_phrase,
    690              headers,
    691              stream,
--> 692          ) = dispatcher.request(
    693              request.method.encode(),
    ..................................................
     self = <httpx._client.Client object at 0x7fef70ffbd60>
     request = <Request('GET', 'https://xxxxnetworkk:5
                5001/h/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-
                1500-jpg/xxxxxxxxp=xxxxxxxxxxxxxxxxxxxxxxxxxxx;x
                xxxxxxxx/xxxxxx_02.jpg')>
     Request = <class 'httpx._models.Request'>
     timeout = Timeout(timeout=60)
     Timeout = <class 'httpx._config.Timeout'>
     Response = <class 'httpx._models.Response'>
     dispatcher.request = <method 'SyncHTTPProxy.request' of <httpcore._sync.http_prox
                           y.SyncHTTPProxy object at 0x7fef706e0250> http_proxy.py:76>
    ..................................................

File /usr/local/lib/python3.8/dist-packages/httpcore/_sync/http_proxy.py, line 110, in request
    76   def request(
    77       self,
    78       method: bytes,
    79       url: URL,
    80       headers: Headers = None,
    81       stream: SyncByteStream = None,
    82       timeout: TimeoutDict = None,
    83   ) -> Tuple[bytes, int, bytes, Headers, SyncByteStream]:
 (...)
    106              self.proxy_headers,
    107              method,
    108              url,
    109          )
--> 110          return self._tunnel_request(
    111              method, url, headers=headers, stream=stream, timeout=timeout
    ..................................................
     self = <httpcore._sync.http_proxy.SyncHTTPProxy object at 0x7fef706
             e0250>
     method = b'GET'
     url = (b'https', b'xxxxnetworkk', 55001, b'/h
            /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-1500-j
            pg/xxxxxxxxp=xxxxxxxxxxxxxxxxxxxxxxxxxxx;xres=or
            g/xxxxxx_02.jpg', )
     URL = typing.Tuple[bytes, bytes, int, bytes]
     headers = [(b'host', b'xxxxnetworkk:55001', ), (b
                'accept', b'*/*', ), (b'accept-encoding', b'gzip, deflate',
                ), (b'connection', b'keep-alive', ), (b'dnt', b'1', ), (b'se
                c-fetch-dest', b'document', ), (b'sec-fetch-mode', b'navigat
                e', ), (b'sec-fetch-site', b'same-origin', ), (b'sec-fetch-u
                ser', b'?1', ), (b'upgrade-insecure-requests', b'1', ), (b'u
                ser-agent', b'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', ),
                ]
     Headers = typing.List[typing.Tuple[bytes, bytes]]
     stream = <httpx._content_streams.ByteStream object at 0x7fef70d949d0>
     SyncByteStream = <class 'httpcore._sync.base.SyncByteStream'>
     timeout = {'connect': 60,
                'read': 60,
                'write': 60,
                'pool': 60}
     TimeoutDict = typing.Dict[str, typing.Union[float, NoneType]]
     Tuple = typing.Tuple
     self.proxy_headers = []
     self._tunnel_request = <method 'SyncHTTPProxy._tunnel_request' of <httpcore._sync.h
                             ttp_proxy.SyncHTTPProxy object at 0x7fef706e0250> http_proxy
                             .py:152>
    ..................................................

File /usr/local/lib/python3.8/dist-packages/httpcore/_sync/http_proxy.py, line 201, in _tunnel_request
    152  def _tunnel_request(
    153      self,
    154      method: bytes,
    155      url: URL,
    156      headers: Headers = None,
    157      stream: SyncByteStream = None,
    158      timeout: TimeoutDict = None,
    159  ) -> Tuple[bytes, int, bytes, Headers, SyncByteStream]:
 (...)
    197
    198          # See if the tunnel was successfully established.
    199          if proxy_status_code < 200 or proxy_status_code > 299:
    200              msg = "%d %s" % (proxy_status_code, proxy_reason_phrase.decode("ascii"))
--> 201              raise ProxyError(msg)
    202
    ..................................................
     self = <httpcore._sync.http_proxy.SyncHTTPProxy object at 0x7fef706
             e0250>
     method = b'GET'
     url = (b'https', b'xxxxnetworkk', 55001, b'/h
            /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx-1050-1500-j
            pg/xxxxxxxxp=xxxxxxxxxxxxxxxxxxxxxxxxxxx;xres=or
            g/xxxxxx_02.jpg', )
     URL = typing.Tuple[bytes, bytes, int, bytes]
     headers = [(b'host', b'xxxxnetworkk:55001', ), (b
                'accept', b'*/*', ), (b'accept-encoding', b'gzip, deflate',
                ), (b'connection', b'keep-alive', ), (b'dnt', b'1', ), (b'se
                c-fetch-dest', b'document', ), (b'sec-fetch-mode', b'navigat
                e', ), (b'sec-fetch-site', b'same-origin', ), (b'sec-fetch-u
                ser', b'?1', ), (b'upgrade-insecure-requests', b'1', ), (b'u
                ser-agent', b'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', ),
                ]
     Headers = typing.List[typing.Tuple[bytes, bytes]]
     stream = <httpx._content_streams.ByteStream object at 0x7fef70d949d0>
     SyncByteStream = <class 'httpcore._sync.base.SyncByteStream'>
     timeout = {'connect': 60,
                'read': 60,
                'write': 60,
                'pool': 60}
     TimeoutDict = typing.Dict[str, typing.Union[float, NoneType]]
     Tuple = typing.Tuple
     proxy_status_code = 403
     msg = '403 Forbidden port'
     ProxyError = <class 'httpcore._exceptions.ProxyError'>
    ..................................................

---- (full traceback above) ----
File <ipython-input-2-b574ee64a121>, line 254, in download_img_from_api_result
    img_resp = self.client.get(api_result["i"], timeout=60)
File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 733, in get
    return self.request(
File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 578, in request
    return self.send(
File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 598, in send
    response = self.send_handling_redirects(
File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 625, in send_handling_redirects
    response = self.send_handling_auth(
File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 662, in send_handling_auth
    response = self.send_single_request(request, timeout)
File /usr/local/lib/python3.8/dist-packages/httpx/_client.py, line 692, in send_single_request
    ) = dispatcher.request(
File /usr/local/lib/python3.8/dist-packages/httpcore/_sync/http_proxy.py, line 110, in request
    return self._tunnel_request(
File /usr/local/lib/python3.8/dist-packages/httpcore/_sync/http_proxy.py, line 201, in _tunnel_request
    raise ProxyError(msg)

ProxyError: 403 Forbidden port

Closing comment (2020-05-16T07:00:12Z): Hi!
AFAIK HTTPX does not support SOCKS proxies yet so issues like this are to be expected.
The workaround you linked to bypasses the proxy part and instead makes a request over a SOCKS socket directly - it doesn’t seem to be the same thing, so I’m not surprised the workaround works while the proper proxy usage encounters issues.
Unless I’m missing something here, I’m tempted to close this as a duplicate of #203, which addresses SOCKS support. :)