From 0c12ee04cbe08570e1916618f5cb4b375b965207 Mon Sep 17 00:00:00 2001 From: clowwindy Date: Tue, 3 Jun 2014 21:59:34 +0800 Subject: [PATCH] implement fast open in local --- .travis.yml | 1 + shadowsocks/tcprelay.py | 57 +++++++++++++++++++++++++++++++++-------- tests/fastopen.json | 10 ++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 tests/fastopen.json diff --git a/.travis.yml b/.travis.yml index 81cfb2b..90be114 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,3 +13,4 @@ script: - python test.py -c tests/salsa20.json - python test.py -c tests/server-multi-passwd.json - python test.py -c tests/workers.json + - python test.py -c tests/fastopen.json diff --git a/shadowsocks/tcprelay.py b/shadowsocks/tcprelay.py index a6e3e3e..dcfa8ed 100644 --- a/shadowsocks/tcprelay.py +++ b/shadowsocks/tcprelay.py @@ -34,6 +34,8 @@ from common import parse_header TIMEOUTS_CLEAN_SIZE = 512 TIMEOUT_PRECISION = 4 +MSG_FASTOPEN = 0x20000000 + CMD_CONNECT = 1 CMD_BIND = 2 CMD_UDP_ASSOCIATE = 3 @@ -199,6 +201,34 @@ class TCPRelayHandler(object): if is_local: data = self._encryptor.encrypt(data) self._data_to_write_to_remote.append(data) + if is_local and self._upstream_status == WAIT_STATUS_INIT and \ + self._config['fast_open']: + try: + data = ''.join(self._data_to_write_to_local) + l = len(data) + s = self._remote_sock.sendto(data, MSG_FASTOPEN, + self.remote_address) + if s < l: + data = data[s:] + self._data_to_write_to_local = [data] + self.update_stream(STREAM_UP, WAIT_STATUS_READWRITING) + self.update_stream(STREAM_DOWN, WAIT_STATUS_READING) + else: + self._data_to_write_to_local = [] + self.update_stream(STREAM_UP, WAIT_STATUS_READING) + self.update_stream(STREAM_DOWN, WAIT_STATUS_READING) + self._stage = STAGE_STREAM + except (OSError, IOError) as e: + if eventloop.errno_from_exception(e) == errno.EINPROGRESS: + self.update_stream(STREAM_UP, WAIT_STATUS_READWRITING) + self.update_stream(STREAM_DOWN, WAIT_STATUS_READING) + elif eventloop.errno_from_exception(e) == errno.ENOTCONN: + logging.error('fast open not supported on this OS') + self._config['fast_open'] = False + self.destroy() + else: + logging.error(e) + self.destroy() elif (is_local and self._stage == STAGE_HELLO) or \ (not is_local and self._stage == STAGE_INIT): try: @@ -259,18 +289,23 @@ class TCPRelayHandler(object): self._fd_to_handlers[remote_sock.fileno()] = self remote_sock.setblocking(False) remote_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) - # TODO support TCP fast open - try: - remote_sock.connect(sa) - except (OSError, IOError) as e: - if eventloop.errno_from_exception(e) == errno.EINPROGRESS: - pass - self._loop.add(remote_sock, - eventloop.POLL_ERR | eventloop.POLL_OUT) - self._stage = STAGE_REPLY - self.update_stream(STREAM_UP, WAIT_STATUS_READWRITING) - self.update_stream(STREAM_DOWN, WAIT_STATUS_READING) + if self._is_local and self._config['fast_open']: + # wait for more data to arrive and send them in one SYN + self._stage = STAGE_REPLY + # TODO when there is already data in this packet + else: + try: + remote_sock.connect(sa) + except (OSError, IOError) as e: + if eventloop.errno_from_exception(e) == \ + errno.EINPROGRESS: + pass + self._loop.add(remote_sock, + eventloop.POLL_ERR | eventloop.POLL_OUT) + self._stage = STAGE_REPLY + self.update_stream(STREAM_UP, WAIT_STATUS_READWRITING) + self.update_stream(STREAM_DOWN, WAIT_STATUS_READING) return except Exception: import traceback diff --git a/tests/fastopen.json b/tests/fastopen.json new file mode 100644 index 0000000..4f3da05 --- /dev/null +++ b/tests/fastopen.json @@ -0,0 +1,10 @@ +{ + "server":"127.0.0.1", + "server_port":8388, + "local_port":1081, + "password":"barfoo!", + "timeout":300, + "method":"aes-256-cfb", + "local_address":"127.0.0.1", + "fast_open":true +}