Skip to content
This repository was archived by the owner on Jan 6, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ getDatKey(link, (err, key) => {
})

function start (key) {
var archive = hyperdrive(storage, key, { sparse: true })
var server = http.createServer(serve(archive, { live: true }))
var drive = hyperdrive(storage, key, { sparse: true })
var server = http.createServer(serve(drive, { live: true }))
server.listen(port)
console.log(`Visit http://localhost:${port} to see archive`)
console.log(`Visit http://localhost:${port} to see drive`)

if (key) {
archive.ready(function () {
discovery(archive, { live: true })
drive.ready(function () {
discovery(drive, { live: true })
})
}
}
12 changes: 6 additions & 6 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ var hyperdrive = require('hyperdrive')
var ram = require('random-access-memory')
var serve = require('.')

var archive = hyperdrive(ram)
var drive = hyperdrive(ram)

var server = http.createServer(serve(archive, { exposeHeaders: true, live: true }))
var server = http.createServer(serve(drive, { exposeHeaders: true, live: true }))

archive.writeFile('readme.md', fs.readFileSync('readme.md'))
archive.writeFile('package.json', fs.readFileSync('package.json'))
archive.writeFile('index.js', fs.readFileSync('index.js'))
archive.writeFile('foo/index.html', '<h1>INDEX PAGE YO</h1>')
drive.writeFile('readme.md', fs.readFileSync('readme.md'))
drive.writeFile('package.json', fs.readFileSync('package.json'))
drive.writeFile('index.js', fs.readFileSync('index.js'))
drive.writeFile('foo/index.html', '<h1>INDEX PAGE YO</h1>')

server.listen(8000)

Expand Down
131 changes: 70 additions & 61 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,65 @@ var debug = require('debug')('hyperdrive-http')

module.exports = serve

function serve (archive, opts) {
function serve (drive, opts) {
if (!opts) opts = {}

archive.ready(() => {
debug('serving', archive.key.toString('hex'))
drive.ready(() => {
debug('serving', drive.key.toString('hex'))
})

return corsify(onrequest)

function onrequest (req, res) {
var name = decodeURI(req.url.split('?')[0])
var query = qs.parse(req.url.split('?')[1] || '')
opts.viewSource = false // reset for each request

var wait = (query.wait && Number(query.wait.toString())) || 0
var have = archive.metadata ? archive.metadata.length : -1

if (wait <= have) return checkWebroot()
waitFor(archive, wait, checkWebroot)

function checkWebroot () {
if (opts.web_root) return ready() // used cached version
getManifest(archive, (err, data) => {
if (err || !data) return ready()
if (data.web_root) opts.web_root = data.web_root
ready()
})
if (req.method === 'GET') {
ongetfile(drive, opts, req, res)
} else {
res.statusCode = 500
res.end('illegal method')
}
}
}

function ready () {
var arch = /^\d+$/.test(query.version) ? archive.checkout(Number(query.version)) : archive
if (query.viewSource) {
debug('view source', query)
opts.viewSource = true
}
debug('view', name, 'view dir', name[name.length - 1] === '/')
if (name[name.length - 1] === '/') ondirectory(arch, name, req, res, opts)
else onfile(arch, name, req, res, opts)
function ongetfile (drive, opts, req, res) {
var name = decodeURI(req.url.split('?')[0])
var query = qs.parse(req.url.split('?')[1] || '')
opts.viewSource = false // reset for each request

var wait = (query.wait && Number(query.wait.toString())) || 0
var have = drive.metadata ? drive.metadata.length : -1

if (wait <= have) return checkWebroot()
waitFor(drive, wait, checkWebroot)

function checkWebroot () {
if (opts.web_root) return ready() // used cached version
getManifest(drive, (err, data) => {
if (err || !data) return ready()
if (data.web_root) opts.web_root = data.web_root
ready()
})
}

function ready () {
var arch = /^\d+$/.test(query.version) ? drive.checkout(Number(query.version)) : drive
if (query.viewSource) {
debug('view source', query)
opts.viewSource = true
}
debug('view', name, 'view dir', name[name.length - 1] === '/')
if (name[name.length - 1] === '/') ondirectory(arch, name, req, res, opts)
else onfile(arch, name, req, res, opts)
}
}

function onfile (archive, name, req, res, opts) {
archive.stat(name, function (err, st) {
if (err) return on404(archive, req, res)
function onfile (drive, name, req, res, opts) {
drive.stat(name, function (err, st) {
if (err) return on404(drive, req, res)

if (st.isDirectory()) {
res.statusCode = 302
res.setHeader('Location', name + '/')
ondirectory(archive, name + '/', req, res, opts)
ondirectory(drive, name + '/', req, res, opts)
return
}

Expand All @@ -75,42 +84,42 @@ function onfile (archive, name, req, res, opts) {
}

if (req.method === 'HEAD') return res.end()
pump(archive.createReadStream(name, r), res)
pump(drive.createReadStream(name, r), res)
})
}

function on404 (archive, req, res) {
getManifest(archive, function (err, parsed) {
function on404 (drive, req, res) {
getManifest(drive, function (err, parsed) {
if (err) return onerror(res, 404, err)

var fallbackPage = parsed.fallback_page

if (!fallbackPage) return onerror(res, 404, new Error('Not Found, No Fallback'))

archive.stat((parsed.web_root || '/') + fallbackPage, function (err) {
drive.stat((parsed.web_root || '/') + fallbackPage, function (err) {
if (err) return onerror(res, 404, err)
onfile(archive, fallbackPage, req, res)
onfile(drive, fallbackPage, req, res)
})
})
}

function ondirectory (archive, name, req, res, opts) {
function ondirectory (drive, name, req, res, opts) {
debug('ondirectory:', name, 'options', opts)
if (opts.viewSource) return ondirectoryindex(archive, name, req, res, opts)
if (opts.viewSource) return ondirectoryindex(drive, name, req, res, opts)

if (name === '/' && opts.web_root) name = opts.web_root
if (name[name.length - 1] !== '/') name = name + '/'
archive.stat(name + 'index.html', function (err) {
if (err) return ondirectoryindex(archive, name, req, res, opts)
onfile(archive, name + 'index.html', req, res)
drive.stat(name + 'index.html', function (err) {
if (err) return ondirectoryindex(drive, name, req, res, opts)
onfile(drive, name + 'index.html', req, res)
})
}

function ondirectoryindex (archive, name, req, res, opts) {
list(archive, name, function (err, entries) {
function ondirectoryindex (drive, name, req, res, opts) {
list(drive, name, function (err, entries) {
if (err) entries = []

var wait = archive.metadata ? archive.metadata.length + 1 : 0
var wait = drive.metadata ? drive.metadata.length + 1 : 0
var script = `
function liveUpdate () {
var xhr = new XMLHttpRequest()
Expand All @@ -132,21 +141,21 @@ function ondirectoryindex (archive, name, req, res, opts) {
liveUpdate()
`

var footer = opts.footer ? 'Archive version: ' + archive.version : null
var html = toHTML({ directory: name, script: (!opts.live || archive._checkout) ? null : script, footer: footer }, entries)
var footer = opts.footer ? 'Archive version: ' + drive.version : null
var html = toHTML({ directory: name, script: (!opts.live || drive._checkout) ? null : script, footer: footer }, entries)
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.setHeader('Content-Length', Buffer.byteLength(html))
if (opts.exposeHeaders) {
res.setHeader('Hyperdrive-Key', archive.key.toString('hex'))
res.setHeader('Hyperdrive-Version', archive.version)
res.setHeader('Hyperdrive-Key', drive.key.toString('hex'))
res.setHeader('Hyperdrive-Version', drive.version)
res.setHeader('Hyperdrive-Http-Version', pkg.version)
}
res.end(html)
})
}

function getManifest (archive, cb) {
archive.readFile('/dat.json', 'utf-8', function (err, data) {
function getManifest (drive, cb) {
drive.readFile('/dat.json', 'utf-8', function (err, data) {
if (err) return cb(err)
try {
var parsed = JSON.parse(data)
Expand All @@ -162,12 +171,12 @@ function getManifest (archive, cb) {
})
}

function waitFor (archive, until, cb) { // this feels a bit hacky, TODO: make less complicated?
archive.setMaxListeners(0)
if (!archive.metadata) archive.once('ready', waitFor.bind(null, archive, until, cb))
if (archive.metadata.length >= until) return cb()
archive.metadata.setMaxListeners(0)
archive.metadata.update(waitFor.bind(null, archive, until, cb))
function waitFor (drive, until, cb) { // this feels a bit hacky, TODO: make less complicated?
drive.setMaxListeners(0)
if (!drive.metadata) drive.once('ready', waitFor.bind(null, drive, until, cb))
if (drive.metadata.length >= until) return cb()
drive.metadata.setMaxListeners(0)
drive.metadata.update(waitFor.bind(null, drive, until, cb))
}

function onerror (res, status, err) {
Expand All @@ -178,8 +187,8 @@ function onerror (res, status, err) {
res.end(err.stack)
}

function list (archive, name, cb) {
archive.readdir(name, function (err, names) {
function list (drive, name, cb) {
drive.readdir(name, function (err, names) {
if (err) return cb(err)

var error = null
Expand All @@ -190,7 +199,7 @@ function list (archive, name, cb) {
for (var i = 0; i < names.length; i++) stat(name + names[i], names[i])

function stat (name, base) {
archive.stat(name, function (err, st) {
drive.stat(name, function (err, st) {
if (err) error = err

if (st) {
Expand Down
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Handle Hyper[drive|core] HTTP Requests",
"main": "index.js",
"scripts": {
"test": "standard"
"test": "tape test/*.js"
},
"keywords": [
"hypercore",
Expand All @@ -29,10 +29,13 @@
},
"homepage": "https://github.com/joehand/hyperdrive-http",
"devDependencies": {
"collect-stream": "^1.2.1",
"dat-encoding": "^5.0.1",
"dat-link-resolve": "^2.3.0",
"hyperdiscovery": "^8.0.0",
"hyperdrive": "^9.0.0",
"hyperdrive": "^10.12.3",
"random-access-memory": "^3.0.0",
"standard": "^12.0.1"
"request": "^2.88.2",
"standard": "^14.3.4",
"tape": "^5.0.1"
}
}
22 changes: 11 additions & 11 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Hyperdrive Http

Serve a [hyperdrive](https://github.com/mafintosh/hyperdrive) archive over HTTP. For an example of use, see [dat.haus](https://github.com/juliangruber/dat.haus).
Serve a [hyperdrive](https://github.com/mafintosh/hyperdrive) drive over HTTP. For an example of use, see [dat.haus](https://github.com/juliangruber/dat.haus).

[![Travis](https://api.travis-ci.org/joehand/hyperdrive-http.svg)](https://travis-ci.org/joehand/hyperdrive-http)

Expand All @@ -10,7 +10,7 @@ Hyperdrive-http returns a function to call when you receive a http request:

```js
var server = http.createServer().listen(8000)
server.on('request', hyperdriveHttp(archive))
server.on('request', hyperdriveHttp(drive))
```

Supports manifest options in `dat.json`:
Expand All @@ -23,12 +23,12 @@ Supports manifest options in `dat.json`:
To use hyperdrive-http you will need to:

* Create your own http server
* Setup your hyperdrive archive
* For remote archives, connect to the swarm
* Setup your hyperdrive drive
* For remote drives, connect to the swarm

## API

Hyperdrive works with many archives/feeds or a single archive.
Hyperdrive works with many drives/feeds or a single drive.

#### Options

Expand All @@ -37,19 +37,19 @@ Hyperdrive works with many archives/feeds or a single archive.
Hyperdrive-Key: de2a51bbaf8a5545eff82c999f15e1fd29637b3f16db94633cb6e2e0c324f833
Hyperdrive-Version: 4
```
- `live` - If set to `true` will reload a directly listing if the archive receives updates.
- `footer` - Add a footer to your HTML page. Automatically adds archive version number to footer.
- `live` - If set to `true` will reload a directly listing if the drive receives updates.
- `footer` - Add a footer to your HTML page. Automatically adds drive version number to footer.

### URL Format

Hyperdrive-http responds to any URL with a specific format. If the URL does cannot be parsed, it will return a 404.

* Get archive listing: `http://archive-example.com/`
* Get file from archive: `http://archive-example.com/filename.pdf`
* Get drive listing: `http://drive-example.com/`
* Get file from drive: `http://drive-example.com/filename.pdf`

If a directory in the archive contains an `index.html` page that file is returned instead of the directory listing. If you'd like to view files use a query string:
If a directory in the drive contains an `index.html` page that file is returned instead of the directory listing. If you'd like to view files use a query string:

* View files: `http://archive-example.com/?viewSource=true`
* View files: `http://drive-example.com/?viewSource=true`


## CLI
Expand Down
1 change: 0 additions & 1 deletion test/404.html

This file was deleted.

29 changes: 29 additions & 0 deletions test/basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const http = require('http')
const request = require('request')
const ram = require('random-access-memory')
const hyperdrive = require('hyperdrive')
const tape = require('tape')
const hyperdriveHttp = require('..')

const PORT = 8000
const BASE = `http://localhost:${PORT}`
tape.only('basic get', t => {
const server = http.createServer()
const drive = hyperdrive(ram)
const onrequest = hyperdriveHttp(drive)

server.listen(PORT)
server.once('request', onrequest)

drive.writeFile('hello', 'world', (err) => {
t.error(err, 'write ok')
request(BASE + '/hello', (err, res, body) => {
t.error(err, 'no request error')
t.equal(res.statusCode, 200)
t.ok(body, 'responds with file')
t.equal(body, 'world', 'content matches')
server.close()
t.end()
})
})
})
6 changes: 0 additions & 6 deletions test/dat.json

This file was deleted.

Loading