mirror of https://gitlab.com/bashrc2/epicyon
Test servers on different ip addresses
parent
22090a1aef
commit
e2de1d1b9c
46
daemon.py
46
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("<html><head></head><body><h1>404 Not Found</h1></body></html>".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()
|
||||
|
|
|
@ -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):
|
||||
|
|
4
posts.py
4
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: {}) -> {}:
|
||||
|
|
|
@ -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
|
||||
|
|
36
tests.py
36
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()
|
||||
|
|
29
webfinger.py
29
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)
|
||||
|
|
Loading…
Reference in New Issue