Skip to content

Commit

Permalink
Merge pull request #12 from hbz/10-deployTestServer
Browse files Browse the repository at this point in the history
Deploy test server
  • Loading branch information
literarymachine committed May 15, 2019
2 parents 9788beb + a0945fc commit 12287d8
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 111 deletions.
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,11 @@ Basic setup:
$ npm install
$ npm test
$ npm start
$ curl -i localhost:3000/inbox
HTTP/1.1 400 Bad Request
$ curl -i "localhost:3000/inbox?target=https://lobid.org/gnd/118696432"
HTTP/1.1 200 OK

To test pubsub interactively, start the server in one terminal:

$ npm start

Start the publisher, take a note of the $address and $port to provide a valid
Start the publisher, take a note of the `$address` and `$port` to provide a valid
topic URL in the next step below:

$ node src/publisher.js http://127.0.0.1/hub http://127.0.0.1/inbox
$ node src/publisher.js http://localhost:3000/hub http://localhost:3000/inbox

Start the subscriber, subscribing to a publisher topic URL (the $address and $port from
above) and a random path:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"supertest": "^3.4.2"
},
"dependencies": {
"content-type": "^1.0.4",
"express": "^4.16.4",
"parse-link-header": "^1.0.1",
"superagent": "^4.1.0",
Expand Down
12 changes: 8 additions & 4 deletions src/publisher.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ const publisherServer = http.createServer()

const linkHeadersCallback = (req, res) => {
const { address, port } = publisherServer.address()
console.log(req.url)
const linkHeaders = [
'<http://localhost:3000/hub>; rel="hub"',
`<${process.argv[2]}>; rel="hub"`,
`<http://${address}:${port}${req.url}>; rel="self"`,
`<http://localhost:3000/inbox?target=http://${address}:${port}${req.url}>; rel="http://www.w3.org/ns/ldp#inbox"`
`<${process.argv[3]}?target=http://${address}:${port}${req.url}>; rel="http://www.w3.org/ns/ldp#inbox"`
]
res.setHeader('Link', linkHeaders)
res.setHeader('Link', linkHeaders.join(', '))
res.end()
}
publisherServer.on('request', linkHeadersCallback)

if (process.argv.length !== 4) {
console.error('Usage: node publisher.js hub inbox')
process.exit(1)
}

publisherServer.listen(0, '127.0.0.1', async () => {
console.log('Publisher listening', publisherServer.address())
})
46 changes: 36 additions & 10 deletions src/pubsub.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import request from 'superagent'
import crypto from 'crypto'
import parseLinkHeader from 'parse-link-header'
import WebSocket from 'ws'
import url from 'url'
import contentType from 'content-type'

const DEFAULT_LEASE = 7
const LDP_INBOX = 'http://www.w3.org/ns/ldp#inbox'

const validateTarget = async target => {
const validateTarget = async (publicHost, target) => {
if (!target) {
throw new Error('Target required')
}
Expand All @@ -16,13 +18,13 @@ const validateTarget = async target => {
if (
!linkHeader ||
!linkHeader[LDP_INBOX] ||
linkHeader[LDP_INBOX].url !== `http://localhost:3000/inbox?target=${target}`
linkHeader[LDP_INBOX].url !== `${publicHost}/inbox?target=${target}`
) {
throw new Error('Invalid link headers for target URL')
}
}

const validateRequest = async (callback, mode, topic, lease = DEFAULT_LEASE) => {
const validateRequest = async (publicHost, callback, mode, topic, lease = DEFAULT_LEASE) => {
if (!callback || !/subscribe|unsubscribe/.test(mode) || !topic) {
throw new Error('Invalid request')
}
Expand All @@ -32,7 +34,7 @@ const validateRequest = async (callback, mode, topic, lease = DEFAULT_LEASE) =>
!linkHeader ||
!linkHeader.hub ||
!linkHeader.self ||
linkHeader.hub.url !== 'http://localhost:3000/hub' ||
linkHeader.hub.url !== `${publicHost}/hub` ||
linkHeader.self.url !== topic
) {
throw new Error('Invalid topic or hub URL')
Expand Down Expand Up @@ -60,26 +62,46 @@ const pubsub = db => {
app.use(express.json({ type: ['application/ld+json', 'application/json'] }))
app.use(express.urlencoded({ extended: true }))

app.use((req, res, next) => {
req.publicHost = url.format({
protocol: req.get('x-forwarded-proto') || req.protocol,
host: req.get('x-forwarded-host') || req.get('host')
})
next()
})

app.get('/inbox', async (req, res) => {
try {
await validateTarget(req.query.target)
await validateTarget(req.publicHost, req.query.target)
} catch (e) {
return res.status(400).send(e.message)
}
res.header('Content-Type', 'application/ld+json')
res.status(200).send({
'@context': 'http://www.w3.org/ns/ldp',
'@id': `${req.protocol}://${req.get('host')}${req.url}`,
'@id': `${req.publicHost}${req.url}`,
'contains': []
})
})

app.options('/inbox', async (req, res) => {
try {
await validateTarget(req.publicHost, req.query.target)
} catch (e) {
return res.status(400).send(e.message)
}
res.header('Accept-Post', 'application/ld+json')
res.status(200).send()
})

app.post('/inbox', async (req, res) => {
const target = req.query.target
const { type } = contentType.parse(req)
try {
if (req.headers['content-type'] !== 'application/ld+json') {
if (type !== 'application/ld+json') {
throw new Error('Invalid Content-Type')
}
await validateTarget(target)
await validateTarget(req.publicHost, target)
} catch (e) {
return res.status(400).send(e.message)
}
Expand Down Expand Up @@ -110,7 +132,7 @@ const pubsub = db => {
} = req.body

try {
await validateRequest(callback, mode, topic, lease)
await validateRequest(req.publicHost, callback, mode, topic, lease)
} catch (e) {
return res.status(400).send(e.message)
}
Expand All @@ -133,11 +155,15 @@ const pubsub = db => {

const wss = new WebSocket.Server({ noServer: true })
wss.on('connection', (ws, req) => {
req.publicHost = url.format({
protocol: req.headers['x-forwarded-proto'] || req.protocol || 'http',
host: req.headers['x-forwarded-host'] || req.headers['host']
})
ws.on('message', async message => {
const { mode, topic } = JSON.parse(message)
const callback = notification => ws.send(notification)
try {
await validateRequest(callback, mode, topic)
await validateRequest(req.publicHost, callback, mode, topic)
} catch (e) {
ws.send(JSON.stringify({ mode: 'reject', topic }))
return // discard subscription request
Expand Down
Loading

0 comments on commit 12287d8

Please sign in to comment.