Skip to content

Commit

Permalink
perf: drop simple-get (#2448)
Browse files Browse the repository at this point in the history
* perf: drop simple-get

* fix: remove http polyfill

* fix: resolve http to false

* fix: add cache-control header
  • Loading branch information
ThaUnknown authored Jan 30, 2023
1 parent c5aae76 commit b3c8376
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 142 deletions.
2 changes: 1 addition & 1 deletion dist/sw.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/webtorrent.chromeapp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/webtorrent.chromeapp.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/webtorrent.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/webtorrent.min.js.map

Large diffs are not rendered by default.

82 changes: 40 additions & 42 deletions lib/torrent.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import cpus from 'cpus'
import debugFactory from 'debug'
import Discovery from 'torrent-discovery'
import FSChunkStore from 'fs-chunk-store' // browser: `hybrid-chunk-store`
import get from 'simple-get'
import fetch from 'cross-fetch-ponyfill'
import ImmediateChunkStore from 'immediate-chunk-store'
import ltDontHave from 'lt_donthave'
import MemoryChunkStore from 'memory-chunk-store'
Expand Down Expand Up @@ -410,65 +410,67 @@ export default class Torrent extends EventEmitter {

const urls = Array.isArray(this.xs) ? this.xs : [this.xs]

self._xsRequestsController = new AbortController()

const signal = self._xsRequestsController.signal

const tasks = urls.map(url => cb => {
getMetadataFromURL(url, cb)
})
parallel(tasks)

function getMetadataFromURL (url, cb) {
async function getMetadataFromURL (url, cb) {
if (url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0) {
self.emit('warning', new Error(`skipping non-http xs param: ${url}`))
return cb(null)
}

const opts = {
url,
method: 'GET',
headers: {
'user-agent': USER_AGENT
}
},
signal
}
let req
let res
try {
req = get.concat(opts, onResponse)
res = await fetch(url, opts)
} catch (err) {
self.emit('warning', new Error(`skipping invalid url xs param: ${url}`))
self.emit('warning', new Error(`http error from xs param: ${url}`))
return cb(null)
}

self._xsRequests.push(req)
if (self.destroyed) return cb(null)
if (self.metadata) return cb(null)

async function onResponse (err, res, torrent) {
if (self.destroyed) return cb(null)
if (self.metadata) return cb(null)

if (err) {
self.emit('warning', new Error(`http error from xs param: ${url}`))
return cb(null)
}
if (res.statusCode !== 200) {
self.emit('warning', new Error(`non-200 status code ${res.statusCode} from xs param: ${url}`))
return cb(null)
}

let parsedTorrent
try {
parsedTorrent = await parseTorrent(torrent)
} catch (err) {}
if (res.status !== 200) {
self.emit('warning', new Error(`non-200 status code ${res.status} from xs param: ${url}`))
return cb(null)
}
let torrent
try {
torrent = new Uint8Array(await res.arrayBuffer())
} catch (e) {
self.emit('warning', e)
return cb(null)
}

if (!parsedTorrent) {
self.emit('warning', new Error(`got invalid torrent file from xs param: ${url}`))
return cb(null)
}
let parsedTorrent
try {
parsedTorrent = await parseTorrent(torrent)
} catch (err) {}

if (parsedTorrent.infoHash !== self.infoHash) {
self.emit('warning', new Error(`got torrent file with incorrect info hash from xs param: ${url}`))
return cb(null)
}
if (!parsedTorrent) {
self.emit('warning', new Error(`got invalid torrent file from xs param: ${url}`))
return cb(null)
}

self._onMetadata(parsedTorrent)
cb(null)
if (parsedTorrent.infoHash !== self.infoHash) {
self.emit('warning', new Error(`got torrent file with incorrect info hash from xs param: ${url}`))
return cb(null)
}
self._onMetadata(parsedTorrent)
cb(null)
}
}

Expand All @@ -479,10 +481,8 @@ export default class Torrent extends EventEmitter {
if (this.metadata || this.destroyed) return
this._debug('got metadata')

this._xsRequests.forEach(req => {
req.abort()
})
this._xsRequests = []
this._xsRequestsController?.abort()
this._xsRequestsController = null

let parsedTorrent
if (metadata && metadata.infoHash) {
Expand Down Expand Up @@ -760,9 +760,7 @@ export default class Torrent extends EventEmitter {

clearInterval(this._noPeersIntervalId)

this._xsRequests.forEach(req => {
req.abort()
})
this._xsRequestsController?.abort()

if (this._rarityMap) {
this._rarityMap.destroy()
Expand Down
124 changes: 33 additions & 91 deletions lib/webconn.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import BitField from 'bitfield'
import debugFactory from 'debug'
import get from 'simple-get'
import fetch from 'cross-fetch-ponyfill'
import ltDontHave from 'lt_donthave'
import { hash } from 'uint8-util'
import { hash, concat } from 'uint8-util'
import Wire from 'bittorrent-protocol'
import once from 'once'

import info from '../package.json' assert { type: 'json' }

Expand Down Expand Up @@ -79,7 +80,8 @@ export default class WebConn extends Wire {
})
}

httpRequest (pieceIndex, offset, length, cb) {
async httpRequest (pieceIndex, offset, length, cb) {
cb = once(cb)
const pieceOffset = pieceIndex * this._torrent.pieceLength
const rangeStart = pieceOffset + offset /* offset within whole torrent */
const rangeEnd = rangeStart + length - 1
Expand Down Expand Up @@ -115,100 +117,40 @@ export default class WebConn extends Wire {
})
}

// Now make all the HTTP requests we need in order to load this piece
// Usually that's one requests, but sometimes it will be multiple
// Send requests in parallel and wait for them all to come back
let numRequestsSucceeded = 0
let hasError = false

let ret
if (requests.length > 1) {
ret = new Uint8Array(length)
}

requests.forEach(request => {
const url = request.url
const start = request.start
const end = request.end
const chunks = await Promise.all(requests.map(async ({ start, end, url }) => {
debug(
'Requesting url=%s pieceIndex=%d offset=%d length=%d start=%d end=%d',
url, pieceIndex, offset, length, start, end
)
const opts = {
url,
method: 'GET',
headers: {
'user-agent': `WebTorrent/${VERSION} (https://webtorrent.io)`,
range: `bytes=${start}-${end}`
},
timeout: SOCKET_TIMEOUT
let res
try {
res = await fetch(url, {
cache: 'no-store',
method: 'GET',
headers: {
'Cache-Control': 'no-store',
'user-agent': `WebTorrent/${VERSION} (https://webtorrent.io)`,
range: `bytes=${start}-${end}`
},
signal: AbortSignal.timeout(SOCKET_TIMEOUT)
})
} catch (e) {
return cb(e)
}
function onResponse (res, data) {
if (res.statusCode < 200 || res.statusCode >= 300) {
if (hasError) return
hasError = true
return cb(new Error(`Unexpected HTTP status code ${res.statusCode}`))
}
debug('Got data of length %d', data.length)

if (requests.length === 1) {
// Common case: fetch piece in a single HTTP request, return directly
cb(null, data)
} else {
// Rare case: reconstruct multiple HTTP requests across 2+ files into one
// piece buffer
ret.set(data, request.fileOffsetInRange)
if (++numRequestsSucceeded === requests.length) {
cb(null, ret)
}
}
if (!res.ok) return cb(new Error(`Unexpected HTTP status code ${res.status}`))
let data
try {
data = new Uint8Array(await res.arrayBuffer())
} catch (e) {
return cb(e)
}
get.concat(opts, (err, res, data) => {
if (hasError) return
if (err) {
// Browsers allow HTTP redirects for simple cross-origin
// requests but not for requests that require preflight.
// Use a simple request to unravel any redirects and get the
// final URL. Retry the original request with the new URL if
// it's different.
//
// This test is imperfect but it's simple and good for common
// cases. It catches all cross-origin cases but matches a few
// same-origin cases too.
if (typeof window === 'undefined' || url.startsWith(`${window.location.origin}/`)) {
hasError = true
return cb(err)
}

return get.head(url, (errHead, res) => {
if (hasError) return
if (errHead) {
hasError = true
return cb(errHead)
}
if (res.statusCode < 200 || res.statusCode >= 300) {
hasError = true
return cb(new Error(`Unexpected HTTP status code ${res.statusCode}`))
}
if (res.url === url) {
hasError = true
return cb(err)
}

opts.url = res.url
get.concat(opts, (err, res, data) => {
if (hasError) return
if (err) {
hasError = true
return cb(err)
}
onResponse(res, data)
})
})
}
onResponse(res, data)
})
})

debug('Got data of length %d', data.length)

return data
}))

cb(null, concat(chunks))
}

destroy () {
Expand Down
Loading

0 comments on commit b3c8376

Please sign in to comment.