Skip to content

Instantly share code, notes, and snippets.

@sjelfull
Forked from RiFi2k/removeContent.js
Created October 2, 2022 21:30
Show Gist options
  • Save sjelfull/b9a410ebb21c281c6bc4c3d6659132e0 to your computer and use it in GitHub Desktop.
Save sjelfull/b9a410ebb21c281c6bc4c3d6659132e0 to your computer and use it in GitHub Desktop.

Revisions

  1. @RiFi2k RiFi2k created this gist Jun 21, 2019.
    127 changes: 127 additions & 0 deletions removeContent.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,127 @@
    addEventListener("fetch", event => {
    event.respondWith(handle(event.request))
    })

    async function handle(request) {
    // Fetch from origin server.
    let response = await fetch(request)

    // Make sure we only modify text, not images.
    let type = response.headers.get("Content-Type") || ""
    if (!type.startsWith("text/")) {
    // Not text. Don't modify.
    return response
    }

    // Create a pipe. The readable side will become our
    // new response body.
    let { readable, writable } = new TransformStream()

    // Start processing the body. NOTE: No await!
    streamTransformBody(response.body, writable)

    // ... and create our Response while that's running.
    return new Response(readable, response)
    }

    // A map of template keys to URLs.
    const templateMap = {
    "BREADCRUMBS": "https://example.com/bread-crumbs"
    }

    async function translate(chunks) {
    const decoder = new TextDecoder()
    const encoder = new TextEncoder()

    // Our chunks are in UTF-8, so we need to decode them before
    // looking them up in our template map. TextDecoder's streaming
    // API makes this easy to perform in a reduction.
    let templateKey = chunks.reduce(
    (accumulator, chunk) =>
    accumulator + decoder.decode(chunk, { stream: true }),
    "")

    // We need one last call to decoder.decode() to flush
    // decoder's buffer. If there's anything left in there, it'll
    // come out as Unicode replacement characters.
    templateKey += decoder.decode()

    if (!templateMap.hasOwnProperty(templateKey)) {
    // We encountered a template key we weren't expecting.
    // Just leave its place in the document blank.
    return new Uint8Array(0)
    }

    // We're expecting this template key and know where to find
    // its resource.
    let response = await fetch(templateMap[templateKey])
    return response.arrayBuffer()
    }

    async function streamTransformBody(readable, writable) {
    const leftBrace = '{'.charCodeAt(0)
    const rightBrace = '}'.charCodeAt(0)

    let reader = readable.getReader()
    let writer = writable.getWriter()

    // We need to track our state outside the loop in case we
    // encounter a template that crosses a chunk boundary.
    // Instead of tracking a separate inTemplate boolean, we can
    // use the nullity of templateChunks to signal whether we're
    // currently in a template.
    let templateChunks = null

    while (true) {
    let { done, value } = await reader.read()
    if (done) break

    // Each chunk may have zero or more templates, so we'll
    // need to loop until we're done processing this chunk.
    while (value.byteLength > 0) {
    if (templateChunks) {
    // We're in the middle of a template. Search for the
    // terminal brace.
    let end = value.indexOf(rightBrace)
    if (end === -1) {
    // This entire chunk is part of a template. No further
    // processing of this chunk is necessary.
    templateChunks.push(value)
    break
    } else {
    // We found the termination of a template.
    templateChunks.push(value.subarray(0, end))

    // Now that we have one complete template, translate it.
    await writer.write(await translate(templateChunks))
    templateChunks = null

    value = value.subarray(end + 1)
    }
    }

    // We're not currently in a template. Search for the
    // initial brace.
    let start = value.indexOf(leftBrace)
    if (start === -1) {
    // This entire chunk is template-free. We can write
    // it and go straight to reading the next one.
    await writer.write(value)
    break
    } else {
    // We found the start of a template -- write the
    // chunk up to that point, then continue processing
    // the rest of the chunk.
    await writer.write(value.subarray(0, start))
    value = value.subarray(start + 1)
    templateChunks = []
    }
    }
    }

    // NOTE: If templateChunks is non-null at this point, we
    // encountered an unterminated template. This may or may
    // not be a problem, depending on your use case.

    await writer.close()
    }