Skip to content

Commit 87745cc

Browse files
committed
add server api
1 parent f2a56a3 commit 87745cc

File tree

6 files changed

+66
-16
lines changed

6 files changed

+66
-16
lines changed

Diff for: README.rst

+29-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Features
8080
- System proxy auto-setting support.
8181
- UDP proxy client/server support.
8282
- Schedule (load balance) among remote servers.
83-
- Client UDP/TCP API support.
83+
- Client/Server API support.
8484

8585
.. _One-Time-Auth: https://shadowsocks.org/en/spec/one-time-auth.html
8686

@@ -387,6 +387,34 @@ Client API
387387
388388
asyncio.run(test_udp('ss://chacha20:password@remote_host:remote_port'))
389389
390+
Server API
391+
----------
392+
393+
- Server API example:
394+
395+
.. code:: rst
396+
397+
import asyncio
398+
import pproxy
399+
400+
server = pproxy.Server('ss://0.0.0.0:1234')
401+
remote = pproxy.Connection('ss://1.2.3.4:5678')
402+
args = dict( rserver = [remote],
403+
verbose = print )
404+
405+
loop = asyncio.get_event_loop()
406+
handler = loop.run_until_complete(server.start_server(args))
407+
try:
408+
loop.run_forever()
409+
except KeyboardInterrupt:
410+
print('exit!')
411+
412+
handler.close()
413+
loop.run_until_complete(handler.wait_closed())
414+
loop.run_until_complete(loop.shutdown_asyncgens())
415+
loop.close()
416+
417+
390418
Examples
391419
--------
392420

Diff for: pproxy/__doc__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__title__ = "pproxy"
2-
__version__ = "2.0.2"
2+
__version__ = "2.0.3"
33
__license__ = "MIT"
44
__description__ = "Proxy server that can tunnel among remote servers by regex rules."
55
__keywords__ = "proxy socks http shadowsocks shadowsocksr ssr redirect pf tunnel cipher ssl udp"

Diff for: pproxy/__init__.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
from .server import ProxyURI
1+
from . import server
22

3-
Connection = ProxyURI.compile_relay
3+
Connection = server.ProxyURI.compile_relay
4+
DIRECT = server.ProxyURI.DIRECT
5+
Server = server.ProxyURI.compile
6+
Rule = server.pattern_compile

Diff for: pproxy/proto.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,14 @@ def udp_connect(self, rauth, host_name, port, data, **kw):
223223
class HTTP(BaseProtocol):
224224
def correct_header(self, header, **kw):
225225
return header and header.isalpha()
226-
async def parse(self, header, reader, writer, auth, authtable, httpget, **kw):
226+
async def parse(self, header, reader, writer, auth, authtable, httpget=None, **kw):
227227
lines = header + await reader.read_until(b'\r\n\r\n')
228228
headers = lines[:-4].decode().split('\r\n')
229229
method, path, ver = HTTP_LINE.match(headers.pop(0)).groups()
230230
lines = '\r\n'.join(i for i in headers if not i.startswith('Proxy-'))
231231
headers = dict(i.split(': ', 1) for i in headers if ': ' in i)
232232
url = urllib.parse.urlparse(path)
233-
if method == 'GET' and not url.hostname:
233+
if method == 'GET' and not url.hostname and httpget:
234234
for path, text in httpget.items():
235235
if url.path == path:
236236
authtable.set_authed()

Diff for: pproxy/server.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def schedule(rserver, salgorithm, host_name):
5151
else:
5252
raise Exception('Unknown scheduling algorithm') #Unreachable
5353

54-
async def stream_handler(reader, writer, unix, lbind, protos, rserver, block, cipher, salgorithm, verbose=DUMMY, modstat=lambda r,h:lambda i:DUMMY, **kwargs):
54+
async def stream_handler(reader, writer, unix, lbind, protos, rserver, cipher, block=None, salgorithm='fa', verbose=DUMMY, modstat=lambda r,h:lambda i:DUMMY, **kwargs):
5555
try:
5656
if unix:
5757
remote_ip, server_ip, remote_text = 'local', None, 'unix_local'
@@ -253,13 +253,13 @@ def prepare_udp_connection(self, host, port, data):
253253
if self.cipher:
254254
data = self.cipher.datagram.encrypt(data)
255255
return data
256-
def start_udp_server(self, loop, args, option):
256+
def start_udp_server(self, args):
257257
class Protocol(asyncio.DatagramProtocol):
258258
def connection_made(self, transport):
259259
self.transport = transport
260260
def datagram_received(self, data, addr):
261-
asyncio.ensure_future(datagram_handler(self.transport, data, addr, **vars(args), **vars(option)))
262-
return loop.create_datagram_endpoint(Protocol, local_addr=(self.host_name, self.port))
261+
asyncio.ensure_future(datagram_handler(self.transport, data, addr, **vars(self), **args))
262+
return asyncio.get_event_loop().create_datagram_endpoint(Protocol, local_addr=(self.host_name, self.port))
263263
async def open_connection(self, host, port, local_addr, lbind):
264264
if self.reuse:
265265
if self.streams is None or self.streams.done() and not self.handler:
@@ -305,12 +305,12 @@ async def prepare_ciphers_and_headers(self, reader_remote, writer_remote, host,
305305
await self.rproto.connect(reader_remote=reader_remote, writer_remote=writer_remote, rauth=self.auth, host_name=whost, port=wport, writer_cipher_r=writer_cipher_r, myhost=self.host_name, sock=writer_remote.get_extra_info('socket'))
306306
return await self.relay.prepare_ciphers_and_headers(reader_remote, writer_remote, host, port, handler)
307307
return reader_remote, writer_remote
308-
def start_server(self, args, option):
309-
handler = functools.partial(reuse_stream_handler if self.reuse else stream_handler, **vars(args), **vars(option))
308+
def start_server(self, args):
309+
handler = functools.partial(reuse_stream_handler if self.reuse else stream_handler, **vars(self), **args)
310310
if self.unix:
311311
return asyncio.start_unix_server(handler, path=self.bind, ssl=self.sslserver)
312312
else:
313-
return asyncio.start_server(handler, host=self.host_name, port=self.port, ssl=self.sslserver, reuse_port=args.ruport)
313+
return asyncio.start_server(handler, host=self.host_name, port=self.port, ssl=self.sslserver, reuse_port=args.get('ruport'))
314314
async def tcp_connect(self, host, port, local_addr=None, lbind=None):
315315
reader, writer = await self.open_connection(host, port, local_addr, lbind)
316316
try:
@@ -431,8 +431,8 @@ def main():
431431
parser.add_argument('--pac', help='http PAC path')
432432
parser.add_argument('--get', dest='gets', default=[], action='append', help='http custom {path,file}')
433433
parser.add_argument('--sys', action='store_true', help='change system proxy setting (mac, windows)')
434+
parser.add_argument('--reuse', dest='ruport', action='store_true', help='set SO_REUSEPORT (Linux only)')
434435
parser.add_argument('--test', help='test this url for all remote proxies and exit')
435-
parser.add_argument('--reuse', help='set SO_REUSEPORT (Linux only)', dest='ruport', action='store_true')
436436
parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
437437
args = parser.parse_args()
438438
if args.test:
@@ -469,14 +469,14 @@ def main():
469469
for option in args.listen:
470470
print('Serving on', option.bind, 'by', ",".join(i.name for i in option.protos) + ('(SSL)' if option.sslclient else ''), '({}{})'.format(option.cipher.name, ' '+','.join(i.name() for i in option.cipher.plugins) if option.cipher and option.cipher.plugins else '') if option.cipher else '')
471471
try:
472-
server = loop.run_until_complete(option.start_server(args, option))
472+
server = loop.run_until_complete(option.start_server(vars(args)))
473473
servers.append(server)
474474
except Exception as ex:
475475
print('Start server failed.\n\t==>', ex)
476476
for option in args.ulisten:
477477
print('Serving on UDP', option.bind, 'by', ",".join(i.name for i in option.protos), f'({option.cipher.name})' if option.cipher else '')
478478
try:
479-
server, protocal = loop.run_until_complete(option.start_udp_server(loop, args, option))
479+
server, protocol = loop.run_until_complete(option.start_udp_server(vars(args)))
480480
servers.append(server)
481481
except Exception as ex:
482482
print('Start server failed.\n\t==>', ex)

Diff for: tests/api_server.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncio
2+
import pproxy
3+
4+
server = pproxy.Server('ss://0.0.0.0:1234')
5+
remote = pproxy.Connection('ss://1.2.3.4:5678')
6+
args = dict( rserver = [remote],
7+
verbose = print )
8+
9+
loop = asyncio.get_event_loop()
10+
handler = loop.run_until_complete(server.start_server(args))
11+
try:
12+
loop.run_forever()
13+
except KeyboardInterrupt:
14+
print('exit!')
15+
16+
handler.close()
17+
loop.run_until_complete(handler.wait_closed())
18+
loop.run_until_complete(loop.shutdown_asyncgens())
19+
loop.close()

0 commit comments

Comments
 (0)