~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/chat.py

  • Committer: Matt Giuca
  • Date: 2009-03-24 06:50:39 UTC
  • mto: This revision was merged to the branch mainline in revision 1322.
  • Revision ID: matt.giuca@gmail.com-20090324065039-5c6xkjeb8x2f5d01
doc/conf.py: Renamed project from "ivle" to "IVLE". (Turns out this is a
    friendly name).

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
import cjson
23
23
import cStringIO
24
 
import hashlib
 
24
import md5
25
25
import sys
26
26
import os
27
27
import socket
28
28
import traceback
29
29
 
30
 
SOCKETTIMEOUT = 60
31
 
BLOCKSIZE = 1024
32
 
 
33
30
class Terminate(Exception):
34
 
    """Exception thrown when server is to be shut down. It will attempt to
35
 
    send the final_response to the client and then exits"""
 
31
    """Exception thrown when server is to be shut down. It will attempt to sned 
 
32
    the final_response to the client and then exits"""
36
33
    def __init__(self, final_response=None):
37
34
        self.final_response = final_response
38
35
 
39
36
    def __str__(self):
40
37
        return repr(self.final_response)
41
38
 
42
 
class ProtocolError(Exception):
43
 
    """Exception thrown when client violates the the chat protocol"""
44
 
    pass
45
39
 
46
40
def start_server(port, magic, daemon_mode, handler, initializer = None):
47
41
    # Attempt to open the socket.
87
81
 
88
82
    while True:
89
83
        (conn, addr) = s.accept()
90
 
        conn.settimeout(SOCKETTIMEOUT)
91
84
        try:
92
 
            # Grab the input and try to decode
93
 
            inp = recv_netstring(conn)
94
 
            try:
95
 
                content = decode(inp, magic)
96
 
            except ProtocolError:
 
85
            # Grab the input
 
86
            buf = cStringIO.StringIO()
 
87
            blk = conn.recv(1024)
 
88
            while blk:
 
89
                buf.write(blk)
 
90
                try:
 
91
                    blk = conn.recv(1024, socket.MSG_DONTWAIT)
 
92
                except:
 
93
                    # Exception thrown if it WOULD block (but we
 
94
                    # told it not to wait) - ie. we are done
 
95
                    blk = None
 
96
            inp = buf.getvalue()
 
97
            env = cjson.decode(inp)
 
98
            
 
99
            # Check that the message is 
 
100
            digest = md5.new(env['content'] + magic).digest().encode('hex')
 
101
            if env['digest'] != digest:
97
102
                conn.close()
98
103
                continue
99
104
 
 
105
            content = cjson.decode(env['content'])
 
106
 
100
107
            response = handler(content)
101
108
 
102
 
            send_netstring(conn, cjson.encode(response))
 
109
            conn.sendall(cjson.encode(response))
103
110
 
104
111
            conn.close()
105
112
 
106
113
        except Terminate, t:
107
114
            # Try and send final response and then terminate
108
115
            if t.final_response:
109
 
                send_netstring(conn, cjson.encode(t.final_response))
 
116
                conn.sendall(cjson.encode(t.final_response))
110
117
            conn.close()
111
118
            sys.exit(0)
112
119
        except Exception:
119
126
                "value": str(e_val),
120
127
                "traceback": tb_dump.getvalue()
121
128
            }
122
 
            send_netstring(conn, cjson.encode(json_exc))
 
129
            conn.sendall(cjson.encode(json_exc))
123
130
            conn.close()
124
131
 
125
132
 
126
133
def chat(host, port, msg, magic, decode = True):
127
134
    sok = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
128
135
    sok.connect((host, port))
129
 
    sok.settimeout(SOCKETTIMEOUT)
130
 
 
131
 
    json = encode(msg, magic)
132
 
 
133
 
    send_netstring(sok, json)
134
 
    inp = recv_netstring(sok)
135
 
 
 
136
    content = cjson.encode(msg)
 
137
    digest = md5.new(content + magic).digest().encode("hex")
 
138
    env = {'digest':digest,'content':content}
 
139
    sok.send(cjson.encode(env))
 
140
 
 
141
    buf = cStringIO.StringIO()
 
142
    blk = sok.recv(1024)
 
143
    while blk:
 
144
        buf.write(blk)
 
145
        try:
 
146
            blk = sok.recv(1024, socket.MSG_DONTWAIT)
 
147
        except socket.error, e:
 
148
            if e[0] != 11:
 
149
                raise
 
150
            # Exception thrown if it WOULD block (but we
 
151
            # told it not to wait) - ie. we are done
 
152
            blk = None
 
153
    inp = buf.getvalue()
136
154
    sok.close()
137
155
 
138
156
    if decode:
140
158
    else:
141
159
        return inp
142
160
 
143
 
def encode(message, magic):
144
 
    """Converts a message into a JSON serialisation and uses a magic
145
 
    string to attach a HMAC digest.
146
 
    """
147
 
    # XXX: Any reason that we double encode?
148
 
    content = cjson.encode(message)
149
 
 
150
 
    digest = hashlib.md5(content + magic).hexdigest()
151
 
    env = {'digest':digest,'content':content}
152
 
    json = cjson.encode(env)
153
 
 
154
 
    return json
155
 
 
156
 
 
157
 
def decode(message, magic):
158
 
    """Takes a message with an attached HMAC digest and validates the message.
159
 
    """
160
 
    msg = cjson.decode(message)
161
 
 
162
 
    # Check that the message is valid
163
 
    digest = hashlib.md5(msg['content'] + magic).hexdigest()
164
 
    if msg['digest'] != digest:
165
 
        raise ProtocolError("HMAC digest is invalid")
166
 
    content = cjson.decode(msg['content'])
167
 
 
168
 
    return content
169
 
 
170
 
 
171
 
def send_netstring(sok, data):
172
 
    """ Sends a netstring to a socket
173
 
    """
174
 
    netstring = "%d:%s,"%(len(data),data)
175
 
    sok.sendall(netstring)
176
 
 
177
 
 
178
 
def recv_netstring(sok):
179
 
    """ Attempts to recieve a Netstring from a socket.
180
 
    Throws a ProtocolError if the received data violates the Netstring 
181
 
    protocol.
182
 
    """
183
 
    # Decode netstring
184
 
    size_buffer = []
185
 
    c = sok.recv(1)
186
 
    while c != ':':
187
 
        # Limit the Netstring to less than 10^10 bytes (~1GB).
188
 
        if len(size_buffer) >= 10:
189
 
            raise ProtocolError(
190
 
                    "Could not read Netstring size in first 9 bytes: '%s'"%(
191
 
                    ''.join(size_buffer)))
192
 
        size_buffer.append(c)
193
 
        c = sok.recv(1)
194
 
    try:
195
 
        # Message should be length plus ',' terminator
196
 
        recv_expected = int(''.join(size_buffer)) + 1
197
 
    except ValueError, e:
198
 
        raise ProtocolError("Could not decode Netstring size as int: '%s'"%(
199
 
                ''.join(size_buffer)))
200
 
 
201
 
    # Read data
202
 
    buf = []
203
 
    recv_data = sok.recv(min(recv_expected, BLOCKSIZE))
204
 
    recv_size = len(recv_data)
205
 
    while recv_size < recv_expected:
206
 
        buf.append(recv_data)
207
 
        recv_data = sok.recv(min(recv_expected-recv_size, 1024))
208
 
        recv_size = recv_size + len(recv_data)
209
 
    assert(recv_size == recv_expected)
210
 
 
211
 
    # Did we receive the correct amount?
212
 
    if recv_data[-1] != ',':
213
 
        raise ProtocolError("Netstring did not end with ','")
214
 
    buf.append(recv_data[:-1])
215
 
 
216
 
    return ''.join(buf)