Trying to build a proxy with aiohttp - python

I'm trying to write a forwarding http proxy with aiohttp, I've currently got it working with http but wanting it to work with https(without decryption).
import asyncio
from aiohttp import web, ClientSession
loop = asyncio.get_event_loop()
async def handler(server_request):
if server_request.method == "CONNECT":
print(await server_request.read())
else:
async with ClientSession() as session:
async with session.request(server_request.method, server_request.raw_path) as request:
response = web.StreamResponse(status=200,
reason='OK',
headers={'Content-Type': 'text/html'})
await response.prepare(server_request)
while True:
chunk = await request.content.read()
if not chunk:
break
response.write(chunk)
return response
server = web.Server(handler)
loop.run_until_complete(loop.create_server(server, "0.0.0.0", 8080))
try:
loop.run_forever()
except KeyboardInterrupt:
loop.close()
pass
I've got to the point where I need to get the raw body to send down the tunnel to the destination but can't seem to access it
If I attempt to read I get an exception:
b''
Unhandled exception
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/aiohttp/web_protocol.py", line 434, in start
yield from resp.prepare(request)
AttributeError: 'NoneType' object has no attribute 'prepare'

The basic idea in a HTTPS proxy is to act like a stream proxy, where we read raw data and put raw data to destination. Unfortunately after skimming through aiohttp I could not find anything which would help achieve the same, so you need to us basic asyncio.
Below code snippets shows the main logic behind the https part
if head[0] == 'CONNECT': # https proxy
try:
logger.info('%sBYPASSING <%s %s> (SSL connection)' %
('[%s] ' % ident if verbose >= 1 else '', head[0], head[1]))
m = REGEX_HOST.search(head[1])
host = m.group(1)
port = int(m.group(2))
req_reader, req_writer = yield from asyncio.open_connection(host, port, ssl=False, loop=loop)
client_writer.write(b'HTTP/1.1 200 Connection established\r\n\r\n')
#asyncio.coroutine
def relay_stream(reader, writer):
try:
while True:
line = yield from reader.read(1024)
if len(line) == 0:
break
writer.write(line)
except:
print_exc()
tasks = [
asyncio.async(relay_stream(client_reader, req_writer), loop=loop),
asyncio.async(relay_stream(req_reader, client_writer), loop=loop),
]
yield from asyncio.wait(tasks, loop=loop)
except:
print_exc()
finally:
return
The complete PROXY code is as below
#!/usr/bin/env python3
VERSION = "v0.2.0"
"""
Copyright (c) 2013 devunt
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
"""
import sys
if sys.version_info < (3, 4):
print('Error: You need python 3.4.0 or above.')
exit(1)
from argparse import ArgumentParser
from socket import TCP_NODELAY
from time import time
from traceback import print_exc
import asyncio
import logging
import random
import functools
import re
REGEX_HOST = re.compile(r'(.+?):([0-9]{1,5})')
REGEX_CONTENT_LENGTH = re.compile(r'\r\nContent-Length: ([0-9]+)\r\n', re.IGNORECASE)
REGEX_CONNECTION = re.compile(r'\r\nConnection: (.+)\r\n', re.IGNORECASE)
clients = {}
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] {%(levelname)s} %(message)s')
logging.getLogger('asyncio').setLevel(logging.CRITICAL)
logger = logging.getLogger('warp')
verbose = 0
def accept_client(client_reader, client_writer, *, loop=None):
ident = hex(id(client_reader))[-6:]
task = asyncio.async(process_warp(client_reader, client_writer, loop=loop), loop=loop)
clients[task] = (client_reader, client_writer)
started_time = time()
def client_done(task):
del clients[task]
client_writer.close()
logger.debug('[%s] Connection closed (took %.5f seconds)' % (ident, time() - started_time))
logger.debug('[%s] Connection started' % ident)
task.add_done_callback(client_done)
#asyncio.coroutine
def process_warp(client_reader, client_writer, *, loop=None):
ident = str(hex(id(client_reader)))[-6:]
header = ''
payload = b''
try:
RECV_MAX_RETRY = 3
recvRetry = 0
while True:
line = yield from client_reader.readline()
if not line:
if len(header) == 0 and recvRetry < RECV_MAX_RETRY:
# handle the case when the client make connection but sending data is delayed for some reasons
recvRetry += 1
yield from asyncio.sleep(0.2, loop=loop)
continue
else:
break
if line == b'\r\n':
break
if line != b'':
header += line.decode()
m = REGEX_CONTENT_LENGTH.search(header)
if m:
cl = int(m.group(1))
while (len(payload) < cl):
payload += yield from client_reader.read(1024)
except:
print_exc()
if len(header) == 0:
logger.debug('[%s] !!! Task reject (empty request)' % ident)
return
req = header.split('\r\n')[:-1]
if len(req) < 4:
logger.debug('[%s] !!! Task reject (invalid request)' % ident)
return
head = req[0].split(' ')
if head[0] == 'CONNECT': # https proxy
try:
logger.info('%sBYPASSING <%s %s> (SSL connection)' %
('[%s] ' % ident if verbose >= 1 else '', head[0], head[1]))
m = REGEX_HOST.search(head[1])
host = m.group(1)
port = int(m.group(2))
req_reader, req_writer = yield from asyncio.open_connection(host, port, ssl=False, loop=loop)
client_writer.write(b'HTTP/1.1 200 Connection established\r\n\r\n')
#asyncio.coroutine
def relay_stream(reader, writer):
try:
while True:
line = yield from reader.read(1024)
if len(line) == 0:
break
writer.write(line)
except:
print_exc()
tasks = [
asyncio.async(relay_stream(client_reader, req_writer), loop=loop),
asyncio.async(relay_stream(req_reader, client_writer), loop=loop),
]
yield from asyncio.wait(tasks, loop=loop)
except:
print_exc()
finally:
return
phost = False
sreq = []
sreqHeaderEndIndex = 0
for line in req[1:]:
headerNameAndValue = line.split(': ', 1)
if len(headerNameAndValue) == 2:
headerName, headerValue = headerNameAndValue
else:
headerName, headerValue = headerNameAndValue[0], None
if headerName.lower() == "host":
phost = headerValue
elif headerName.lower() == "connection":
if headerValue.lower() in ('keep-alive', 'persist'):
# current version of this program does not support the HTTP keep-alive feature
sreq.append("Connection: close")
else:
sreq.append(line)
elif headerName.lower() != 'proxy-connection':
sreq.append(line)
if len(line) == 0 and sreqHeaderEndIndex == 0:
sreqHeaderEndIndex = len(sreq) - 1
if sreqHeaderEndIndex == 0:
sreqHeaderEndIndex = len(sreq)
m = REGEX_CONNECTION.search(header)
if not m:
sreq.insert(sreqHeaderEndIndex, "Connection: close")
if not phost:
phost = '127.0.0.1'
path = head[1][len(phost)+7:]
logger.info('%sWARPING <%s %s>' % ('[%s] ' % ident if verbose >= 1 else '', head[0], head[1]))
new_head = ' '.join([head[0], path, head[2]])
m = REGEX_HOST.search(phost)
if m:
host = m.group(1)
port = int(m.group(2))
else:
host = phost
port = 80
try:
req_reader, req_writer = yield from asyncio.open_connection(host, port, flags=TCP_NODELAY, loop=loop)
req_writer.write(('%s\r\n' % new_head).encode())
yield from req_writer.drain()
yield from asyncio.sleep(0.2, loop=loop)
def generate_dummyheaders():
def generate_rndstrs(strings, length):
return ''.join(random.choice(strings) for _ in range(length))
import string
return ['X-%s: %s\r\n' % (generate_rndstrs(string.ascii_uppercase, 16),
generate_rndstrs(string.ascii_letters + string.digits, 128)) for _ in range(32)]
req_writer.writelines(list(map(lambda x: x.encode(), generate_dummyheaders())))
yield from req_writer.drain()
req_writer.write(b'Host: ')
yield from req_writer.drain()
def feed_phost(phost):
i = 1
while phost:
yield random.randrange(2, 4), phost[:i]
phost = phost[i:]
i = random.randrange(2, 5)
for delay, c in feed_phost(phost):
yield from asyncio.sleep(delay / 10.0, loop=loop)
req_writer.write(c.encode())
yield from req_writer.drain()
req_writer.write(b'\r\n')
req_writer.writelines(list(map(lambda x: (x + '\r\n').encode(), sreq)))
req_writer.write(b'\r\n')
if payload != b'':
req_writer.write(payload)
req_writer.write(b'\r\n')
yield from req_writer.drain()
try:
while True:
buf = yield from req_reader.read(1024)
if len(buf) == 0:
break
client_writer.write(buf)
except:
print_exc()
except:
print_exc()
client_writer.close()
#asyncio.coroutine
def start_warp_server(host, port, *, loop = None):
try:
accept = functools.partial(accept_client, loop=loop)
server = yield from asyncio.start_server(accept, host=host, port=port, loop=loop)
except OSError as ex:
logger.critical('!!! Failed to bind server at [%s:%d]: %s' % (host, port, ex.args[1]))
raise
else:
logger.info('Server bound at [%s:%d].' % (host, port))
return server
def main():
"""CLI frontend function. It takes command line options e.g. host,
port and provides `--help` message.
"""
parser = ArgumentParser(description='Simple HTTP transparent proxy')
parser.add_argument('-H', '--host', default='127.0.0.1',
help='Host to listen [default: %(default)s]')
parser.add_argument('-p', '--port', type=int, default=8800,
help='Port to listen [default: %(default)d]')
parser.add_argument('-v', '--verbose', action='count', default=0,
help='Print verbose')
args = parser.parse_args()
if not (1 <= args.port <= 65535):
parser.error('port must be 1-65535')
if args.verbose >= 3:
parser.error('verbose level must be 1-2')
if args.verbose >= 1:
logger.setLevel(logging.DEBUG)
if args.verbose >= 2:
logging.getLogger('warp').setLevel(logging.DEBUG)
logging.getLogger('asyncio').setLevel(logging.DEBUG)
global verbose
verbose = args.verbose
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(start_warp_server(args.host, args.port))
loop.run_forever()
except OSError:
pass
except KeyboardInterrupt:
print('bye')
finally:
loop.close()
if __name__ == '__main__':
exit(main())
PS: The code has been taken from https://github.com/devunt/warp. The complete code is posted here so that if the link becomes invalid in future, the answer is still valid.

Related

Client seems to hang after a specific reply from server

I am implementing an authentication process between a server and a client in order to exchange some information. I have created a function called serverToClientAuth on both server and client that carries out a basic back and forth communication to establish authenticated integrity checks of each other before exchanging some data.
When I run the server, followed by the client, the exchange occurs perfectly with encryption and hashing working as required but on the very last message from the server to the Client, the client seems to hang and is unable to exit the function and go back to normal operation Although the first 3 communications work fine, the last one does not for some reason.
I thought at first that it had something to do with the encryption/Decryption functions but as the first 3 operations work, I don't see why the last doesn't? It seems that the recieve_msg function is grabbing the last message instead of the authentication function, this is the final '12' output on the client window as seen below.
I have included both the server and client as well as the output of each below. If anyone has any idea as to why this hang is occurring, it would be greatly appreciated.
#Server.py
from socket import AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR, socket
from threading import Thread
from signal import signal, SIGINT
import sys
import os
import random
import rsa
WELCOME_MSG = 'Server is active and awaiting connections'
def nonceGenerator():
num = ""
for i in range(5):
rand = random.randint(0,1)
num += str(rand)
return num
def generateAKeys():
(publicKey, privateKey) = rsa.newkeys(1024)
with open('keys/APubKey.pem', 'wb') as p:
p.write(publicKey.save_pkcs1('PEM'))
with open('keys/APrivKey.pem', 'wb') as p:
p.write(privateKey.save_pkcs1('PEM'))
def generateBKeys():
(publicKey, privateKey) = rsa.newkeys(1024)
with open('keys/BPubKey.pem', 'wb') as p:
p.write(publicKey.save_pkcs1('PEM'))
with open('keys/BPrivKey.pem', 'wb') as p:
p.write(privateKey.save_pkcs1('PEM'))
def generateCKeys():
(publicKey, privateKey) = rsa.newkeys(1024)
with open('keys/CPubKey.pem', 'wb') as p:
p.write(publicKey.save_pkcs1('PEM'))
with open('keys/CPrivKey.pem', 'wb') as p:
p.write(privateKey.save_pkcs1('PEM'))
def generateSKeys():
(publicKey, privateKey) = rsa.newkeys(1024)
with open('keys/SPubKey.pem', 'wb') as p:
p.write(publicKey.save_pkcs1('PEM'))
with open('keys/SPrivKey.pem', 'wb') as p:
p.write(privateKey.save_pkcs1('PEM'))
def loadKeys():
with open('keys/SPubKey.pem', 'rb') as p:
SPubKey = rsa.PublicKey.load_pkcs1(p.read())
with open('keys/SPrivKey.pem', 'rb') as p:
SPrivKey = rsa.PrivateKey.load_pkcs1(p.read())
with open('keys/APubKey.pem', 'rb') as p:
APubKey = rsa.PublicKey.load_pkcs1(p.read())
with open('keys/APrivKey.pem', 'rb') as p:
APrivKey = rsa.PrivateKey.load_pkcs1(p.read())
with open('keys/BPubKey.pem', 'rb') as p:
BPubKey = rsa.PublicKey.load_pkcs1(p.read())
with open('keys/BPrivKey.pem', 'rb') as p:
BPrivKey = rsa.PrivateKey.load_pkcs1(p.read())
with open('keys/CPubKey.pem', 'rb') as p:
CPubKey = rsa.PublicKey.load_pkcs1(p.read())
with open('keys/CPrivKey.pem', 'rb') as p:
CPrivKey = rsa.PrivateKey.load_pkcs1(p.read())
return SPubKey, SPrivKey, APubKey, APrivKey, BPubKey, BPrivKey, CPubKey, CPrivKey
def encrypt(message, key):
return rsa.encrypt(message.encode('utf8'), key)
def decrypt(ciphertext, key):
try:
return rsa.decrypt(ciphertext, key).decode('utf8')
except:
return False
def sign(message, key):
return rsa.sign(message.encode('utf8'), key, 'SHA-1')
def verify(message, signature, key):
try:
return rsa.verify(message.encode('utf8'), signature, key,) == 'SHA-1'
except:
return False
def incoming_connections():
while True:
client, addr = SERVER.accept()
print(f'A client has connected {addr}')
# client.send(WELCOME_MSG.encode())
Thread(target=single_client, args=(client,)).start()
print('Client connected')
def single_client(client):
client_name = client.recv(BUFFERSIZE).decode()
#client_name = 'Anonymous'
welcome_msg = f'Welcome {client_name}.\nType exit() or press CTRL+D or CTRL+C to exit.\n'
client.send(welcome_msg.encode())
chat_msg = f'{client_name} has joined the room'
broadcast_msg(chat_msg.encode())
clients[client] = client_name
serverToClientAuth(client) #Begin Authentication process upon reciving client
while True:
msg = client.recv(BUFFERSIZE)
if msg == 'online()'.encode('utf8'):
real_clients_num, real_clients_name = get_clients()
client.send(f'Online users {real_clients_num} : {real_clients_name}'.encode('utf8'))
elif msg == EXIT_CMD.encode('utf8'):
print(f'{clients[client]} has disconnected ')
client.send('You are leaving the room...'.encode())
client.close()
client_leaving = clients[client]
del clients[client]
broadcast_msg(f'{client_leaving} has left the room!'.encode())
break
elif '#'.encode('utf8') in msg:
unicast_msg(msg, client)
else:
broadcast_msg(msg, clients[client] + ': ')
def get_clients():
real_clients_num = 0
real_clients_name = []
for k,v in clients.items():
if v != 'Anonymous':
real_clients_num += 1
real_clients_name.append(v)
return real_clients_num, real_clients_name
def broadcast_msg(msg, name=""):
for client in clients:
client.send(name.encode() + msg)
def unicast_msg(msg, client):
# Get only name by removing #
msg = msg.decode('utf8')
refered_client, client_msg = msg.split(' ',1)
client_to_connect = refered_client.strip('#')
for k,v in clients.items():
if v == client_to_connect:
k.send(f'{clients[client]} -> {client_to_connect}: {client_msg}'.encode('utf8'))
def receive_msg():
while True:
try:
msg = client_socket.recv(BUFFERSIZE).decode("utf8")
print(msg)
except OSError as error:
return error
def serverToClientAuth(client):
Ka=bytes('1', 'utf8')
Kb=bytes('2', 'utf8')
Kc=3
user = clients[client] #A, B or C
message1 = client.recv(1024) #Nonce Na, Nb or Nc
NEnc, sig1 = message1[:128], message1[128:]
N = decrypt(NEnc, SPrivKey)
if verify(N, sig1, APubKey):
Ns = nonceGenerator() #Servers Nonce
NNs = "{}|{}".format(N, Ns)
NNsEnc = encrypt(NNs, APubKey)
sig2 = sign(NNs, SPrivKey)
message2 = NNsEnc + sig2
client.send(message2)
message3 = client.recv(1024)
NsXX = decrypt(message3, SPrivKey)
print(NsXX)
NsXX = NsXX.split("|")
if NsXX[0] == Ns: #Does the Nonce that client returned match what was sent?
if NsXX[1] == 'B' and NsXX[2] == 'C':
message4 = Ka + Kb
client.send(message4)
print("Client " + user + " has been authenticated to the server\n")
elif NsXX[1] == 'A' and NsXX[2] == 'C':
message4 = "{}|{}".format(Ka, Kc)
client.send(message4.encode('utf8'))
print("Client " + user + " has been authenticated to the server\n")
elif NsXX[1] == 'A' and NsXX[2] == 'B':
message4 = "{}|{}".format(Ka, Kb)
client.send(message4.encode('utf8'))
print("Client " + user + " has been authenticated to the server\n")
else:
print("Unrecognised identifier")
else:
print("Replied nonce from Client does not match")
else:
print('The message signature could not be verified')
if __name__ == "__main__":
clients = {}
generateAKeys()
generateBKeys()
generateCKeys()
generateSKeys()
SPubKey, SPrivKey, APubKey, APrivKey, BPubKey, BPrivKey, CPubKey, CPrivKey = loadKeys()
HOST = '127.0.0.1'
PORT = 5000
BUFFERSIZE = 1024
ADDR = (HOST, PORT)
EXIT_CMD = "exit()"
SERVER = socket(AF_INET, SOCK_STREAM)
SERVER.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
SERVER.bind(ADDR)
SERVER.listen(2)
print("Waiting for connection...")
ACCEPT_THREAD = Thread(target=incoming_connections)
ACCEPT_THREAD.start()
ACCEPT_THREAD.join()
SERVER.close()
The server file ^^^
#Client A
from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
from signal import signal, SIGINT
import sys
import os
import random
import rsa
def nonceGenerator():
num = ""
for i in range(5):
rand = random.randint(0,1)
num += str(rand)
return num
def loadKeys():
with open('keys/APubKey.pem', 'rb') as p:
APubKey = rsa.PublicKey.load_pkcs1(p.read())
with open('keys/APrivKey.pem', 'rb') as p:
APrivKey = rsa.PrivateKey.load_pkcs1(p.read())
with open('keys/SPubKey.pem', 'rb') as p:
SPubKey = rsa.PublicKey.load_pkcs1(p.read())
return APubKey, APrivKey, SPubKey
def encrypt(message, key):
return rsa.encrypt(message.encode('utf8'), key)
def decrypt(ciphertext, key):
try:
return rsa.decrypt(ciphertext, key).decode('utf8')
except:
return False
def sign(message, key):
return rsa.sign(message.encode('utf8'), key, 'SHA-1')
def verify(message, signature, key):
try:
return rsa.verify(message.encode('utf8'), signature, key,) == 'SHA-1'
except:
return False
def receive_msg():
while True:
try:
msg = client_socket.recv(BUFFERSIZE).decode("utf8")
print(msg)
except OSError as error:
return error
def keyBoard_Input():
while True:
try:
msg = input()
if msg != 'exit()':
client_socket.send(msg.encode('utf8'))
else:
clean_exit()
except EOFError:
clean_exit()
def send_msg(data):
try:
msg = data
if msg != 'exit()':
client_socket.send(msg.encode('utf8'))
else:
clean_exit()
except EOFError:
clean_exit()
def clean_exit():
client_socket.send('exit()'.encode('utf8'))
client_socket.close()
sys.exit(0)
def handler(signal_received, frame):
clean_exit()
def serverToClientAuth(client):
Na = nonceGenerator()
NaEnc = encrypt(Na, SPubKey)
sig1 = sign(Na, APrivKey)
message1 = NaEnc + sig1
client_socket.send(message1)
message2 = client_socket.recv(1024)
NaNsEnc, sig2 = message2[:128], message2[128:]
NaNs = decrypt(NaNsEnc, APrivKey)
if verify(NaNs, sig2, SPubKey):
NaNs = NaNs.split("|")
if NaNs[0] == Na:
Ns = NaNs[1]
print(Ns)
NsBC = "{}|{}|{}".format(Ns, 'B', 'C')
NsBCEnc = encrypt(NsBC, SPubKey) #No signature needed anymore
message3 = NsBCEnc
client_socket.send(message3)
print('Client gets stuick here')
message4 = client_socket.recv(1024)
print(message4)
message4 = message4.split("|")
APubKey, CPubKey = message3[0], message3[1]
print("Client B is Authenticated to the Server")
print("Ka = " + APubKey)
print("Kc = " + CPubKey)
else:
print("Replied nonce from server does not match")
else:
print('The message signature could not be verified')
if __name__ == '__main__':
signal(SIGINT, handler)
HOST = '127.0.0.1'
PORT = 5000
BUFFERSIZE = 1024
ADDR = (HOST, PORT)
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(ADDR)
receive_thread = Thread(target=receive_msg)
receive_thread.start()
APubKey, APrivKey, SPubKey = loadKeys()
send_msg('A') #Assign Client ID at the moment the connection is initiated
serverToClientAuth(client_socket)
keyBoard_Input()
Client File ^^^^
Waiting for connection...
A client has connected ('127.0.0.1', 51407)
Client connected
01001|B|C
Client A has been authenticated to the server
Server response ^^^
Welcome A.
Type exit() or press CTRL+D or CTRL+C to exit.
01001
Client gets stuick here
12
Client Response ^^^
The '12' displayed here should be a '1|2' as that is the message sent by the server. However, it seems that the receive_msg function is actually taking the message instead of the function, causing the function to hang and wait for a message.

Server/Client app and JSONDecodeError: Unterminated string python

I have async socket server file and client file.
When i send something like that "download filename.ex" to the client, this client's code hande my request:
try:
content = read(sp_data[-1]).decode('utf-8')
print(content)
msg = json.dumps({'file': sp_data[-1], 'command': data, 'content': content,
'msg': f'[+] File {sp_data[-1]} has been successfully downloaded.'}).encode('utf-8')
except FileNotFoundError:
msg = json.dumps({'msg': f'[-] File {sp_data[-1]} not found', 'command': data}).encode('utf-8')
s.send(msg)
When client send some data to the socketserver, this server's code handle received message:
def recv_message(client_socket):
global messages
data = json.loads(client_socket.recv(4096).decode('utf-8').strip()) ##Important here i got this error json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)
raddr = get_raddr(str(client_socket))
raddr = f'{raddr[0]}:{raddr[1]}'
message = f'From: {raddr}\nCommand: {data["command"]}\nOutput: \n\n{data["msg"]}'
try:
d = messages[raddr]
d.append(message)
messages[raddr] = d
except KeyError:
messages[raddr] = [message]
except AttributeError:
print(message, messages)
if 'content' in data.keys(): ##Important
print(data['content'])
threading.Thread(target=create_file, args=(data['file'], data['content'],), daemon=False).start()
Error:
data = json.loads(client_socket.recv(4096).decode('utf-8').strip())
json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)
But server's code above give me this error when it receive message from the first code(when i send something like that "download file.ex" to the client, client detect my command as its special command, execute the first code, send json file to the server. But if i send "dir" command to the client, it will detect my command like shell command, will run command through subprocess, will send result to the server back and i won't get any errors.)
Note: I also reduced socketserver's code. Therefore, something in my code can work worse. The main goal of this post - make download feature works. I also understand that my code is big. I left "##Important" comments in my files. U can watch only code that located by these comments.
Server:
import selectors
import socket
import threading
import json
import base64
import shlex
selector = selectors.DefaultSelector()
connections = {}
def accept_conn(server_socket):
sock, addr = server_socket.accept()
connections[len(connections) + 1] = [sock, f'{addr[0]}:{addr[-1]}']
selector.register(fileobj=sock, events=selectors.EVENT_READ, data=recv_message)
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('localhost', 4444))
s.listen()
selector.register(fileobj=s, events=selectors.EVENT_READ, data=accept_conn)
messages = {}
##Important
def create_file(file, content): #content - base64 string
print(content)
with open(file, 'wb') as f:
f.write(base64.b64decode(content.encode('utf-8')))
def recv_message(client_socket):
global messages
data = json.loads(client_socket.recv(4096).decode('utf-8').strip()) ##Important here i got this error json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 67 (char 66)
raddr = get_raddr(str(client_socket))
raddr = f'{raddr[0]}:{raddr[1]}'
message = f'From: {raddr}\nCommand: {data["command"]}\nOutput: \n\n{data["msg"]}'
try:
d = messages[raddr]
d.append(message)
messages[raddr] = d
except KeyError:
messages[raddr] = [message]
except AttributeError:
print(message, messages)
if 'content' in data.keys(): ##Important
print(data['content'])
threading.Thread(target=create_file, args=(data['file'], data['content'],), daemon=False).start()
def get_raddr(string):
'''Get raddr parameter from client socket'''
raddr = string.replace('>', '')
return eval(raddr[raddr.find('raddr')::].replace('raddr=', ''))
def is_manage_string(sub, string):
tokens = shlex.split(string)
try:
if len(tokens) == 2 and tokens[0] == sub and str(int(tokens[-1])):
return True, int(tokens[-1])
except Exception as e:
print(e)
return False
manage_process = False
def manage():
global manage_process
while True:
manage_process = False
command = input('>>> ').strip()
if command == 'list':
try:
for i in range(1, len(connections) + 1):
print(f'{i}\t{connections[i][-1]}')
except KeyError:
pass
if len(connections) == 0:
print('[-] There are not any connections')
elif 'manage' in command:
index = is_manage_string('manage', command)
if index:
index = index[-1]
else:
print('[-] Invalid command\nUse manage "number_of_connection"\nEx: manage 1')
continue
if index >= 1 and index <= len(connections):
sock, addr = connections[index]
print(addr)
print(f'{addr} is used')
while True: ##Important here i launch loop which send data to socket
manage_process = True
command = input('>>> ').strip()
if command == 'messages':
try:
if messages[addr] == list():
print()
continue
except KeyError:
pass
try:
print('\n\n'.join(messages[addr]))
except KeyError:
print()
elif command == 'message':
try:
print(messages[addr][-1])
except:
print()
elif command == 'clear_messages':
try:
if messages[addr]:
messages[addr] = []
except KeyError:
print('[-] There are not any messages for cleaning up')
elif command == 'leave':
print(f'Leaving connection {addr}')
break
elif command: ##Important if command hasn't been detected as my special command(leave, messages), it will be executed like shell command
try:
sock.send(command.encode('utf-8'))
print(
'Your input has not been detected as special command and will execute like shell command or like client special command(ex: download; see client file)')
except ConnectionResetError:
print("Connection has been lost, therefore shell commands can't be used")
else:
continue
else:
print('[-] Invalid number of connection')
elif command:
print('[-] Invalid command\nType "help" to see avalible commands')
##Important
def event_loop():
while True:
data = selector.select()
for key, _ in data:
try:
key.data(key.fileobj)
except ConnectionResetError:
selector.unregister(key.fileobj)
##Important
threading.Thread(target=manage, daemon=True).start()
event_loop()
Client:
import socket
import subprocess
import shlex
import threading
import json
import base64
s = socket.socket()
s.connect(('localhost', 4444))
##Important
def read(file):
with open(file, 'rb') as f:
return base64.b64encode(f.read())
def runner(data):
sp_data = shlex.split(data)
try:
print(sp_data)
if len(sp_data) == 2 and sp_data[0] == 'download': ###Important here we create json object which will be send to socketserver
try:
content = read(sp_data[-1]).decode('utf-8')
print(content)
msg = json.dumps({'file': sp_data[-1], 'command': data, 'content': content,
'msg': f'[+] File {sp_data[-1]} has been successfully downloaded.'}).encode('utf-8')
except FileNotFoundError:
msg = json.dumps({'msg': f'[-] File {sp_data[-1]} not found', 'command': data}).encode('utf-8')
s.send(msg)
return ''
except Exception as e:
print(e)
command = subprocess.run(data, shell=True, encoding='cp866', text=True, capture_output=True)
command = command.stderr if command.stderr else command.stdout
command = json.dumps({'msg': command, 'command': data})
s.send(command.encode('utf-8'))
while True:##Important
data = s.recv(4096).decode('utf-8').strip()
threading.Thread(target=runner, args=(data,)).start()
import socket
import struct
class Socket(socket.socket):
def __init__(self):
self.sock = socket.socket()
super().__init__(socket.AF_INET, socket.SOCK_STREAM)
def send_msg(self, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
self.sock.sendall(msg)
def recv_msg(self):
# Read message length and unpack it into an integer
raw_msglen = self.recv_all(4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return self.recv_all(msglen)
def recv_all(self, n):
data = bytearray()
while len(data) < n:
packet = self.sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data
I reshaped ur code into Socket class.
Solution - usage these features:
def send_msg(sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recv_msg(sock):
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
def recvall(sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = bytearray()
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data.extend(packet)
return data

Designing a reverse SOCKS relay in Python

I have the following situation:
WWW <- ServerBehindNAT(C) -> (S)MyApp(S) <- (C)BrowserUser
C == Client connection.
S == Server connection.
ServerBehindNAT represents a node that connects via SOCKS to MyApp on my server.
BrowserUser represents a client browser with SOCKS proxy set as IP of MyApp.
MyApp represents this script which accepts incoming connection from BrowserUser, accepts incoming connection from ServerBehindNAT, and sends requests from BrowserUser through to ServerBehindNAT.
WWW represents the external Internet/www.
I have created a multi-threaded SOCKS server which listens on 2 ports. What I'm having trouble designing is the part where it forwards requests from BrowserUser through to ServerBehindNAT:
(S)<--(S)
The first code I had:
import logging
import select
import socket
import struct
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler, BaseRequestHandler
import threading
import mysql.connector
logging.basicConfig(level=logging.DEBUG)
SOCKS_VERSION = 5
def sql_connect():
""" database connection"""
try:
sql = mysql.connector.connect(host='localhost',
database='prox_main232',
user='prox_main551',
password='H20gN!vsi#rJ')
if sql.is_connected():
print('connected to sql')
return sql
except Exception as e:
print(e)
return False
def sql_auth(username, password):
sql = sql_connect()
cursor = sql.cursor()
query = "select `id` from partners where username = %s and password = %s"
params = (username, password)
try:
cursor.execute(query, params)
except Exception as e:
print("Error: ", e)
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
pass
class SocksProxy(StreamRequestHandler):
username = 'username'
password = 'password'
def handle(self):
logging.info('Accepting connection from %s:%s' % self.client_address)
# greeting header
# read and unpack 2 bytes from a client
header = self.connection.recv(2)
version, nmethods = struct.unpack("!BB", header)
# socks 5
assert version == SOCKS_VERSION
assert nmethods > 0
# get available methods
methods = self.get_available_methods(nmethods)
# accept only USERNAME/PASSWORD auth
if 2 not in set(methods):
# close connection
self.server.close_request(self.request)
return
# send welcome message
self.connection.sendall(struct.pack("!BB", SOCKS_VERSION, 2))
if not self.verify_credentials():
return
# request
version, cmd, _, address_type = struct.unpack("!BBBB", self.connection.recv(4))
assert version == SOCKS_VERSION
if address_type == 1: # IPv4
address = socket.inet_ntoa(self.connection.recv(4))
elif address_type == 3: # Domain name
domain_length = ord(self.connection.recv(1)[0])
address = self.connection.recv(domain_length)
port = struct.unpack('!H', self.connection.recv(2))[0]
# reply
try:
if cmd == 1: # CONNECT
remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote.connect((address, port))
bind_address = remote.getsockname()
logging.info('Connected to %s %s' % (address, port))
else:
self.server.close_request(self.request)
addr = struct.unpack("!I", socket.inet_aton(bind_address[0]))[0]
port = bind_address[1]
reply = struct.pack("!BBBBIH", SOCKS_VERSION, 0, 0, address_type,
addr, port)
except Exception as err:
logging.error(err)
# return connection refused error
reply = self.generate_failed_reply(address_type, 5)
self.connection.sendall(reply)
# establish data exchange
if reply[1] == 0 and cmd == 1:
self.exchange_loop(self.connection, remote)
self.server.close_request(self.request)
def get_available_methods(self, n):
methods = []
for i in range(n):
methods.append(ord(self.connection.recv(1)))
return methods
def verify_credentials(self):
version = ord(self.connection.recv(1))
assert version == 1
username_len = ord(self.connection.recv(1))
username = self.connection.recv(username_len).decode('utf-8')
password_len = ord(self.connection.recv(1))
password = self.connection.recv(password_len).decode('utf-8')
if username == self.username and password == self.password:
# success, status = 0
response = struct.pack("!BB", version, 0)
self.connection.sendall(response)
return True
# failure, status != 0
response = struct.pack("!BB", version, 0xFF)
self.connection.sendall(response)
self.server.close_request(self.request)
return False
def generate_failed_reply(self, address_type, error_number):
return struct.pack("!BBBBIH", SOCKS_VERSION, error_number, 0, address_type, 0, 0)
def exchange_loop(self, client, remote):
while True:
# wait until client or remote is available for read
r, w, e = select.select([client, remote], [], [])
if client in r:
data = client.recv(4096)
if remote.send(data) <= 0:
break
if remote in r:
data = remote.recv(4096)
if client.send(data) <= 0:
break
if __name__ == '__main__':
####sql_connect() # connect to database
print("Accept partner connections...")
#with ThreadingTCPServer(('127.0.0.1', 9013), SocksProxy) as server:
# server.serve_forever()
partner_server = ThreadingTCPServer(('127.0.0.1', 9013), SocksProxy)
partner_thread = threading.Thread(target=partner_server.serve_forever)
print("Accept app connections...")
app_server = ThreadingTCPServer(('127.0.0.1', 9014), SocksProxy)
app_thread = threading.Thread(target=app_server.serve_forever)
for t in partner_thread, app_thread:
t.start()
for t in partner_thread, app_thread:
t.join()
To conceptualize accepting inbound connection on both ports and sending request of one through the other (Making it a type of reverse proxy) is difficult to know the next step. I got the idea to communicate between two SOCKS server classes. I attempted that here:
import logging
import select
import socket
import struct
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler, BaseRequestHandler
import threading
logging.basicConfig(level=logging.DEBUG)
SOCKS_VERSION = 5
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
pass
class NatServer(StreamRequestHandler):
username = 'username'
password = 'password'
def __init__(self):
NatServer.client = None
NatServer.remote = None
def handle(self):
logging.info('Accepting app connection from %s:%s' % self.client_address)
# greeting header
# read and unpack 2 bytes from a client
header = self.connection.recv(2)
version, nmethods = struct.unpack("!BB", header)
# socks 5
assert version == SOCKS_VERSION
assert nmethods > 0
# get available methods
methods = self.get_available_methods(nmethods)
# accept only USERNAME/PASSWORD auth
if 2 not in set(methods):
# close connection
self.server.close_request(self.request)
return
# send welcome message
self.connection.sendall(struct.pack("!BB", SOCKS_VERSION, 2))
if not self.verify_credentials():
return
# request
version, cmd, _, address_type = struct.unpack("!BBBB", self.connection.recv(4))
assert version == SOCKS_VERSION
if address_type == 1: # IPv4
address = socket.inet_ntoa(self.connection.recv(4))
elif address_type == 3: # Domain name
domain_length = ord(self.connection.recv(1)[0])
address = self.connection.recv(domain_length)
port = struct.unpack('!H', self.connection.recv(2))[0]
# reply
try:
if cmd == 1: # CONNECT
remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote.connect((address, port))
bind_address = remote.getsockname()
logging.info('Connected to %s %s' % (address, port))
else:
self.server.close_request(self.request)
addr = struct.unpack("!I", socket.inet_aton(bind_address[0]))[0]
port = bind_address[1]
reply = struct.pack("!BBBBIH", SOCKS_VERSION, 0, 0, address_type,
addr, port)
except Exception as err:
logging.error(err)
# return connection refused error
reply = self.generate_failed_reply(address_type, 5)
self.connection.sendall(reply)
# establish data exchange
if reply[1] == 0 and cmd == 1:
self.exchange_loop(self.connection, remote)
self.server.close_request(self.request)
def get_available_methods(self, n):
methods = []
for i in range(n):
methods.append(ord(self.connection.recv(1)))
return methods
def verify_credentials(self):
version = ord(self.connection.recv(1))
assert version == 1
username_len = ord(self.connection.recv(1))
username = self.connection.recv(username_len).decode('utf-8')
password_len = ord(self.connection.recv(1))
password = self.connection.recv(password_len).decode('utf-8')
if username == self.username and password == self.password:
# success, status = 0
response = struct.pack("!BB", version, 0)
self.connection.sendall(response)
return True
# failure, status != 0
response = struct.pack("!BB", version, 0xFF)
self.connection.sendall(response)
self.server.close_request(self.request)
return False
def generate_failed_reply(self, address_type, error_number):
return struct.pack("!BBBBIH", SOCKS_VERSION, error_number, 0, address_type, 0, 0)
def exchange_loop(self, client, remote):
self.bridge_sockets(client, remote)
while True:
# wait until client or remote is available for read
r, w, e = select.select([client, remote], [], [])
if client in r:
data = client.recv(4096)
if remote.send(data) <= 0:
break
if remote in r:
data = remote.recv(4096)
if client.send(data) <= 0:
break
def bridge_sockets(self, client, remote):
NatServer.client = client
NatServer.remote = remote
browser_server = ThreadingTCPServer(('127.0.0.1', 9013), BrowserServer)
browser_thread = threading.Thread(target=partner_server.serve_forever)
''', kwargs={'app_client': client,
'app_remote': remote}'''
browser_thread.start()
#partner_thread.join()
class BrowserServer(StreamRequestHandler):
username: "username"
password: "password"
def __init__(self): # , app_client, app_remote
self.app_client = NatServer.client
self.app_remote = NatServer.remote
def handle(self):
logging.info('Accepting partner connection from %s:%s' % self.client_address)
# greeting header
# read and unpack 2 bytes from a client
header = self.connection.recv(2)
version, nmethods = struct.unpack("!BB", header)
# socks 5
assert version == SOCKS_VERSION
assert nmethods > 0
# get available methods
methods = self.get_available_methods(nmethods)
# accept only USERNAME/PASSWORD auth
if 2 not in set(methods):
# close connection
self.server.close_request(self.request)
return
# send welcome message
self.connection.sendall(struct.pack("!BB", SOCKS_VERSION, 2))
if not self.verify_credentials():
return
# client request
version, cmd, _, address_type = struct.unpack("!BBBB", self.connection.recv(4))
assert version == SOCKS_VERSION
if address_type == 1: # IPv4
address = socket.inet_ntoa(self.connection.recv(4))
elif address_type == 3: # Domain name
domain_length = ord(self.connection.recv(1)[0])
address = self.connection.recv(domain_length)
port = struct.unpack('!H', self.connection.recv(2))[0]
# reply
try:
if cmd == 1: # CONNECT
remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote.connect((address, port))
bind_address = remote.getsockname()
logging.info('Connected to %s %s' % (address, port))
else:
self.server.close_request(self.request)
addr = struct.unpack("!I", socket.inet_aton(bind_address[0]))[0]
port = bind_address[1]
reply = struct.pack("!BBBBIH", SOCKS_VERSION, 0, 0, address_type,
addr, port)
except Exception as err:
logging.error(err)
# return connection refused error
reply = self.generate_failed_reply(address_type, 5)
self.connection.sendall(reply)
# establish data exchange
if reply[1] == 0 and cmd == 1:
self.exchange_loop(self.connection, remote)
self.server.close_request(self.request)
def get_available_methods(self, n):
methods = []
for i in range(n):
methods.append(ord(self.connection.recv(1)))
return methods
def verify_credentials(self):
version = ord(self.connection.recv(1))
assert version == 1
username_len = ord(self.connection.recv(1))
username = self.connection.recv(username_len).decode('utf-8')
password_len = ord(self.connection.recv(1))
password = self.connection.recv(password_len).decode('utf-8')
if username == self.username and password == self.password:
# success, status = 0
response = struct.pack("!BB", version, 0)
self.connection.sendall(response)
return True
# failure, status != 0
response = struct.pack("!BB", version, 0xFF)
self.connection.sendall(response)
self.server.close_request(self.request)
return False
def generate_failed_reply(self, address_type, error_number):
return struct.pack("!BBBBIH", SOCKS_VERSION, error_number, 0, address_type, 0, 0)
def exchange_loop(self, client, remote):
# app_<-partner_client
app_client = self.app_client
app_remote = self.app_remote
while True:
# wait until client or remote is available for read
#r, w, e = select.select([client, remote], [], [])
r, w, e = select.select([client, app_client], [], [])
if app_client in r:
data = app_client.recv(4096)
if client.send(data) <= 0:
break
if client in r:
data = client.recv(4096)
if app_client.send(data) <= 0:
break
if __name__ == '__main__':
nat_server = ThreadingTCPServer(('127.0.0.1', 9014), NatServer)
nat_thread = threading.Thread(target=app_server.serve_forever)
nat_thread.start()
nat_thread.join()
I'm not sure if that's the right idea but it gave me the error when I connected to BrowserServer port via a web browser:
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 36632)
Traceback (most recent call last):
File "/home/user/.pyenv/versions/3.7.3/lib/python3.7/socketserver.py", line 650, in process_request_thread
self.finish_request(request, client_address)
File "/home/user/.pyenv/versions/3.7.3/lib/python3.7/socketserver.py", line 360, in finish_request
self.RequestHandlerClass(request, client_address, self)
TypeError: __init__() takes 1 positional argument but 4 were given
Just so that everyone is clear on what I'm trying to accomplish: MyApp will run on a server and open 2 ports. ServerBehindNAT will be running client code which will maintain a connection to MyApp. BrowserUser will use MyApp server IP as a SOCKS server in his browser. When BrowserUser visits a site, the request will go through MyApp to ServerBehindNAT, who will process the request and send responses back through MyApp.

using corenlp with python3. Stanford_corenlp_pywrapper for python3 gives encoding issues for writing and reading sockets

I am using https://github.com/brendano/stanford_corenlp_pywrapper. It has a sock.py script for communcation which works for python2.
from __future__ import division
import subprocess, tempfile, time, os, logging, re, struct, socket, atexit, glob, itertools
from copy import copy,deepcopy
from pprint import pprint
try:
import ujson as json
except ImportError:
import json
# SUGGESTED: for constituent parsing models, specify shift-reduce parser in
# configdict with:
# 'parse.model': 'edu/stanford/nlp/models/srparser/englishSR.ser.gz'
MODES_items = [
('ssplit', {'annotators': "tokenize, ssplit",
'description': "tokenization and sentence splitting (included in all subsequent ones)", }),
('pos', {'annotators':"tokenize, ssplit, pos, lemma",
'description':"POS (and lemmas)",}),
('ner', {'annotators':"tokenize, ssplit, pos, lemma, ner, entitymentions",
'description':"POS and NER (and lemmas)",}),
('parse', {'annotators':"tokenize, ssplit, pos, lemma, parse",
'description':"fairly basic parsing with POS, lemmas, trees, dependencies",}),
('nerparse', {'annotators':"tokenize, ssplit, pos, lemma, ner, entitymentions, parse",
'description':"parsing with NER, POS, lemmas, depenencies."}),
('coref', {'annotators':"tokenize, ssplit, pos, lemma, ner, entitymentions, parse, dcoref",
'description':"Coreference, including constituent parsing."})
]
MODES = dict(MODES_items)
logging.basicConfig() # wtf, why we have to call this?
LOG = logging.getLogger("CoreNLP_PyWrapper")
LOG.setLevel("INFO")
# LOG.setLevel("DEBUG")
PARSEDOC_TIMEOUT_SEC = 60 * 5
STARTUP_BUSY_WAIT_INTERVAL_SEC = 1.0
def command(mode=None, configfile=None, configdict=None, comm_mode=None,
java_command="java",
java_options="-Xmx4g -XX:ParallelGCThreads=1",
**kwargs):
d = {}
d.update(**locals())
d.update(**kwargs)
more_config = ""
if mode is None and configfile is None and configdict is None:
assert False, "Need to set mode, or the annotators directly, for this wrapper to work."
if mode:
if configdict is not None:
assert 'annotators' not in configdict, "mode was given but annotators are set in the configdict. use only one please."
elif configdict is None:
configdict = {}
LOG.info("mode given as '%s' so setting annotators: %s" % (mode, MODES[mode]['annotators']))
configdict['annotators'] = MODES[mode]['annotators']
if configfile:
more_config += " --configfile {}".format(configfile)
if configdict:
j = json.dumps(configdict)
assert "'" not in j, "can't handle single quote in config values"
more_config += " --configdict '{}'".format(j)
d['more_config'] = more_config
if comm_mode=='SOCKET':
d['comm_info'] = "--server {server_port}".format(**d)
elif comm_mode=='PIPE':
d['comm_info'] = "--outpipe {outpipe}".format(**d)
else: assert False, "need comm_mode to be SOCKET or PIPE but got " + repr(comm_mode)
cmd = """exec {java_command} {java_options} -cp '{classpath}'
corenlp.SocketServer {comm_info} {more_config}"""
return cmd.format(**d).replace("\n", " ")
class SubprocessCrashed(Exception):
pass
class CoreNLP:
def __init__(self, mode=None,
configfile=None, configdict=None,
corenlp_jars=(
"/home/sw/corenlp/stanford-corenlp-full-2015-04-20/*",
"/home/sw/stanford-srparser-2014-10-23-models.jar",
),
comm_mode='PIPE', # SOCKET or PIPE
server_port=12340, outpipe_filename_prefix="/tmp/corenlp_pywrap_pipe",
**more_configdict_args
):
self.mode = mode
self.proc = None
self.server_port = server_port
self.configfile = configfile
self.comm_mode = comm_mode
self.outpipe = None
self.configdict = deepcopy(configdict)
if not self.configdict: self.configdict = {}
self.configdict.update(more_configdict_args)
if not self.configdict: self.configdict = None
if self.comm_mode=='PIPE':
tag = "pypid=%d_time=%s" % (os.getpid(), time.time())
self.outpipe = "%s_%s" % (outpipe_filename_prefix, tag)
assert not os.path.exists(self.outpipe)
assert isinstance(corenlp_jars, (list,tuple))
deglobbed = itertools.chain(*[glob.glob(f) for f in corenlp_jars])
assert any(os.path.exists(f) for f in deglobbed), "CoreNLP jar files don't seem to exist; are the paths correct? Searched files: %s" % repr(deglobbed)
local_libdir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
'lib')
jars = [os.path.join(local_libdir, "*")]
jars += corenlp_jars
self.classpath = ':'.join(jars)
# self.classpath += ":../bin:bin" ## for eclipse java dev
# LOG.info("CLASSPATH: " + self.classpath)
self.start_server()
# This probably is only half-reliable, but worth a shot.
atexit.register(self.cleanup)
def cleanup(self):
self.kill_proc_if_running()
if self.outpipe and os.path.exists(self.outpipe):
os.unlink(self.outpipe)
def __del__(self):
# This is also an unreliable way to ensure the subproc is gone, but
# might as well try
self.cleanup()
def start_server(self):
self.kill_proc_if_running()
if self.comm_mode=='PIPE':
if not os.path.exists(self.outpipe):
os.mkfifo(self.outpipe)
cmd = command(**self.__dict__)
LOG.info("Starting java subprocess, and waiting for signal it's ready, with command: %s" % cmd)
self.proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE)
time.sleep(STARTUP_BUSY_WAIT_INTERVAL_SEC)
if self.comm_mode=='SOCKET':
sock = self.get_socket(num_retries=100, retry_interval=STARTUP_BUSY_WAIT_INTERVAL_SEC)
sock.close()
elif self.comm_mode=='PIPE':
self.outpipe_fp = open(self.outpipe, 'r')
while True:
# This loop is for if you have timeouts for the socket connection
# The pipe system doesn't have timeouts, so this should run only
# once in that case.
try:
ret = self.send_command_and_parse_result('PING\t""', 2)
if ret is None:
continue
assert ret == "PONG", "Bad return data on startup ping: " + ret
LOG.info("Successful ping. The server has started.")
break
except socket.error, e:
LOG.info("Waiting for startup: ping got exception: %s %s" % (type(e), e))
LOG.info("pausing before retry")
time.sleep(STARTUP_BUSY_WAIT_INTERVAL_SEC)
LOG.info("Subprocess is ready.")
def ensure_proc_is_running(self):
if self.proc is None:
# Has never been started
self.start_server()
elif self.proc.poll() is not None:
# Restart
self.start_server()
def kill_proc_if_running(self):
if self.proc is None:
# it's never been started yet
return
retcode = self.proc.poll()
if retcode is not None:
LOG.info("Subprocess seems to be stopped, exit code %s" % retcode)
elif retcode is None:
LOG.warning("Killing subprocess %s" % self.proc.pid)
os.kill(self.proc.pid, 9)
def parse_doc(self, text, timeout=PARSEDOC_TIMEOUT_SEC, raw=False):
cmd = "PARSEDOC\t%s" % json.dumps(text)
return self.send_command_and_parse_result(cmd, timeout, raw=raw)
def get_socket(self, num_retries=1, retry_interval=1):
# could be smarter here about reusing the same socket?
for trial in range(num_retries):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # not sure if this is needed?
sock.connect(('localhost', self.server_port))
return sock
except (socket.error, socket.timeout) as e:
LOG.info("socket error when making connection (%s)" % e)
if trial < num_retries-1:
LOG.info("pausing before retry")
time.sleep(retry_interval)
assert False, "couldnt connect socket"
def send_command_and_parse_result(self, cmd, timeout, raw=False):
try:
self.ensure_proc_is_running()
data = self.send_command_and_get_string_result(cmd, timeout)
if data is None: return None
decoded = None
if raw:
return data
try:
decoded = json.loads(data)
except ValueError:
LOG.warning("Bad JSON returned from subprocess; returning null.")
LOG.warning("Bad JSON length %d, starts with: %s" % (len(data), repr(data[:1000])))
return None
return decoded
except socket.timeout, e:
LOG.info("Socket timeout happened, returning None: %s %s" % (type(e), e))
return None
# This is tricky. maybe the process is running smoothly but just
# taking longer than we like. if it's in thie state, and we try to
# send another command, what happens? Should we forcibly restart
# the process now just in case?
def send_command_and_get_string_result(self, cmd, timeout):
if self.comm_mode == 'SOCKET':
sock = self.get_socket(num_retries=100)
sock.settimeout(timeout)
sock.sendall(cmd + "\n")
size_info_str = sock.recv(8)
elif self.comm_mode == 'PIPE':
self.proc.stdin.write(cmd + "\n")
self.proc.stdin.flush()
size_info_str = self.outpipe_fp.read(8)
# java "long" is 8 bytes, which python struct calls "long long".
# java default byte ordering is big-endian.
size_info = struct.unpack('>Q', size_info_str)[0]
# print "size expected", size_info
chunks = []
curlen = lambda: sum(len(x) for x in chunks)
while True:
remaining_size = size_info - curlen()
if self.comm_mode == 'SOCKET':
data = sock.recv(remaining_size)
elif self.comm_mode == 'PIPE':
data = self.outpipe_fp.read(remaining_size)
chunks.append(data)
if curlen() >= size_info: break
if len(chunks) > 1000:
LOG.warning("Incomplete value from server")
return None
time.sleep(0.01)
return ''.join(chunks)
def test_modes():
import pytest
gosimple(comm_mode='SOCKET')
gosimple(comm_mode='PIPE')
with pytest.raises(AssertionError):
gosimple(comm_mode=None)
with pytest.raises(AssertionError):
gosimple(comm_mode='asdfasdf')
def test_coref():
assert_no_java("no java when starting")
p = CoreNLP("coref")
ret = p.parse_doc("I saw Fred. He saw me.")
pprint(ret)
assert 'entities' in ret
assert isinstance(ret['entities'], list)
def gosimple(**kwargs):
assert_no_java("no java when starting")
p = CoreNLP("ssplit", **kwargs)
ret = p.parse_doc("Hello world.")
# pprint(ret)
assert len(ret['sentences']) == 1
assert u' '.join(ret['sentences'][0]['tokens']) == u"Hello world ."
p.kill_proc_if_running()
assert_no_java()
def test_paths():
import pytest
with pytest.raises(AssertionError):
CoreNLP("ssplit", corenlp_jars=["/asdfadsf/asdfasdf"])
def assert_no_java(msg=""):
ps_output = os.popen("ps wux").readlines()
javalines = [x for x in ps_output if re.search(r'\bbin/java\b', x)]
print ''.join(javalines)
assert len(javalines) == 0, msg
# def test_doctimeout():
# assert_no_java("no java when starting")
#
# p = CoreNLP("pos")
# ret = p.parse_doc(open("allbrown.txt").read(), 0.5)
# assert ret is None
# p.kill_proc_if_running()
# assert_no_java()
if __name__=='__main__':
import sys
if sys.argv[1]=='modes':
for mode,d in MODES_items:
print " * `%s`: %s" % (mode, d['description'])
if sys.argv[1]=='modes_json':
# import json as stdjson
# print stdjson.dumps(MODES, indent=4)
print '"%s"' % json.dumps(MODES).replace('"', r'\"')
I changed it to python3 using 2to3 script which addded brackets around print and changed exception handling. Updated code
"""
Client and process monitor for the java socket server.
"""
import subprocess, tempfile, time, os, logging, re, struct, socket, atexit, glob, itertools
from copy import copy,deepcopy
from pprint import pprint
try:
import ujson as json
except ImportError:
import json
# SUGGESTED: for constituent parsing models, specify shift-reduce parser in
# configdict with:
# 'parse.model': 'edu/stanford/nlp/models/srparser/englishSR.ser.gz'
MODES_items = [
('ssplit', {'annotators': "tokenize, ssplit",
'description': "tokenization and sentence splitting (included in all subsequent ones)", }),
('pos', {'annotators':"tokenize, ssplit, pos, lemma",
'description':"POS (and lemmas)",}),
('ner', {'annotators':"tokenize, ssplit, pos, lemma, ner, entitymentions",
'description':"POS and NER (and lemmas)",}),
('parse', {'annotators':"tokenize, ssplit, pos, lemma, parse",
'description':"fairly basic parsing with POS, lemmas, trees, dependencies",}),
('nerparse', {'annotators':"tokenize, ssplit, pos, lemma, ner, entitymentions, parse",
'description':"parsing with NER, POS, lemmas, depenencies."}),
('coref', {'annotators':"tokenize, ssplit, pos, lemma, ner, entitymentions, parse, dcoref",
'description':"Coreference, including constituent parsing."})
]
MODES = dict(MODES_items)
logging.basicConfig() # wtf, why we have to call this?
LOG = logging.getLogger("CoreNLP_PyWrapper")
LOG.setLevel("INFO")
# LOG.setLevel("DEBUG")
PARSEDOC_TIMEOUT_SEC = 60 * 5
STARTUP_BUSY_WAIT_INTERVAL_SEC = 1.0
def command(mode=None, configfile=None, configdict=None, comm_mode=None,
java_command="java",
java_options="-Xmx4g -XX:ParallelGCThreads=1",
**kwargs):
d = {}
d.update(**locals())
d.update(**kwargs)
more_config = ""
if mode is None and configfile is None and configdict is None:
assert False, "Need to set mode, or the annotators directly, for this wrapper to work."
if mode:
if configdict is not None:
assert 'annotators' not in configdict, "mode was given but annotators are set in the configdict. use only one please."
elif configdict is None:
configdict = {}
LOG.info("mode given as '%s' so setting annotators: %s" % (mode, MODES[mode]['annotators']))
configdict['annotators'] = MODES[mode]['annotators']
if configfile:
more_config += " --configfile {}".format(configfile)
if configdict:
j = json.dumps(configdict)
assert "'" not in j, "can't handle single quote in config values"
more_config += " --configdict '{}'".format(j)
d['more_config'] = more_config
if comm_mode=='SOCKET':
d['comm_info'] = "--server {server_port}".format(**d)
elif comm_mode=='PIPE':
d['comm_info'] = "--outpipe {outpipe}".format(**d)
else: assert False, "need comm_mode to be SOCKET or PIPE but got " + repr(comm_mode)
cmd = """exec {java_command} {java_options} -cp '{classpath}'
corenlp.SocketServer {comm_info} {more_config}"""
return cmd.format(**d).replace("\n", " ")
class SubprocessCrashed(Exception):
pass
class CoreNLP:
def __init__(self, mode=None,
configfile=None, configdict=None,
corenlp_jars=(
"/home/sw/corenlp/stanford-corenlp-full-2015-04-20/*",
"/home/sw/stanford-srparser-2014-10-23-models.jar",
),
comm_mode='PIPE', # SOCKET or PIPE
server_port=12340, outpipe_filename_prefix="/tmp/corenlp_pywrap_pipe",
**more_configdict_args
):
self.mode = mode
self.proc = None
self.server_port = server_port
self.configfile = configfile
self.comm_mode = comm_mode
self.outpipe = None
self.configdict = deepcopy(configdict)
if not self.configdict: self.configdict = {}
self.configdict.update(more_configdict_args)
if not self.configdict: self.configdict = None
if self.comm_mode=='PIPE':
tag = "pypid=%d_time=%s" % (os.getpid(), time.time())
self.outpipe = "%s_%s" % (outpipe_filename_prefix, tag)
assert not os.path.exists(self.outpipe)
assert isinstance(corenlp_jars, (list,tuple))
deglobbed = itertools.chain(*[glob.glob(f) for f in corenlp_jars])
assert any(os.path.exists(f) for f in deglobbed), "CoreNLP jar files don't seem to exist; are the paths correct? Searched files: %s" % repr(deglobbed)
local_libdir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
'lib')
jars = [os.path.join(local_libdir, "*")]
jars += corenlp_jars
self.classpath = ':'.join(jars)
# self.classpath += ":../bin:bin" ## for eclipse java dev
# LOG.info("CLASSPATH: " + self.classpath)
self.start_server()
# This probably is only half-reliable, but worth a shot.
atexit.register(self.cleanup)
def cleanup(self):
self.kill_proc_if_running()
if self.outpipe and os.path.exists(self.outpipe):
os.unlink(self.outpipe)
def __del__(self):
# This is also an unreliable way to ensure the subproc is gone, but
# might as well try
self.cleanup()
def start_server(self):
self.kill_proc_if_running()
if self.comm_mode=='PIPE':
if not os.path.exists(self.outpipe):
os.mkfifo(self.outpipe)
cmd = command(**self.__dict__)
LOG.info("Starting java subprocess, and waiting for signal it's ready, with command: %s" % cmd)
self.proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE)
time.sleep(STARTUP_BUSY_WAIT_INTERVAL_SEC)
if self.comm_mode=='SOCKET':
sock = self.get_socket(num_retries=100, retry_interval=STARTUP_BUSY_WAIT_INTERVAL_SEC)
sock.close()
elif self.comm_mode=='PIPE':
self.outpipe_fp = open(self.outpipe, 'r')
while True:
# This loop is for if you have timeouts for the socket connection
# The pipe system doesn't have timeouts, so this should run only
# once in that case.
try:
ret = self.send_command_and_parse_result('PING\t""', 2)
if ret is None:
continue
assert ret == "PONG", "Bad return data on startup ping: " + ret
LOG.info("Successful ping. The server has started.")
break
except socket.error as e:
LOG.info("Waiting for startup: ping got exception: %s %s" % (type(e), e))
LOG.info("pausing before retry")
time.sleep(STARTUP_BUSY_WAIT_INTERVAL_SEC)
LOG.info("Subprocess is ready.")
def ensure_proc_is_running(self):
if self.proc is None:
# Has never been started
self.start_server()
elif self.proc.poll() is not None:
# Restart
self.start_server()
def kill_proc_if_running(self):
if self.proc is None:
# it's never been started yet
return
retcode = self.proc.poll()
if retcode is not None:
LOG.info("Subprocess seems to be stopped, exit code %s" % retcode)
elif retcode is None:
LOG.warning("Killing subprocess %s" % self.proc.pid)
os.kill(self.proc.pid, 9)
def parse_doc(self, text, timeout=PARSEDOC_TIMEOUT_SEC, raw=False):
cmd = "PARSEDOC\t%s" % json.dumps(text)
return self.send_command_and_parse_result(cmd, timeout, raw=raw)
def get_socket(self, num_retries=1, retry_interval=1):
# could be smarter here about reusing the same socket?
for trial in range(num_retries):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # not sure if this is needed?
sock.connect(('localhost', self.server_port))
return sock
except (socket.error, socket.timeout) as e:
LOG.info("socket error when making connection (%s)" % e)
if trial < num_retries-1:
LOG.info("pausing before retry")
time.sleep(retry_interval)
assert False, "couldnt connect socket"
def send_command_and_parse_result(self, cmd, timeout, raw=False):
try:
self.ensure_proc_is_running()
data = self.send_command_and_get_string_result(cmd, timeout)
if data is None: return None
decoded = None
if raw:
return data
try:
decoded = json.loads(data)
except ValueError:
LOG.warning("Bad JSON returned from subprocess; returning null.")
LOG.warning("Bad JSON length %d, starts with: %s" % (len(data), repr(data[:1000])))
return None
return decoded
except socket.timeout as e:
LOG.info("Socket timeout happened, returning None: %s %s" % (type(e), e))
return None
# This is tricky. maybe the process is running smoothly but just
# taking longer than we like. if it's in thie state, and we try to
# send another command, what happens? Should we forcibly restart
# the process now just in case?
def send_command_and_get_string_result(self, cmd, timeout):
if self.comm_mode == 'SOCKET':
sock = self.get_socket(num_retries=100)
sock.settimeout(timeout)
sock.sendall(cmd + "\n")
size_info_str = sock.recv(8)
elif self.comm_mode == 'PIPE':
self.proc.stdin.write(cmd + "\n")
self.proc.stdin.flush()
size_info_str = self.outpipe_fp.read(8)
# java "long" is 8 bytes, which python struct calls "long long".
# java default byte ordering is big-endian.
size_info = struct.unpack('>Q', size_info_str)[0]
# print "size expected", size_info
chunks = []
curlen = lambda: sum(len(x) for x in chunks)
while True:
remaining_size = size_info - curlen()
if self.comm_mode == 'SOCKET':
data = sock.recv(remaining_size)
elif self.comm_mode == 'PIPE':
data = self.outpipe_fp.read(remaining_size)
chunks.append(data)
if curlen() >= size_info: break
if len(chunks) > 1000:
LOG.warning("Incomplete value from server")
return None
time.sleep(0.01)
return ''.join(chunks)
def test_modes():
import pytest
gosimple(comm_mode='SOCKET')
gosimple(comm_mode='PIPE')
with pytest.raises(AssertionError):
gosimple(comm_mode=None)
with pytest.raises(AssertionError):
gosimple(comm_mode='asdfasdf')
def test_coref():
assert_no_java("no java when starting")
p = CoreNLP("coref")
ret = p.parse_doc("I saw Fred. He saw me.")
pprint(ret)
assert 'entities' in ret
assert isinstance(ret['entities'], list)
def gosimple(**kwargs):
assert_no_java("no java when starting")
p = CoreNLP("ssplit", **kwargs)
ret = p.parse_doc("Hello world.")
# pprint(ret)
assert len(ret['sentences']) == 1
assert ' '.join(ret['sentences'][0]['tokens']) == "Hello world ."
p.kill_proc_if_running()
assert_no_java()
def test_paths():
import pytest
with pytest.raises(AssertionError):
CoreNLP("ssplit", corenlp_jars=["/asdfadsf/asdfasdf"])
def assert_no_java(msg=""):
ps_output = os.popen("ps wux").readlines()
javalines = [x for x in ps_output if re.search(r'\bbin/java\b', x)]
print(''.join(javalines))
assert len(javalines) == 0, msg
# def test_doctimeout():
# assert_no_java("no java when starting")
#
# p = CoreNLP("pos")
# ret = p.parse_doc(open("allbrown.txt").read(), 0.5)
# assert ret is None
# p.kill_proc_if_running()
# assert_no_java()
if __name__=='__main__':
import sys
if sys.argv[1]=='modes':
for mode,d in MODES_items:
print(" * `%s`: %s" % (mode, d['description']))
if sys.argv[1]=='modes_json':
# import json as stdjson
# print stdjson.dumps(MODES, indent=4)
print('"%s"' % json.dumps(MODES).replace('"', r'\"'))
However, I still get an error at
byteself.proc.stdin.write(cmd + "\n")
TypeError: a bytes-like object is required, not 'str'
I know it has to do with encoding and I may have to use encode/decode at a few places where it reads/writes but since I have not worked with sockets a lot, I am not sure where to change.

How can I make multiclient server in python using multithreading?

This is the code that I have used.But I don't get actual result that I want.When I execute code ChatServer file works properly,but ChatClient gives only one line(Usage : python telnet.py hostname port).Please Help me.I am new in python.
The server code:
#!/usr/bin/env python
#!/usr/bin/env python
"""
A basic, multiclient 'chat server' using Python's select module
with interrupt handling.
Entering any line of input at the terminal will exit the server.
"""
import select
import socket
import sys
import signal
from communication import send, receive
BUFSIZ = 1024
class ChatServer(object):
""" Simple chat server using select """
def __init__(self, port=3490, backlog=5):
self.clients = 0
# Client map
self.clientmap = {}
# Output socket list
self.outputs = []
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind(('',port))
print 'Listening to port',port,'...'
self.server.listen(backlog)
# Trap keyboard interrupts
signal.signal(signal.SIGINT, self.sighandler)
def sighandler(self, signum, frame):
# Close the server
print 'Shutting down server...'
# Close existing client sockets
for o in self.outputs:
o.close()
self.server.close()
def getname(self, client):
# Return the printable name of the
# client, given its socket...
info = self.clientmap[client]
host, name = info[0][0], info[1]
return '#'.join((name, host))
def serve(self):
inputs = [self.server,sys.stdin]
self.outputs = []
running = 1
while running:
try:
inputready,outputready,exceptready = select.select(inputs, self.outputs, [])
except select.error, e:
break
except socket.error, e:
break
for s in inputready:
if s == self.server:
# handle the server socket
client, address = self.server.accept()
print 'chatserver: got connection %d from %s' % (client.fileno(), address)
# Read the login name
cname = receive(client).split('NAME: ')[1]
# Compute client name and send back
self.clients += 1
send(client, 'CLIENT: ' + str(address[0]))
inputs.append(client)
self.clientmap[client] = (address, cname)
# Send joining information to other clients
msg = '\n(Connected: New client (%d) from %s)' % (self.clients, self.getname(client))
for o in self.outputs:
# o.send(msg)
send(o, msg)
self.outputs.append(client)
elif s == sys.stdin:
# handle standard input
junk = sys.stdin.readline()
running = 0
else:
# handle all other sockets
try:
# data = s.recv(BUFSIZ)
data = receive(s)
if data:
# Send as new client's message...
msg = '\n#[' + self.getname(s) + ']>> ' + data
# Send data to all except ourselves
for o in self.outputs:
if o != s:
# o.send(msg)
send(o, msg)
else:
print 'chatserver: %d hung up' % s.fileno()
self.clients -= 1
s.close()
inputs.remove(s)
self.outputs.remove(s)
# Send client leaving information to others
msg = '\n(Hung up: Client from %s)' % self.getname(s)
for o in self.outputs:
# o.send(msg)
send(o, msg)
except socket.error, e:
# Remove
inputs.remove(s)
self.outputs.remove(s)
self.server.close()
if __name__ == "__main__":
ChatServer().serve()
The chat client:
#! /usr/bin/env python
"""
Simple chat client for the chat server. Defines
a simple protocol to be used with chatserver.
"""
import socket
import sys
import select
from communication import send, receive
BUFSIZ = 1024
class ChatClient(object):
""" A simple command line chat client using select """
def __init__(self, name, host='127.0.0.1', port=3490):
self.name = name
# Quit flag
self.flag = False
self.port = int(port)
self.host = host
# Initial prompt
self.prompt='[' + '#'.join((name, socket.gethostname().split('.')[0])) + ']> '
# Connect to server at port
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, self.port))
print 'Connected to chat server#%d' % self.port
# Send my name...
send(self.sock,'NAME: ' + self.name)
data = receive(self.sock)
# Contains client address, set it
addr = data.split('CLIENT: ')[1]
self.prompt = '[' + '#'.join((self.name, addr)) + ']> '
except socket.error, e:
print 'Could not connect to chat server #%d' % self.port
sys.exit(1)
def cmdloop(self):
while not self.flag:
try:
sys.stdout.write(self.prompt)
sys.stdout.flush()
# Wait for input from stdin & socket
inputready, outputready,exceptrdy = select.select([0, self.sock], [],[])
for i in inputready:
if i == 0:
data = sys.stdin.readline().strip()
if data: send(self.sock, data)
elif i == self.sock:
data = receive(self.sock)
if not data:
print 'Shutting down.'
self.flag = True
break
else:
sys.stdout.write(data + '\n')
sys.stdout.flush()
except KeyboardInterrupt:
print 'Interrupted.'
self.sock.close()
break
if __name__ == "__main__":
import sys
if len(sys.argv)<3:
sys.exit('Usage: %s chatid host portno' % sys.argv[0])
client = ChatClient(sys.argv[1],sys.argv[2], int(sys.argv[3]))
client.cmdloop()
###############################################################################
# The communication module (communication.py)
###############################################################################
import cPickle
import socket
import struct
marshall = cPickle.dumps
unmarshall = cPickle.loads
def send(channel, *args):
buf = marshall(args)
value = socket.htonl(len(buf))
size = struct.pack("L",value)
channel.send(size)
channel.send(buf)
def receive(channel):
size = struct.calcsize("L")
size = channel.recv(size)
try:
size = socket.ntohl(struct.unpack("L", size)[0])
except struct.error, e:
return ''
buf = ""
while len(buf) < size:
buf = channel.recv(size - len(buf))
return unmarshall(buf)[0]

Categories