I've written a server/client tool which can "check the Six degrees of separation theory" in Russian social network VK.com. Client sends task (which contains two VK IDs) and Server tries to find all connections between these two IDs. I've also written a simple HTTP client just to practice working with sockets.
HTTPClient.py:
import socket
import re
def get(host, port, address):
s = socket.socket()
s.connect((host,port))
s.sendall("GET %s HTTP/1.0\nHost: %s\n\n" % (address, host))
answer = ""
symbol = s.recv(1024576)
while symbol != '':
answer += symbol
symbol = s.recv(1024576)
s.close()
answer = re.search("\r\n\r\n(.+)", answer, re.DOTALL).group(1)
return answer
VKApi.py:
import HTTPClient
import json
import re
import types
import md5
import time
class BaseAPIException(Exception):
def __init__(self, text=''):
Exception.__init__(self,text)
class LoginException(BaseAPIException):
pass
class MethodExecutionException(BaseAPIException):
pass
class VKApi:
def __init__(self,appId,scope=[],callbackUrl='https://oauth.vk.com/blank.html'):
self.appId = appId
if ( not isinstance(scope,types.ListType)): raise Exception("Wrong args")
scope.append('nohttps')
self.scope = ','.join(scope)
self.callbackUrl=callbackUrl
self.isLoggedIn = False
self.callsCount = 0
def getAuthorizeLink(self):
return "https://oauth.vk.com/authorize?client_id=%s&scope=%s&redirect_uri=%s&display=page&v=5.21&response_type=token" % \
(self.appId, self.scope, self.callbackUrl)
def login(self, link):
tokenRegExp = re.search('access_token=(.*?)(&|$)',link)
if tokenRegExp == None: raise LoginException('No token found')
self.token = tokenRegExp.group(1)
secretRegExp = re.search('secret=(.*?)(&|$)',link)
if secretRegExp == None: raise LoginException('No secret found')
self.secret = secretRegExp.group(1);
self.isLoggedIn = True
def loginByToken(self,token,secret):
self.token=token
self.secret=secret
self.isLoggedIn = True
def callMethod(self,method,args={}):
if not self.isLoggedIn: raise MethodExecutionException('Not logged in')
if self.callsCount == 3:
time.sleep(1)
self.callsCount = 0
self.callsCount += 1
link = '/method/%s?' % method
link += '&'.join([ '%s=%s' % (k,v) for (k,v) in args.items()])
if len(args)>0: link += '&'
link += 'access_token=%s' % self.token
signature = md5.md5(link + self.secret).hexdigest()
link += '&sig=%s' % signature
try:
response = HTTPClient.get('api.vk.com',80,link)
except Exception as e:
raise MethodExecutionException(e.message)
responseObj = json.loads(response)
if 'error' in responseObj: raise MethodExecutionException(responseObj['error']['error_msg'])
return responseObj['response']
VKHandshakeChecker.py:
import VKApi
import threading
class VKHandshakeChecker:
@property
def answers(self):
return self.__answers
@property
def status(self):
return self.__status
@property
def currentSequence(self):
return self.__currentSequence
def __init__(self,vkapi,maxDepth=6,cache={}):
isinstance(vkapi,VKApi.VKApi)
self.vkapi = vkapi
self.__maxDepth=maxDepth
self.__answers = list()
self.__alreadyBeen = dict()
self.__status = 'IDLE'
self.__canwork = True
self.__cache = cache
def startFinding(self,firstId,secondId):
self.__thread = threading.Thread(target=VKHandshakeChecker.__findHandshakes,args=(self,firstId,secondId))
self.__thread.start()
def stopFinding(self):
self.__canwork = False
def __findHandshakes(self,firstId,secondId):
self.__status = 'WORKING'
self.__answers = list()
for depth in range(2,self.__maxDepth):
self.__currentSequence = [firstId]
self.__findHandshakesRecursive(secondId,depth)
self.__status = 'OK'
def __findHandshakesRecursive(self,finalId,depth):
if self.__canwork == False:
return
if len(self.__currentSequence) == depth:
if (self.currentSequence[-1] == finalId):
self.__answers.append(list(self.__currentSequence))
else:
for friendId in self.__getFriends(self.__currentSequence[-1]):
self.__currentSequence.append(friendId)
self.__findHandshakesRecursive(finalId,depth)
self.__currentSequence.pop()
return
def __getFriends(self,uid):
if uid in self.__cache:
return self.__cache[uid]
try:
response = self.vkapi.callMethod('friends.get',{'user_id':uid})
except:
response = []
self.__cache[uid] = response
return response
VKHandshakeServer.py
import VKHandshakeChecker
import VKApi
import socket
import threading
import cPickle as pickle
import sys
import random
import string
def handle(sock):
files = sock.makefile('rb+')
request = pickle.load(files)
response = {'header': 'ERROR', 'body':'Non-existent command'}
if request['header'] == 'START':
response = start(request['body'])
elif request['header'] == 'STOP':
response = stop(request['body'])
elif request['header'] == 'GETCURRENTSEQUENCE':
response = getCurrentSequence(request['body'])
elif request['header'] == 'GETANSWERS':
response = getAnswers(request['body'])
elif request['header'] == 'GETSTATUS':
response = getStatus(request['body'])
pickle.dump(response,files)
files.flush()
sock.close()
def start(request):
try:
objectId = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5))
checker = VKHandshakeChecker.VKHandshakeChecker(request['api'], cache=cache)
checker.startFinding(request['firstId'],request['secondId'])
checkers[objectId] = checker
return {'header': 'OK', 'body': {'id': objectId}}
except Exception as e:
return {'header': 'ERROR', 'body': repr(e)}
def stop(request):
try:
checkers[request['id']].stopFinding()
del checkers[request['id']]
finally:
return {'header': 'OK', 'body': 'OK'}
def getCurrentSequence(request):
try:
checker = checkers[request['id']]
curSeq = checker.currentSequence
return {'header': 'OK', 'body': curSeq}
except Exception, e:
return {'header': 'ERROR', 'body': repr(e)}
def getAnswers(request):
try:
checker = checkers[request['id']]
answers = checker.answers
return {'header': 'OK', 'body': answers}
except Exception, e:
return {'header': 'ERROR', 'body': repr(e)}
def getStatus(request):
try:
checker = checkers[request['id']]
status = checker.status
return {'header': 'OK', 'body': status}
except Exception, e:
return {'header': 'ERROR', 'body': repr(e)}
checkers = dict()
cache = dict()
host, port = sys.argv[1:3]
port=int(port)
s = socket.socket()
s.bind((host,port))
s.listen(5)
while True:
conn, addr = s.accept()
threading.Thread(target=handle,args=(conn,)).start()
VKHandshakeClient.py:
import socket
import cPickle as pickle
def sendCommand(server, port, header, body=''):
s = socket.socket()
s.connect((server, int(port)))
request = {'header': header, 'body': body}
files = s.makefile('rb+')
pickle.dump(request,files)
files.flush()
response = pickle.load(files)
if response['header'] == 'ERROR':
raise Exception(response['body'])
return response['body']