OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / test / _xpeer-attach.mjs

103 lines · javascript ref:
// Cross-peer attachment fetch: upload to one daemon, read from another.
// Proves OverlayProtocolV2 transports type:'local' chunks (issue #18 verify #3).
//
// Run on tabertron — uses local pear-git-v3 daemon (port 5005, prefix /git).
// Reads back via stortron over its public HTTPS endpoint.

import http from 'http'
import https from 'https'
import b4a from 'b4a'
import crypto from 'hypercore-crypto'
import { signTestEnvelope } from '../lib/auth/envelope.js'

const TABERTRON = 'http://127.0.0.1:5005/git'
const STORTRON = 'https://7rnx.net/git'

function reqRaw (mod, opts, body) {
  return new Promise((resolve, reject) => {
    const req = mod.request(opts, (res) => {
      const parts = []
      res.on('data', c => parts.push(c))
      res.on('end', () => resolve({
        status: res.statusCode,
        body: b4a.concat(parts),
        setCookie: res.headers['set-cookie']
      }))
      res.on('error', reject)
    })
    req.on('error', reject)
    if (body) req.write(body)
    req.end()
  })
}

async function postJson (urlStr, body, headers = {}) {
  const u = new URL(urlStr)
  const json = JSON.stringify(body || {})
  const mod = u.protocol === 'https:' ? https : http
  const port = u.port || (u.protocol === 'https:' ? 443 : 80)
  const res = await reqRaw(mod, {
    method: 'POST', hostname: u.hostname, port, path: u.pathname,
    headers: { 'Content-Type': 'application/json', 'Content-Length': b4a.byteLength(json), ...headers }
  }, json)
  let parsed; try { parsed = JSON.parse(res.body.toString('utf8')) } catch { parsed = res.body.toString('utf8') }
  return { status: res.status, body: parsed, setCookie: res.setCookie }
}

async function loginHttp (base) {
  const { an } = await import('anproto')
  const kp = await an.gen()
  const env = await signTestEnvelope({ keypair: kp, method: 'whoami', params: {} })
  const res = await postJson(base + '/api/rpc', env)
  if (!res.body.ok) throw new Error('login failed: ' + JSON.stringify(res.body))
  const setCookie = res.setCookie
  return Array.isArray(setCookie) ? setCookie[0].split(';')[0] : setCookie.split(';')[0]
}

async function uploadBytes (base, bytes, cookie) {
  const u = new URL(base + '/api/upload')
  const mod = u.protocol === 'https:' ? https : http
  const port = u.port || (u.protocol === 'https:' ? 443 : 80)
  const res = await reqRaw(mod, {
    method: 'POST', hostname: u.hostname, port, path: u.pathname,
    headers: { 'Content-Type': 'application/octet-stream', 'Content-Length': bytes.length, Cookie: cookie }
  }, bytes)
  let parsed; try { parsed = JSON.parse(res.body.toString('utf8')) } catch { parsed = res.body.toString('utf8') }
  return { status: res.status, body: parsed }
}

async function fetchFile (base, path) {
  const u = new URL(base + '/files/raw?path=' + encodeURIComponent(path))
  const mod = u.protocol === 'https:' ? https : http
  const port = u.port || (u.protocol === 'https:' ? 443 : 80)
  return reqRaw(mod, {
    method: 'GET', hostname: u.hostname, port, path: u.pathname + u.search
  })
}

console.log('[1] login to tabertron (HTTP, local)')
const tabCookie = await loginHttp(TABERTRON)
console.log('    ok')

console.log('[2] upload random 1 MB to tabertron (multi-chunk)')
const data = crypto.randomBytes(1024 * 1024)
const upload = await uploadBytes(TABERTRON, data, tabCookie)
console.log('    status=' + upload.status + ' hash=' + upload.body.hash + '  size=' + upload.body.size)
console.log('    path=' + upload.body.path)

console.log('[3] fetch from tabertron itself (sanity)')
const tabRead = await fetchFile(TABERTRON, upload.body.path)
console.log('    status=' + tabRead.status + ' size=' + tabRead.body.length + '  byte-equal=' + tabRead.body.equals(data))

console.log('[4] fetch from stortron via public HTTPS — first hit may need peer transport time')
const t0 = Date.now()
const storRead = await fetchFile(STORTRON, upload.body.path)
const elapsed = Date.now() - t0
console.log('    status=' + storRead.status + ' size=' + storRead.body.length + '  byte-equal=' + storRead.body.equals(data) + '  elapsed=' + elapsed + 'ms')

if (storRead.status !== 200 || !storRead.body.equals(data)) {
  console.error('FAIL: cross-peer attachment fetch broken')
  process.exit(1)
}
console.log('PASS: tabertron→stortron attachment cross-peer fetch works')