-
Notifications
You must be signed in to change notification settings - Fork 1
V1 #1
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
base: main
Are you sure you want to change the base?
V1 #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# This workflow will upload a Python Package using Twine when a release is created | ||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries | ||
|
||
# This workflow uses actions that are not certified by GitHub. | ||
# They are provided by a third-party and are governed by | ||
# separate terms of service, privacy policy, and support | ||
# documentation. | ||
|
||
name: Upload Python Package | ||
|
||
on: | ||
push: | ||
branches: | ||
- 'main' | ||
|
||
permissions: | ||
contents: read | ||
|
||
jobs: | ||
deploy: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Set up Python | ||
uses: actions/setup-python@v3 | ||
with: | ||
python-version: '3.x' | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install build | ||
- name: Build package | ||
run: python -m build | ||
- name: Publish package | ||
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 | ||
with: | ||
user: __token__ | ||
password: ${{ secrets.PYPI_API_TOKEN }} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# http_mptcp | ||
This | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you remove the binary files from the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
[build-system] | ||
requires = ["hatchling"] | ||
build-backend = "hatchling.build" | ||
|
||
[project] | ||
name = "http-mptcp" | ||
version = "1.0.0" | ||
authors = [ | ||
{ name="Dourov Maxime", email="[email protected]" }, | ||
] | ||
description = "a MPTCP extension for the http library" | ||
readme = "README.md" | ||
requires-python = ">=3.8" | ||
keywords = ["MPTCP", "multipath", "tcp", "http", "https"] | ||
classifiers = [ | ||
"Programming Language :: Python :: 3", | ||
"License :: OSI Approved :: MIT License", | ||
] | ||
|
||
[project.urls] | ||
Homepage = "" | ||
Issues = "" | ||
|
||
[tool.hatch.build.targets.wheel] | ||
packages = ["src/client", "src/server"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from http import * |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
from http.client import * | ||
import socket | ||
import errno | ||
import sys | ||
|
||
_GLOBAL_DEFAULT_TIMEOUT = object() | ||
|
||
def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, *, all_errors=False, proto=0): | ||
host, port = address | ||
exceptions = [] | ||
for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): | ||
af, socktype, _, canonname, sa = res | ||
sock = None | ||
try: | ||
sock = socket.socket(af, socktype, proto) | ||
# if timeout is not _GLOBAL_DEFAULT_TIMEOUT: | ||
# sock.settimeout(timeout) | ||
if source_address: | ||
sock.bind(source_address) | ||
sock.connect(sa) | ||
# Break explicitly a reference cycle | ||
exceptions.clear() | ||
return sock | ||
|
||
except Exception as exc: | ||
if not all_errors: | ||
exceptions.clear() # raise only the last error | ||
exceptions.append(exc) | ||
if sock is not None: | ||
sock.close() | ||
|
||
if len(exceptions): | ||
try: | ||
if not all_errors: | ||
raise exceptions[0] | ||
raise ExceptionGroup("create_connection failed", exceptions) | ||
finally: | ||
# Break explicitly a reference cycle | ||
exceptions.clear() | ||
else: | ||
raise "getaddrinfo returns an empty list" | ||
|
||
class HTTPConnection(http.client.HTTPConnection): | ||
def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, | ||
source_address=None, blocksize=8192): | ||
super().__init__(host, port, timeout, source_address, blocksize) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you not set |
||
|
||
def connect(self): | ||
sys.audit("http.client.connect", self, self.host, self.port) | ||
self.sock = _create_connection((self.host,self.port), self.timeout, self.source_address, proto=socket.IPPROTO_MPTCP) | ||
|
||
try: | ||
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | ||
except OSError as e: | ||
if e.errno != errno.ENOPROTOOPT: | ||
raise | ||
|
||
if self._tunnel_host: | ||
self._tunnel() | ||
|
||
def _create_https_context(http_version): | ||
context = ssl._create_default_https_context() | ||
# send ALPN extension to indicate HTTP/1.1 protocol | ||
if http_version == 11: | ||
context.set_alpn_protocols(['http/1.1']) | ||
# enable PHA for TLS 1.3 connections if available | ||
if context.post_handshake_auth is not None: | ||
context.post_handshake_auth = True | ||
return context | ||
|
||
try: | ||
import ssl | ||
except ImportError: | ||
pass | ||
else: | ||
class HTTPSConnection(HTTPConnection): | ||
"This class allows communication via SSL." | ||
|
||
default_port = HTTPS_PORT = 443 | ||
|
||
def __init__(self, host, port=None, | ||
*, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, | ||
source_address=None, context=None, blocksize=8192): | ||
super(HTTPConnection, self).__init__(host, port, timeout, | ||
source_address, | ||
blocksize=blocksize) | ||
if context is None: | ||
context = _create_https_context(self._http_vsn) | ||
self._context = context | ||
|
||
def connect(self): | ||
super().connect() | ||
|
||
if self._tunnel_host: | ||
server_hostname = self._tunnel_host | ||
else: | ||
server_hostname = self.host | ||
|
||
self.sock = self._context.wrap_socket(self.sock, server_hostname=server_hostname) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from http.cookiejar import * |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from http.cookies import * |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from http.server import HTTPServer as HTTPServer_old | ||
from http.server import test | ||
from http.server import * | ||
|
||
import socket | ||
import os | ||
import socketserver | ||
from socketserver import BaseServer | ||
|
||
class HTTPServer(HTTPServer_old): | ||
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): | ||
"""Constructor. May be extended, do not override.""" | ||
print("bob") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the builder? |
||
BaseServer.__init__(self, server_address, RequestHandlerClass) | ||
self.socket = socket.socket(family=self.address_family, | ||
type=self.socket_type, | ||
proto=socket.IPPROTO_MPTCP) | ||
if bind_and_activate: | ||
try: | ||
self.server_bind() | ||
self.server_activate() | ||
except: | ||
self.server_close() | ||
raise | ||
|
||
class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer): | ||
daemon_threads = True | ||
|
||
if __name__ == '__main__': | ||
import argparse | ||
import contextlib | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument('--cgi', action='store_true', | ||
help='run as CGI server') | ||
parser.add_argument('--bind', '-b', metavar='ADDRESS', | ||
help='specify alternate bind address ' | ||
'(default: all interfaces)') | ||
parser.add_argument('--directory', '-d', default=os.getcwd(), | ||
help='specify alternate directory ' | ||
'(default: current directory)') | ||
parser.add_argument('port', action='store', default=8000, type=int, | ||
nargs='?', | ||
help='specify alternate port (default: 8000)') | ||
args = parser.parse_args() | ||
if args.cgi: | ||
handler_class = CGIHTTPRequestHandler | ||
else: | ||
handler_class = SimpleHTTPRequestHandler | ||
|
||
# ensure dual-stack is not disabled; ref #38907 | ||
class DualStackServer(ThreadingHTTPServer): | ||
|
||
def server_bind(self): | ||
# suppress exception when protocol is IPv4 | ||
with contextlib.suppress(Exception): | ||
self.socket.setsockopt( | ||
socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) | ||
return super().server_bind() | ||
|
||
def finish_request(self, request, client_address): | ||
self.RequestHandlerClass(request, client_address, self, | ||
directory=args.directory) | ||
|
||
test( | ||
HandlerClass=handler_class, | ||
ServerClass=DualStackServer, | ||
port=args.port, | ||
bind=args.bind, | ||
) |
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.
Nice readme :-)
Should you duplicate what's in the PR message?