Skip to content

Commit

Permalink
feat: handle redirects via option default=true (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
SgtPooki authored Nov 7, 2023
1 parent e93d334 commit 45433cc
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ $ docker run -it -p 8080:8080 -e DEBUG="helia-http-gateway" helia
| `USE_TRUSTLESS_GATEWAYS` | Whether to fetch content from trustless-gateways or not | `true` |
| `TRUSTLESS_GATEWAYS` | Comma separated list of trusted gateways to fetch content from | [Defined in Helia](https://github.com/ipfs/helia/blob/main/packages/helia/src/block-brokers/trustless-gateway/index.ts) |
| `USE_LIBP2P` | Whether to use libp2p networking | `true` |
| `RESOLVE_REDIRECTS` | Whether to resolve redirects before looking up dnslink entries | `true` |

<!--
TODO: currently broken when used in docker, but they work when running locally (you can cache datastore and blockstore locally to speed things up if you want)
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const DEBUG = process.env.DEBUG ?? ''

export const USE_SUBDOMAINS = process.env.USE_SUBDOMAINS === 'true'

export const RESOLVE_REDIRECTS = process.env.RESOLVE_REDIRECTS !== 'false'

/**
* If set to any value other than 'true', we will disable prometheus metrics.
*
Expand Down
50 changes: 48 additions & 2 deletions src/heliaFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ interface HeliaPathParts {
relativePath: string
}

interface HeliaFetchConfig {
resolveRedirects: boolean
}

/**
* Fetches files from IPFS or IPNS
*/
Expand All @@ -31,6 +35,7 @@ export class HeliaFetch {
private readonly rootFilePatterns: string[]
public node!: Helia
public ready: Promise<void>
private readonly config: HeliaFetchConfig
private readonly ipnsResolutionCache = new LRUCache<string, string>({
max: 10000,
ttl: 1000 * 60 * 60 * 24
Expand All @@ -39,11 +44,13 @@ export class HeliaFetch {
constructor ({
node,
rootFilePatterns = ROOT_FILE_PATTERNS,
logger
logger,
config = {}
}: {
node?: Helia
rootFilePatterns?: string[]
logger?: debug.Debugger
config?: Partial<HeliaFetchConfig>
} = {}) {
// setup a logger
if (logger !== undefined) {
Expand All @@ -55,6 +62,10 @@ export class HeliaFetch {
if (node !== undefined) {
this.node = node
}
this.config = {
resolveRedirects: true,
...config
}
this.rootFilePatterns = rootFilePatterns
this.ready = this.init()
this.log('Initialized')
Expand Down Expand Up @@ -151,17 +162,52 @@ export class HeliaFetch {
}
}

/**
* Checks if a redirect is needed and returns either the original address or the redirect address.
*/
private async checkForRedirect (address: string, options?: Parameters<UnixFS['cat']>[1]): Promise<string> {
if (!this.config.resolveRedirects) {
return address
}
try {
this.log('Checking for redirect of:', address)
const redirectCheckResponse = await fetch(`http://${address}`, { method: 'HEAD', redirect: 'manual', ...options })
if ([301, 302, 307, 308].includes(redirectCheckResponse.status)) {
this.log('Redirect statuscode :', redirectCheckResponse.status)
const redirectText = redirectCheckResponse.headers.get('location')
if (redirectText == null) {
this.log('No location header')
return address
} else {
const redirectUrl = new URL(redirectText)
this.log('Redirect found:', redirectUrl.host)
return redirectUrl.host
}
}
} catch {
// ignore errors on redirect checks
this.log('Error checking for redirect for url')
}
return address
}

/**
* Fetch IPNS content.
*/
private async fetchIpns (address: string, options?: Parameters<UnixFS['cat']>[1]): Promise<AsyncIterable<Uint8Array>> {
if (!this.ipnsResolutionCache.has(address)) {
this.log('Fetching from DNS over HTTP:', address)
const txtRecords = await this.dohResolver.resolveTxt(`_dnslink.${address}`)
const redirectAddress = await this.checkForRedirect(address)
const txtRecords = await this.dohResolver.resolveTxt(`_dnslink.${redirectAddress}`)
const pathEntry = txtRecords.find(([record]) => record.startsWith('dnslink='))
const path = pathEntry?.[0].replace('dnslink=', '')
this.log('Got Path from DNS over HTTP:', path)
this.ipnsResolutionCache.set(address, path ?? 'not-found')
if (redirectAddress !== address) {
this.ipnsResolutionCache.set(redirectAddress, path ?? 'not-found')
}
// we don't do anything with this, but want to fail if it is not a valid path
this.parsePath(path ?? '')
}
if (this.ipnsResolutionCache.get(address) === 'not-found') {
this.log('No Path found:', address)
Expand Down
7 changes: 5 additions & 2 deletions src/heliaServer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type FastifyReply, type FastifyRequest, type RouteGenericInterface } from 'fastify'
import { CID } from 'multiformats'
import { USE_SUBDOMAINS } from './constants.js'
import { USE_SUBDOMAINS, RESOLVE_REDIRECTS } from './constants.js'
import { DEFAULT_MIME_TYPE, parseContentType } from './contentType.js'
import { getCustomHelia } from './getCustomHelia.js'
import { HeliaFetch } from './heliaFetch.js'
Expand Down Expand Up @@ -53,7 +53,10 @@ export class HeliaServer {
async init (): Promise<void> {
this.heliaFetch = new HeliaFetch({
logger: this.log,
node: await getCustomHelia()
node: await getCustomHelia(),
config: {
resolveRedirects: RESOLVE_REDIRECTS
}
})
await this.heliaFetch.ready
// eslint-disable-next-line no-console
Expand Down

0 comments on commit 45433cc

Please sign in to comment.