Implement HTTP(S) mode (closes #112) (#883)

This commit is contained in:
Lonami 2018-07-08 17:45:49 +02:00 committed by GitHub
parent e0513e10df
commit 128053750d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 1 deletions

View File

@ -11,6 +11,7 @@ import asyncio
import errno import errno
import logging import logging
import socket import socket
import ssl
from io import BytesIO from io import BytesIO
CONN_RESET_ERRNOS = { CONN_RESET_ERRNOS = {
@ -28,6 +29,7 @@ try:
except ImportError: except ImportError:
socks = None socks = None
SSL_PORT = 443
__log__ = logging.getLogger(__name__) __log__ = logging.getLogger(__name__)
@ -37,15 +39,18 @@ class TcpClient:
class SocketClosed(ConnectionError): class SocketClosed(ConnectionError):
pass pass
def __init__(self, *, loop, timeout, proxy=None): def __init__(self, *, loop, timeout, ssl=None, proxy=None):
""" """
Initializes the TCP client. Initializes the TCP client.
:param proxy: the proxy to be used, if any. :param proxy: the proxy to be used, if any.
:param timeout: the timeout for connect, read and write operations. :param timeout: the timeout for connect, read and write operations.
:param ssl: ssl.wrap_socket keyword arguments to use when connecting
if port == SSL_PORT, or do nothing if not present.
""" """
self._loop = loop self._loop = loop
self.proxy = proxy self.proxy = proxy
self.ssl = ssl
self._socket = None self._socket = None
self._closed = asyncio.Event(loop=self._loop) self._closed = asyncio.Event(loop=self._loop)
self._closed.set() self._closed.set()
@ -87,6 +92,8 @@ class TcpClient:
try: try:
if self._socket is None: if self._socket is None:
self._socket = self._create_socket(mode, self.proxy) self._socket = self._create_socket(mode, self.proxy)
if self.ssl and port == SSL_PORT:
self._socket = ssl.wrap_socket(self._socket, **self.ssl)
await asyncio.wait_for( await asyncio.wait_for(
self._loop.sock_connect(self._socket, address), self._loop.sock_connect(self._socket, address),

View File

@ -2,3 +2,4 @@ from .tcpfull import ConnectionTcpFull
from .tcpabridged import ConnectionTcpAbridged from .tcpabridged import ConnectionTcpAbridged
from .tcpobfuscated import ConnectionTcpObfuscated from .tcpobfuscated import ConnectionTcpObfuscated
from .tcpintermediate import ConnectionTcpIntermediate from .tcpintermediate import ConnectionTcpIntermediate
from .http import ConnectionHttp

View File

@ -0,0 +1,62 @@
import errno
import ssl
from .common import Connection
from ...extensions import TcpClient
class ConnectionHttp(Connection):
def __init__(self, *, loop, timeout, proxy=None):
super().__init__(loop=loop, timeout=timeout, proxy=proxy)
self.conn = TcpClient(
timeout=self._timeout, loop=self._loop, proxy=self._proxy,
ssl=dict(ssl_version=ssl.PROTOCOL_SSLv23, ciphers='ADH-AES256-SHA')
)
self.read = self.conn.read
self.write = self.conn.write
self._host = None
async def connect(self, ip, port):
self._host = '{}:{}'.format(ip, port)
try:
await self.conn.connect(ip, port)
except OSError as e:
if e.errno == errno.EISCONN:
return # Already connected, no need to re-set everything up
else:
raise
def get_timeout(self):
return self.conn.timeout
def is_connected(self):
return self.conn.is_connected
async def close(self):
self.conn.close()
async def recv(self):
while True:
line = await self._read_line()
if line.lower().startswith(b'content-length: '):
await self.read(2)
length = int(line[16:-2])
return await self.read(length)
async def _read_line(self):
newline = ord('\n')
line = await self.read(1)
while line[-1] != newline:
line += await self.read(1)
return line
async def send(self, message):
await self.write(
'POST /api HTTP/1.1\r\n'
'Host: {}\r\n'
'Content-Type: application/x-www-form-urlencoded\r\n'
'Connection: keep-alive\r\n'
'Keep-Alive: timeout=100000, max=10000000\r\n'
'Content-Length: {}\r\n\r\n'.format(self._host, len(message))
.encode('ascii') + message
)