########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/FtRpc/Marshal.py,v 1.12 2005/03/22 09:57:31 mbrown Exp $
"""
RPC server protocol implementation

Code to marshal and unmarshal our little protocol's messages.
A message consists of:
  4 bytes for command/response code
  10 bytes for data length
  n bytes for data where n=length (cPickle)

Copyright 2005 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

import socket, cPickle

from Ft.Server.FtRpc import FtServerFtRpcException, MessageSource, Error

def Connect(userName, passwd, host, port):
    """Connect to an FtRpc Server """

    import Commands
    #Connect to the host on the specified port
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect((host, port))
    except socket.error:
        raise FtServerFtRpcException(Error.NO_SERVER,
                                     host=host,
                                     port=port)

    cmd = Commands.LoginCommand(userName, passwd)
    cmd.send(s)
    return s, '/'


def Marshal(cmd, data):
    """
    Turn a command and its data into a list of strings to be sent
    """
    cmdStr = "%04d" % cmd
    dataStr = cPickle.dumps(data, 1)
    lenStr = "%010d" % len(dataStr)
    return cmdStr + lenStr + dataStr


def Send(s, cmd, data):
    """Send a command (or a response)"""
    if s is None:
        #Assume that the transaction has already ended
        raise FtServerFtRpcException(Error.TRANSACTION_NOT_IN_PROGRESS)

    cmdStr = Marshal(cmd, data)
    s.sendall(cmdStr)
    return


def Receive(s):
    """Read in a response (or a command)"""
    res = ""
    try:
        rspCode = s.recv(4)
        try:
            rspCode = int(rspCode)
        except ValueError:
            #Usually means the server bombed!!!
            raise FtServerFtRpcException(Error.SERVER_COMMS_ERROR)

        dataSize = int(s.recv(10))
        oSiz = dataSize
        while len(res) != oSiz:
            res = res + s.recv(dataSize)
            dataSize = oSiz - len(res)
        #print res,

        data = cPickle.loads(res)
        #print "-" * 40
        #print repr(data)
    except:
        #Leave the pass-through until we can properly propagate server-side exceptions...
        raise
        #Server bombed
        raise FtServerFtRpcException(Error.SERVER_COMMS_ERROR)

    return rspCode, data

