Я сталкиваюсь с ошибкой ConnectionResetError при попытке получить доступ к определенному URL-адресу, используя как запросы Python, так и библиотеки urllib. Несмотря на предоставление соответствующих заголовков, соединение принудительно закрывается удаленным хостом. Эта проблема возникает постоянно, и я ищу информацию о ее причине и возможных решениях.
Вот фрагмент кода, который я использую:
import requests
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'en-US,en;q=0.9',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'DNT': '1',
'Pragma': 'no-cache',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
'sec-ch-ua': '"Google Chrome";v = "123", "Not:A-Brand";v = "8", "Chromium";v = "123"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
response = requests.get('https://newjersey.mylicense.com/verification/Search.aspx', headers=headers)
И вот ошибка, которую я получаю:
('Connection aborted.', ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None))
Используемые библиотеки:
Я попытался получить доступ к URL-адресу, используя библиотеки Requests и urllib, предоставив необходимые заголовки для имитации запроса браузера. Я ожидал, что соединение будет установлено успешно, что позволит мне получить желаемый контент. Однако я постоянно получал ConnectionResetError, указывающий, что соединение было принудительно закрыто удаленным хостом.
Рассматриваемый URL-адрес работает должным образом при доступе через веб-браузер, что указывает на то, что проблема может заключаться в библиотеках Python, а не в самом сервере.






Чтобы это выяснить, потребовалось чертовски много усилий по устранению неполадок. Основная проблема заключается в том, что шифр ('AES256-GCM-SHA384'), используемый сервером для TLS-соединения, не является одним из шифров по умолчанию, используемых пакетом ssl при установке защищенного соединения. Это приводит к сбою соединения при рукопожатии, что вызывает наблюдаемую нами ошибку.
Вам нужно использовать HTTPAdapter с правильным шифром.
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
class SoleCipherAdapter(HTTPAdapter):
"""Custom adapter that only uses 1 cipher"""
CIPHER = 'AES256-GCM-SHA384'
def init_poolmanager(self, *args, **kwargs):
context = create_urllib3_context(ciphers=self.CIPHER)
kwargs['ssl_context'] = context
return super().init_poolmanager(*args, **kwargs)
url = 'https://newjersey.mylicense.com/verification/Search.aspx'
sess = requests.Session()
sess.mount('https://', SoleCipherAdapter())
res = sess.get(url)
res.status_code
# returns:
# 200
Использование Curl работает, но не очень информативно.
C:\>curl "https://newjersey.mylicense.com/verification/Search.aspx" -vv --head
* Trying 208.95.153.120:443...
* Connected to newjersey.mylicense.com (208.95.153.120) port 443
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* ALPN: server did not agree on a protocol. Uses default.
* using HTTP/1.x
> HEAD /verification/Search.aspx HTTP/1.1
> Host: newjersey.mylicense.com
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
...
Python терпит неудачу:
Попытка создать соединение вручную в Python завершается неудачей с той же ошибкой, которую вы видите, используя следующее:
import socket
import ssl
host = 'newjersey.mylicense.com'
context = ssl.create_default_context()
data = b"""HEAD /verification/Search.aspx HTTP/1.1
Host: newjersey.mylicense.com
User-Agent: python/3.11.8
Accept: */*
"""
with socket.create_connection((host, 443)) as sock:
with context.wrap_socket(sock, server_hostname=host) as secure_sock:
secure_sock.send(data)
print(secure_sock.read().decode())
# raises:
File ~\envs\test\Lib\ssl.py:1379, in SSLSocket.do_handshake(self, block)
1377 if timeout == 0.0 and block:
1378 self.settimeout(None)
-> 1379 self._sslobj.do_handshake()
1380 finally:
1381 self.settimeout(timeout)
ConnectionResetError: [WinError 10054] An existing connection was forcibly
closed by the remote host
Я обратился к созданию соединения вручную с помощью openssl. Здесь мы наконец находим необходимую информацию. (Это довольно многословно.)
C:\>openssl s_client -connect newjersey.mylicense.com:443
Соединение установлено успешно, и выводится следующая информация (для краткости я удалил ее фрагменты):
CONNECTED(000001B4)
depth=2 C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", ...
verify return:1
...
---
Certificate chain
0 s:CN = *.mylicense.com
i:C = US, ST = Arizona, L = Scottsdale, O = "GoDaddy.com, Inc.", OU = ...
a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
v:NotBefore: May 28 22:06:00 2023 GMT; NotAfter: Jun 28 07:22:12 2024 GMT
...
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGkjCCBXqgAwIBAgIJAKhBrHwkidbVMA0GCSqGSIb3DQEBCwUAMIG0MQswCQYD
...
-----END CERTIFICATE-----
subject=CN = *.mylicense.com
issuer=...
---
No client certificate CA names sent
---
SSL handshake has read 4236 bytes and written 647 bytes
Verification: OK
---
New, TLSv1.2, Cipher is AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : AES256-GCM-SHA384
Session-ID: ...
Session-ID-ctx:
Master-Key: ...
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1712847797
Timeout : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: yes
---
После подключения мы можем отправить HTTP-запрос в качестве необработанных входных данных:
...
Verify return code: 0 (ok)
Extended master secret: yes
---
HEAD /verification/Search.aspx HTTP/1.1
Host: newjersey.mylicense.com
User-Agent: python/3.11.8
Accept: */*
И возвращает ответ на запрос HEAD:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 43543
Content-Type: text/html; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.5
Set-Cookie: ASP.NET_SessionId=...
Импортируемые части информации о подключении: SSL handshake has read 4236 bytes and written 647 bytes и TLSv1.2, Cipher is AES256-GCM-SHA384. Здесь рукопожатие прошло успешно, и оно сообщает нам версию TLS и использованный шифр. requests по умолчанию использует TLS 1.2, так что это то же самое. Осталось попробовать другой шифр.
На самом деле это всего лишь добавление одной строки к предыдущему коду Python:
import socket
import ssl
host = 'newjersey.mylicense.com'
context = ssl.create_default_context()
context.set_ciphers('AES256-GCM-SHA384')
data = b"""HEAD /verification/Search.aspx HTTP/1.1
Host: newjersey.mylicense.com
User-Agent: python/3.11.8
Accept: */*
"""
with socket.create_connection((host, 443)) as sock:
with context.wrap_socket(sock, server_hostname=host) as secure_sock:
secure_sock.send(data)
print(secure_sock.read().decode())
И, наконец, мы получаем ожидаемый результат:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 43543
Content-Type: text/html; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.5
Set-Cookie: ASP.NET_SessionId=iejmoxg
Это очень странно. Я могу успешно отправить запрос GET, используя Powershell, но не Python.