~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
239 lines · 7.3 KB Raw History
// The edge cache in the Worker entry: site-hostname GETs are served from
// caches.default so repeat traffic never crosses to the Durable Object,
// while the PDS's own hostname bypasses the cache entirely.

import { afterEach, describe, expect, it, vi } from 'vitest';
import worker, {
  responseStorable,
  siteCacheable,
  siteHost,
} from '../src/index.js';

const ENV_BASE = {
  HOSTNAME: 'pds.example.com',
  PDS_EXPERIMENTAL_SITE_DOMAIN: 'example.com',
};

/** @param {Request} request @param {Record<string, string>} [env] */
function cacheableFor(request, env = {}) {
  return siteCacheable(request, /** @type {any} */ ({ ...ENV_BASE, ...env }));
}

describe('siteHost', () => {
  /** @param {string} url @param {Record<string, string>} [env] */
  const hostFor = (url, env = {}) =>
    siteHost(new Request(url), /** @type {any} */ ({ ...ENV_BASE, ...env }));

  it('accepts only hostnames under the site domain, never the PDS', () => {
    expect(hostFor('https://blog.example.com/')).toBe(true);
    expect(hostFor('https://example.com/')).toBe(true);
    expect(hostFor('https://pds.example.com/')).toBe(false);
    expect(hostFor('https://other.test/')).toBe(false);
    expect(hostFor('http://localhost:8787/')).toBe(false);
    expect(
      hostFor('https://blog.example.com/', {
        PDS_EXPERIMENTAL_SITE_DOMAIN: '',
      }),
    ).toBe(false);
  });

  it('compares the PDS hostname without its port', () => {
    expect(
      hostFor('https://pds.example.com/', { HOSTNAME: 'pds.example.com:8443' }),
    ).toBe(false);
  });
});

describe('siteCacheable', () => {
  it('accepts site-hostname GETs and nothing else', () => {
    expect(cacheableFor(new Request('https://blog.example.com/'))).toBe(true);
    expect(cacheableFor(new Request('https://pds.example.com/'))).toBe(false);
    expect(
      cacheableFor(
        new Request('https://blog.example.com/', { method: 'POST' }),
      ),
    ).toBe(false);
    expect(
      cacheableFor(new Request('https://blog.example.com/'), {
        PDS_EXPERIMENTAL_SITE_DOMAIN: '',
      }),
    ).toBe(false);
  });

  it('keeps registry reads out of the cache', () => {
    expect(
      cacheableFor(new Request('https://example.com/v2/app/manifests/latest')),
    ).toBe(false);
    expect(
      cacheableFor(new Request('https://example.com/npm/@pdsjs%2fcore')),
    ).toBe(false);
    expect(cacheableFor(new Request('https://example.com/v2x'))).toBe(true);
  });
});

describe('responseStorable', () => {
  it('stores only shareable 200s', () => {
    const ok = new Response('x', {
      status: 200,
      headers: { 'Cache-Control': 'public, max-age=60' },
    });
    expect(responseStorable(ok)).toBe(true);
    expect(responseStorable(new Response('x', { status: 404 }))).toBe(false);
    expect(
      responseStorable(
        new Response('x', { headers: { 'Cache-Control': 'no-store' } }),
      ),
    ).toBe(false);
    expect(
      responseStorable(
        new Response('x', { headers: { 'Set-Cookie': 'sid=1' } }),
      ),
    ).toBe(false);
  });
});

describe('worker entry cache flow', () => {
  /** @type {Map<string, Response>} */
  const store = new Map();
  const fakeCaches = {
    default: {
      /** @param {Request} request */
      match: async (request) => store.get(request.url)?.clone(),
      /** @param {Request} request @param {Response} response */
      put: async (request, response) => {
        store.set(request.url, response);
      },
    },
  };

  /** @param {Response} doResponse */
  function makeEnv(doResponse) {
    let doCalls = 0;
    const env = /** @type {any} */ ({
      ...ENV_BASE,
      PDS: {
        idFromName: () => 'default',
        get: () => ({
          fetch: async () => {
            doCalls += 1;
            return doResponse.clone();
          },
        }),
      },
    });
    return { env, doCalls: () => doCalls };
  }

  const ctx = /** @type {any} */ ({
    /** @param {Promise<unknown>} p */
    waitUntil: (p) => p,
  });

  afterEach(() => {
    store.clear();
    vi.unstubAllGlobals();
  });

  it('serves the second request from cache and answers conditionals with 304', async () => {
    vi.stubGlobal('caches', fakeCaches);
    const { env, doCalls } = makeEnv(
      new Response('<h1>hi</h1>', {
        status: 200,
        headers: {
          'Cache-Control': 'public, max-age=60',
          ETag: '"bafycid"',
        },
      }),
    );

    const url = 'https://blog.example.com/';
    const first = await worker.fetch(new Request(url), env, ctx);
    expect(first.status).toBe(200);
    expect(doCalls()).toBe(1);

    const second = await worker.fetch(new Request(url), env, ctx);
    expect(second.status).toBe(200);
    expect(second.headers.get('X-Sites-Cache')).toBe('hit');
    expect(await second.text()).toBe('<h1>hi</h1>');
    expect(doCalls()).toBe(1);

    const conditional = await worker.fetch(
      new Request(url, { headers: { 'If-None-Match': '"bafycid"' } }),
      env,
      ctx,
    );
    expect(conditional.status).toBe(304);
    expect(conditional.headers.get('Content-Length')).toBeNull();
    expect(doCalls()).toBe(1);
  });

  it('never caches the 404 for an unknown site', async () => {
    vi.stubGlobal('caches', fakeCaches);
    const { env, doCalls } = makeEnv(
      new Response('not found', {
        status: 404,
        headers: { 'Cache-Control': 'no-store' },
      }),
    );

    const url = 'https://nosuch.example.com/';
    await worker.fetch(new Request(url), env, ctx);
    await worker.fetch(new Request(url), env, ctx);
    expect(doCalls()).toBe(2);
    expect(store.size).toBe(0);
  });

  it('redirects plain-HTTP site requests to HTTPS before anything else', async () => {
    // No caches stub: the redirect must not touch the cache or the DO
    const { env, doCalls } = makeEnv(new Response('x', { status: 200 }));
    const response = await worker.fetch(
      new Request('http://blog.example.com/sub/page?q=1'),
      env,
      ctx,
    );
    expect(response.status).toBe(301);
    expect(response.headers.get('Location')).toBe(
      'https://blog.example.com/sub/page?q=1',
    );
    expect(doCalls()).toBe(0);
  });

  it('trusts X-Forwarded-Proto over the URL scheme, both ways', async () => {
    const { env, doCalls } = makeEnv(new Response('x', { status: 200 }));
    // The edge hands the worker an https URL for a plain-HTTP visitor
    const disguised = await worker.fetch(
      new Request('https://blog.example.com/', {
        headers: { 'X-Forwarded-Proto': 'http' },
      }),
      env,
      ctx,
    );
    expect(disguised.status).toBe(301);
    expect(disguised.headers.get('Location')).toBe('https://blog.example.com/');
    expect(doCalls()).toBe(0);

    vi.stubGlobal('caches', fakeCaches);
    const secure = await worker.fetch(
      new Request('https://blog.example.com/', {
        headers: { 'X-Forwarded-Proto': 'https' },
      }),
      env,
      ctx,
    );
    expect(secure.status).toBe(200);
    expect(doCalls()).toBe(1);
  });

  it('sends PDS-hostname requests straight through, never touching caches', async () => {
    // No caches stub at all: touching it would throw
    const { env, doCalls } = makeEnv(new Response('{}', { status: 200 }));
    const response = await worker.fetch(
      new Request('https://pds.example.com/xrpc/_health'),
      env,
      ctx,
    );
    expect(response.status).toBe(200);
    expect(doCalls()).toBe(1);
    expect(store.size).toBe(0);
  });
});