From e2de1d1b9cdf9bdb3798df231d21551265b5ccd0 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 1 Jul 2019 22:01:43 +0100 Subject: [PATCH] Test servers on different ip addresses --- daemon.py | 46 +++++++++++++++++++++++++++------------------- person.py | 4 ++++ posts.py | 4 +--- session.py | 6 ++++-- tests.py | 36 ++++++++++++++++++------------------ webfinger.py | 29 ++++++++++++++++++++--------- 6 files changed, 74 insertions(+), 51 deletions(-) diff --git a/daemon.py b/daemon.py index f3c5673ac..950ab1f68 100644 --- a/daemon.py +++ b/daemon.py @@ -6,7 +6,7 @@ __maintainer__ = "Bob Mottram" __email__ = "bob@freedombone.net" __status__ = "Production" -from http.server import BaseHTTPRequestHandler, HTTPServer +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer #import socketserver import json import time @@ -14,6 +14,7 @@ from pprint import pprint from session import createSession from webfinger import webfingerMeta from webfinger import webfingerLookup +from webfinger import webfingerHandle from person import personLookup from person import personKeyLookup from person import personOutboxJson @@ -60,9 +61,11 @@ class PubServer(BaseHTTPRequestHandler): self.wfile.write("

404 Not Found

".encode('utf-8')) def _webfinger(self) -> bool: + print('############### _webfinger well-known') if not self.path.startswith('/.well-known'): return False + print('############### _webfinger host-meta') if self.path.startswith('/.well-known/host-meta'): wfResult=webfingerMeta() if wfResult: @@ -70,11 +73,13 @@ class PubServer(BaseHTTPRequestHandler): self.wfile.write(wfResult.encode('utf-8')) return + print('############### _webfinger lookup '+self.path+' '+str(self.server.baseDir)) wfResult=webfingerLookup(self.path,self.server.baseDir) if wfResult: - self._set_headers('application/json') + self._set_headers('application/jrd+json') self.wfile.write(json.dumps(wfResult).encode('utf-8')) else: + print('############### _webfinger lookup 404 '+self.path) self._404() return True @@ -85,24 +90,30 @@ class PubServer(BaseHTTPRequestHandler): return False return True - def do_GET(self): + def do_GET(self): + print('############### GET from '+self.server.baseDir) if self.server.GETbusy: currTimeGET=int(time.time()) if currTimeGET-self.server.lastGET<10: + print('############### Busy') self.send_response(429) self.end_headers() return self.server.lastGET=currTimeGET self.server.GETbusy=True + print('############### _permittedDir') if not self._permittedDir(self.path): + print('############# Not permitted') self._404() self.server.GETbusy=False return # get webfinger endpoint for a person + print('############### _webfinger') if self._webfinger(): self.server.GETbusy=False return + print('############### _webfinger end') # get outbox feed for a person outboxFeed=personOutboxJson(self.server.baseDir,self.server.domain,self.server.port,self.path,self.server.https,maxPostsInFeed) if outboxFeed: @@ -137,6 +148,7 @@ class PubServer(BaseHTTPRequestHandler): return # check that a json file was requested if not self.path.endswith('.json'): + print('############# Not json: '+self.path+' '+self.server.baseDir) self._404() self.server.GETbusy=False return @@ -149,12 +161,13 @@ class PubServer(BaseHTTPRequestHandler): contentJson=json.loads(content) self.wfile.write(json.dumps(contentJson).encode('utf8')) else: + print('############# Unknown file') self._404() self.server.GETbusy=False def do_HEAD(self): self._set_headers('application/json') - + def do_POST(self): if self.server.POSTbusy: currTimePOST=int(time.time()) @@ -200,24 +213,16 @@ class PubServer(BaseHTTPRequestHandler): self.server.POSTbusy=False return - - print('**************** POST get handle') - handle='' + pprint(messageJson) + print('**************** POST get actor url from '+self.server.baseDir) + personUrl=messageJson['object']['attributedTo'] print('**************** POST create session') currSessionTime=int(time.time()) if currSessionTime-self.server.sessionLastUpdate>600: self.server.sessionLastUpdate=currSessionTime self.server.session = createSession(self.server.useTor) - print('**************** POST webfinger the handle') - wfRequest = webfingerHandle(self.server.session,handle,self.server.https,self.server.cachedWebfingers) - if not wfRequest: - print('**************** POST unknown webfinger') - self.send_response(401) - self.end_headers() - self.server.POSTbusy=False - return - print('**************** POST get public key') - pubKey=getPersonPubKey(self.server.session,wfRequest,self.server.personCache) + print('**************** POST get public key of '+personUrl+' from '+self.server.baseDir) + pubKey=getPersonPubKey(self.server.session,personUrl,self.server.personCache) if not pubKey: print('**************** POST no sender public key') self.send_response(401) @@ -233,6 +238,8 @@ class PubServer(BaseHTTPRequestHandler): # send the message back #self._set_headers('application/json') #self.wfile.write(json.dumps(message).encode('utf-8')) + + self.server.receivedMessage=True self.send_response(200) self.end_headers() self.server.POSTbusy=False @@ -244,8 +251,8 @@ def runDaemon(domain: str,port=80,https=True,fedList=[],useTor=False) -> None: print('Invalid domain: ' + domain) return - serverAddress = ('', port) - httpd = HTTPServer(serverAddress, PubServer) + serverAddress = (domain, port) + httpd = ThreadingHTTPServer(serverAddress, PubServer) httpd.domain=domain httpd.port=port httpd.https=https @@ -260,5 +267,6 @@ def runDaemon(domain: str,port=80,https=True,fedList=[],useTor=False) -> None: httpd.lastPOST=0 httpd.GETbusy=False httpd.POSTbusy=False + httpd.receivedMessage=False print('Running ActivityPub daemon on ' + domain + ' port ' + str(port)) httpd.serve_forever() diff --git a/person.py b/person.py index 53e9554f4..f745a3b5b 100644 --- a/person.py +++ b/person.py @@ -123,6 +123,8 @@ def personKeyLookup(domain: str,path: str,baseDir: str) -> str: username=path.replace('/users/','',1).replace('/main-key','') if not validUsername(username): return None + if ':' in domain: + domain=domain.split(':')[0] handle=username.lower()+'@'+domain.lower() filename=baseDir+'/accounts/'+handle.lower()+'.json' if not os.path.isfile(filename): @@ -151,6 +153,8 @@ def personLookup(domain: str,path: str,baseDir: str) -> {}: return None if not validUsername(username): return None + if ':' in domain: + domain=domain.split(':')[0] handle=username.lower()+'@'+domain.lower() filename=baseDir+'/accounts/'+handle.lower()+'.json' if not os.path.isfile(filename): diff --git a/posts.py b/posts.py index 1b8b33407..ffcc402be 100644 --- a/posts.py +++ b/posts.py @@ -107,9 +107,8 @@ def getPersonBox(session,wfRequest: {},personCache: {},boxName='inbox') -> (str, return personJson[boxName],pubKeyId,pubKey,personId -def getPersonPubKey(session,wfRequest,personCache: {}) -> str: +def getPersonPubKey(session,personUrl: str,personCache: {}) -> str: asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} - personUrl = getUserUrl(wfRequest) if not personUrl: return None personJson = getPersonFromCache(personUrl,personCache) @@ -121,7 +120,6 @@ def getPersonPubKey(session,wfRequest,personCache: {}) -> str: pubKey=personJson['publicKey']['publicKeyPem'] storePersonInCache(personUrl,personJson,personCache) - return pubKey def getUserPosts(session,wfRequest: {},maxPosts: int,maxMentions: int,maxEmoji: int,maxAttachments: int,federationList: [],personCache: {}) -> {}: diff --git a/session.py b/session.py index 0ffcc1ffa..10371ff3e 100644 --- a/session.py +++ b/session.py @@ -26,9 +26,11 @@ def getJson(session,url: str,headers: {},params: {}) -> {}: sessionHeaders=headers if params: sessionParams=params - sessionHeaders['User-agent'] = "HotJava/1.1.2 FCS" + sessionHeaders['User-agent'] = "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv)" session.cookies.clear() - return session.get(url, headers=sessionHeaders, params=sessionParams).json() + result=session.get(url, headers=sessionHeaders, params=sessionParams) + print("*****result "+url+' ' + str(result)) + return result.json() def postJson(session,postJsonObject: {},federationList: [],inboxUrl: str,headers: {}) -> str: """Post a json message to the inbox of another person diff --git a/tests.py b/tests.py index c0533a808..131eebfaf 100644 --- a/tests.py +++ b/tests.py @@ -98,21 +98,19 @@ def testThreads(): thr.join() assert thr.isAlive()==False -def createServerAlice(path: str,port: int): +def createServerAlice(path: str,domain: str,port: int,federationList: []): print('Creating test server: Alice on port '+str(port)) if os.path.isdir(path): shutil.rmtree(path) os.mkdir(path) os.chdir(path) - federationList=['127.0.0.1'] username='alice' - domain='127.0.0.1' https=False useTor=False privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,username,domain,port,https,True) deleteAllPosts(username,domain,path) - followPerson(path,username,domain,'bob','127.0.0.1:61936',federationList) - followerOfPerson(path,username,domain,'bob','127.0.0.1:61936',federationList) + followPerson(path,username,domain,'bob','127.0.10.2:61936',federationList) + followerOfPerson(path,username,domain,'bob','127.0.10.2:61936',federationList) createPublicPost(path,username, domain, port,https, "No wise fish would go anywhere without a porpoise", False, True) createPublicPost(path,username, domain, port,https, "Curiouser and curiouser!", False, True) createPublicPost(path,username, domain, port,https, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True) @@ -121,21 +119,19 @@ def createServerAlice(path: str,port: int): print('Server running: Alice') runDaemon(domain,port,https,federationList,useTor) -def createServerBob(path: str,port: int): +def createServerBob(path: str,domain: str,port: int,federationList: []): print('Creating test server: Bob on port '+str(port)) if os.path.isdir(path): shutil.rmtree(path) os.mkdir(path) os.chdir(path) - federationList=['127.0.0.1'] username='bob' - domain='127.0.0.1' https=False useTor=False privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,username,domain,port,https,True) deleteAllPosts(username,domain,path) - followPerson(path,username,domain,'alice','127.0.0.1:61935',federationList) - followerOfPerson(path,username,domain,'alice','127.0.0.1:61935',federationList) + followPerson(path,username,domain,'alice','127.0.10.1:61935',federationList) + followerOfPerson(path,username,domain,'alice','127.0.10.1:61935',federationList) createPublicPost(path,username, domain, port,https, "It's your life, live it your way.", False, True) createPublicPost(path,username, domain, port,https, "One of the things I've realised is that I am very simple", False, True) createPublicPost(path,username, domain, port,https, "Quantum physics is a bit of a passion of mine", False, True) @@ -154,7 +150,7 @@ def testPostMessageBetweenServers(): https=False useTor=False - federationList=['127.0.0.1'] + federationList=['127.0.0.1','127.0.10.1','127.0.10.2'] baseDir=os.getcwd() if not os.path.isdir(baseDir+'/.tests'): @@ -162,22 +158,25 @@ def testPostMessageBetweenServers(): # create the servers aliceDir=baseDir+'/.tests/alice' + aliceDomain='127.0.10.1' alicePort=61935 - thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,alicePort),daemon=True) - thrAlice.start() - assert thrAlice.isAlive()==True + thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList),daemon=True) bobDir=baseDir+'/.tests/bob' + bobDomain='127.0.10.2' bobPort=61936 - thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobPort),daemon=True) + thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList),daemon=True) + + thrAlice.start() thrBob.start() + assert thrAlice.isAlive()==True assert thrBob.isAlive()==True # wait for both servers to be running while not (testServerAliceRunning and testServerBobRunning): time.sleep(1) - time.sleep(5) + time.sleep(6) print('Alice sends to Bob') os.chdir(aliceDir) @@ -192,10 +191,11 @@ def testPostMessageBetweenServers(): ccUrl=None alicePersonCache={} aliceCachedWebfingers={} - sendResult = sendPost(sessionAlice,aliceDir,'alice', '127.0.0.1', alicePort, 'bob', '127.0.0.1', bobPort, ccUrl, https, 'Why is a mouse when it spins?', followersOnly, saveToFile, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) + sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, https, 'Why is a mouse when it spins?', followersOnly, saveToFile, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) print('sendResult: '+str(sendResult)) - time.sleep(15) + for i in range(10): + time.sleep(1) # stop the servers thrAlice.kill() diff --git a/webfinger.py b/webfinger.py index 08b491448..9402c5c5a 100644 --- a/webfinger.py +++ b/webfinger.py @@ -38,23 +38,24 @@ def webfingerHandle(session,handle: str,https: bool,cachedWebfingers: {}) -> {}: username, domain = parseHandle(handle) if not username: return None - wf=getWebfingerFromCache(username+'@'+domain,cachedWebfingers) + wfDomain=domain + if ':' in wfDomain: + wfDomain=wfDomain.split(':')[0] + print('***********cachedWebfingers '+str(cachedWebfingers)) + wf=getWebfingerFromCache(username+'@'+wfDomain,cachedWebfingers) if wf: return wf prefix='https' if not https: - prefix='http' + prefix='http' url = '{}://{}/.well-known/webfinger'.format(prefix,domain) - if ':' in domain: - domain=domain.split(':')[0] - par = {'resource': 'acct:{}'.format(username+'@'+domain)} + par = {'resource': 'acct:{}'.format(username+'@'+wfDomain)} hdr = {'Accept': 'application/jrd+json'} #try: result = getJson(session, url, hdr, par) #except: - # print("Unable to webfinger " + url) - # return None - storeWebfingerInCache(username+'@'+domain, result,cachedWebfingers) + # print("Unable to webfinger " + url + ' ' + str(hdr) + ' ' + str(par)) + storeWebfingerInCache(username+'@'+wfDomain,result,cachedWebfingers) return result def generateMagicKey(publicKeyPem) -> str: @@ -144,23 +145,33 @@ def webfingerMeta() -> str: def webfingerLookup(path: str,baseDir: str) -> {}: """Lookup the webfinger endpoint for an account """ + print('############### _webfinger lookup 1') if not path.startswith('/.well-known/webfinger?'): return None + print('############### _webfinger lookup 2') handle=None if 'resource=acct:' in path: + print('############### _webfinger lookup 3') handle=path.split('resource=acct:')[1].strip() else: + print('############### _webfinger lookup 4') if 'resource=acct%3A' in path: - handle=path.split('resource=acct%3A')[1].replace('%40','@').strip() + print('############### _webfinger lookup 5') + handle=path.split('resource=acct%3A')[1].replace('%40','@').strip() + print('############### _webfinger lookup 6') if not handle: return None + print('############### _webfinger lookup 7') if '&' in handle: handle=handle.split('&')[0].strip() + print('############### _webfinger lookup 8') if '@' not in handle: return None filename=baseDir+'/wfendpoints/'+handle.lower()+'.json' + print('############### _webfinger lookup 9: '+filename) if not os.path.isfile(filename): return None + print('############### _webfinger lookup 10') wfJson={"user": "unknown"} with open(filename, 'r') as fp: wfJson=commentjson.load(fp)