# Path: twimp/handshake.py
# class Handshaker(object):
#     """The handshake protocol.
# 
#     Interaction should go something like this:
#       * cl: ()->v+CLHS sent->(v+SVHS rcvd)->SVHS sent->(CLHS rcvd) OK!
#       * sv: ()->(v rcvd)->v+SVHS sent->(CLHS rcvd)->CLHS sent->(SVHS rcvd) OK!
# 
#     where:
#       * v    - version packet
#       * SVHS - server handshake packet
#       * CLHS - client handshake packet
#     """
# 
#     packet_bytes = 1536
#     protocol_version = 3
# 
#     def __init__(self, protocol, epoch_base, is_client=False):
#         self.epoch = epoch_base
#         self.version_packet = _s_uchar.pack(self.protocol_version)
#         self.protocol = protocol
#         self.is_client = is_client
# 
#     def epoch_time(self):
#         return time.time() - self.epoch
# 
# 
#     def defaultHandshakeFailed(self, msg=''):
#         self.protocol.handshakeFailed()
#         # raising exception should cause closing the connection eventually
#         raise HandshakeFailedError(string=msg)
# 
# 
#     def version_supported(self, version):
#         return version == self.protocol_version
# 
#     def generate_request(self, context=None):
#         return (_s_ts_simpver.pack(ms_time(self.epoch_time()), 0) +
#                 generate_random_bytes(self.packet_bytes - 8))
# 
#     def generate_response(self, request):
#         return (buffer(request, 0, 4) +
#                 (buffer(_s_ulong.pack(ms_time(self.epoch_time()))) +
#                  buffer(request, 8)))
# 
#     def verify_response(self, request, response):
#         return (buffer(request, 0, 4) == buffer(response, 0, 4) and
#                 buffer(request, 8) == buffer(response, 8))
# 
#     def gen_handler(self):
#         p = self.protocol
# 
#         if self.is_client:
#             ts_init = time.time()
#             hs_mine = self.generate_request()
#             p.transport.writeSequence((self.version_packet, hs_mine))
# 
#         # wait for the "version packet"
#         s = yield 1
#         version_other, = _s_uchar.unpack(s.read(1))
# 
#         if not self.version_supported(version_other):
#             self.defaultHandshakeFailed('protocol version mismatch')
#             return
# 
#         # now, the primary handshake packet
#         s = yield self.packet_bytes
# 
#         hs_other = s.read(self.packet_bytes)
# 
#         if not self.is_client:
#             ts_init = time.time()
#             hs_mine = self.generate_request(context=hs_other)
#             p.transport.writeSequence((self.version_packet, hs_mine))
# 
#         p.transport.write(self.generate_response(hs_other))
# 
#         # and now, the handshake response
#         s = yield self.packet_bytes
# 
#         hs_response = buffer(s.read(self.packet_bytes))
#         ts_received_back = time.time()
# 
#         if not self.verify_response(hs_mine, hs_response):
#             self.defaultHandshakeFailed('response verification failed')
#             return
# 
#         p.handshakeSucceeded(ts_init - self.epoch, ts_received_back - ts_init)
# 
# class HandshakeFailedError(ConnectError):
#     """Handshake failed"""
# 
# Path: twimp/utils.py
# class GeneratorWrapperProtocol(protocol.Protocol):
#     def __init__(self, proto=None):
#         self._buf = vecbuf.VecBuf()
#         self._toread = 0
# 
#         self._handler_changed = False
# 
#         self._handler = proto
#         if proto:
#             self._toread = self._handler.send(None)
# 
#     def init_handler(self, proto=None, do_init=True):
#         if proto:
#             if self._handler:
#                 # need to indicate we should retry on StopIteration...
#                 self._handler_changed = True
#             self._handler = proto
#         if do_init:
#             self._toread = self._handler.send(None)
# 
#     def connectionLost(self, reason=protocol.connectionDone):
#         # clean up
#         self._buf.read(len(self._buf))
#         self._handler = None
#         self._toread = None
# 
#     def dataReceived(self, data):
#         self._buf.write(data)
# 
#         while 1: # need to be able to retry immediately when handlers change
#             try:
#                 while len(self._buf) >= self._toread:
#                     self._toread = self._handler.send(self._buf)
#                 break           # no data, no retry
#             except StopIteration:
#                 if self._handler_changed:
#                     self._handler_changed = False
#                 else:
#                     self._toread = None
#                     self._handler = None
#                     break
#             except:
#                 # undocumented, but real; this way we can signal
#                 # protocol errors directly, and by not passing/raising
#                 # directly we avoid a stack trace log, with the same
#                 # effect - connectionLost
#                 f = failure.Failure()
#                 log.info(f.value, exc_info=log.isEnabledFor(logging.DEBUG))
#                 return f


import struct
import time
from twisted.python import failure
from twisted.trial import unittest
from twimp.handshake import Handshaker, HandshakeFailedError
from twimp.utils import GeneratorWrapperProtocol
from helpers import StringTransport
        self.assertIdentical(err, None)
        self.assertFalse(tsrv.disconnecting, 'disconnecting')
        self.assertFalse(tcli.disconnecting, 'disconnecting')

        self.assertEquals(p_srv.handshake_status, 'ok')

    def test_server_side_ok(self):
        t = StringTransport()

        p = TestHandshakeProtocol()

        now = time.time()
        hs = Handshaker(p, now)

        p.init_handler(hs.gen_handler())

        p.makeConnection(t)
        # server shouldn't respond before spoken to
        self.assertEquals(t.value(), '')

        ver_pkt = struct.pack('B', self.protocol_version)

        err = p.dataReceived(ver_pkt)

        self.assertIdentical(err, None)

        # server may wait until it receives handshake packet before
        # responding, so not testing here

        hs_rand_data = '.' * (self.handshake_length - 8)

# Path: twimp/handshake.py
# class Handshaker(object):
#     """The handshake protocol.
# 
#     Interaction should go something like this:
#       * cl: ()->v+CLHS sent->(v+SVHS rcvd)->SVHS sent->(CLHS rcvd) OK!
#       * sv: ()->(v rcvd)->v+SVHS sent->(CLHS rcvd)->CLHS sent->(SVHS rcvd) OK!
# 
#     where:
#       * v    - version packet
#       * SVHS - server handshake packet
#       * CLHS - client handshake packet
#     """
# 
#     packet_bytes = 1536
#     protocol_version = 3
# 
#     def __init__(self, protocol, epoch_base, is_client=False):
#         self.epoch = epoch_base
#         self.version_packet = _s_uchar.pack(self.protocol_version)
#         self.protocol = protocol
#         self.is_client = is_client
# 
#     def epoch_time(self):
#         return time.time() - self.epoch
# 
# 
#     def defaultHandshakeFailed(self, msg=''):
#         self.protocol.handshakeFailed()
#         # raising exception should cause closing the connection eventually
#         raise HandshakeFailedError(string=msg)
# 
# 
#     def version_supported(self, version):
#         return version == self.protocol_version
# 
#     def generate_request(self, context=None):
#         return (_s_ts_simpver.pack(ms_time(self.epoch_time()), 0) +
#                 generate_random_bytes(self.packet_bytes - 8))
# 
#     def generate_response(self, request):
#         return (buffer(request, 0, 4) +
#                 (buffer(_s_ulong.pack(ms_time(self.epoch_time()))) +
#                  buffer(request, 8)))
# 
#     def verify_response(self, request, response):
#         return (buffer(request, 0, 4) == buffer(response, 0, 4) and
#                 buffer(request, 8) == buffer(response, 8))
# 
#     def gen_handler(self):
#         p = self.protocol
# 
#         if self.is_client:
#             ts_init = time.time()
#             hs_mine = self.generate_request()
#             p.transport.writeSequence((self.version_packet, hs_mine))
# 
#         # wait for the "version packet"
#         s = yield 1
#         version_other, = _s_uchar.unpack(s.read(1))
# 
#         if not self.version_supported(version_other):
#             self.defaultHandshakeFailed('protocol version mismatch')
#             return
# 
#         # now, the primary handshake packet
#         s = yield self.packet_bytes
# 
#         hs_other = s.read(self.packet_bytes)
# 
#         if not self.is_client:
#             ts_init = time.time()
#             hs_mine = self.generate_request(context=hs_other)
#             p.transport.writeSequence((self.version_packet, hs_mine))
# 
#         p.transport.write(self.generate_response(hs_other))
# 
#         # and now, the handshake response
#         s = yield self.packet_bytes
# 
#         hs_response = buffer(s.read(self.packet_bytes))
#         ts_received_back = time.time()
# 
#         if not self.verify_response(hs_mine, hs_response):
#             self.defaultHandshakeFailed('response verification failed')
#             return
# 
#         p.handshakeSucceeded(ts_init - self.epoch, ts_received_back - ts_init)
# 
# class HandshakeFailedError(ConnectError):
#     """Handshake failed"""
# 
# Path: twimp/utils.py
# class GeneratorWrapperProtocol(protocol.Protocol):
#     def __init__(self, proto=None):
#         self._buf = vecbuf.VecBuf()
#         self._toread = 0
# 
#         self._handler_changed = False
# 
#         self._handler = proto
#         if proto:
#             self._toread = self._handler.send(None)
# 
#     def init_handler(self, proto=None, do_init=True):
#         if proto:
#             if self._handler:
#                 # need to indicate we should retry on StopIteration...
#                 self._handler_changed = True
#             self._handler = proto
#         if do_init:
#             self._toread = self._handler.send(None)
# 
#     def connectionLost(self, reason=protocol.connectionDone):
#         # clean up
#         self._buf.read(len(self._buf))
#         self._handler = None
#         self._toread = None
# 
#     def dataReceived(self, data):
#         self._buf.write(data)
# 
#         while 1: # need to be able to retry immediately when handlers change
#             try:
#                 while len(self._buf) >= self._toread:
#                     self._toread = self._handler.send(self._buf)
#                 break           # no data, no retry
#             except StopIteration:
#                 if self._handler_changed:
#                     self._handler_changed = False
#                 else:
#                     self._toread = None
#                     self._handler = None
#                     break
#             except:
#                 # undocumented, but real; this way we can signal
#                 # protocol errors directly, and by not passing/raising
#                 # directly we avoid a stack trace log, with the same
#                 # effect - connectionLost
#                 f = failure.Failure()
#                 log.info(f.value, exc_info=log.isEnabledFor(logging.DEBUG))
#                 return f


# Path: test/test_handshake.py
import struct
import time
from twisted.python import failure
from twisted.trial import unittest
from twimp.handshake import Handshaker, HandshakeFailedError
from twimp.utils import GeneratorWrapperProtocol
from helpers import StringTransport

        self.assertIdentical(err, None)
        self.assertFalse(tsrv.disconnecting, 'disconnecting')
        self.assertFalse(tcli.disconnecting, 'disconnecting')

        self.assertEquals(p_srv.handshake_status, 'ok')

    def test_server_side_ok(self):
        t = StringTransport()

        p = TestHandshakeProtocol()

        now = time.time()
        hs = Handshaker(p, now)

        p.init_handler(hs.gen_handler())

        p.makeConnection(t)
        # server shouldn't respond before spoken to
        self.assertEquals(t.value(), '')

        ver_pkt = struct.pack('B', self.protocol_version)

        err = p.dataReceived(ver_pkt)

        self.assertIdentical(err, None)

        # server may wait until it receives handshake packet before
        # responding, so not testing here

        hs_rand_data = '.' * (self.handshake_length - 8)

        timestamp = 42