Failover after x amount of time

Hi folks,

I have worker logic that, for certain extensions (such as mp4’s) I send the request over a different CDN that actually allows me to cache video (to comply with CloudFlare’s ToS). This works well but I’m running into an issue: this CDN is not perfect or as well deployed as CF and can sometimes be slow to respond when content is not cached.

I’d like to allow some time for the 206 response to come (client always sends a range request and the CDN chunks this into 8 MB slices no matter what range the client actually requested so responses are typically fast) but retry to origin if it takes say longer than 3 or 4 seconds. The original request can actually complete (and that would be preferred as that’d leave that chunk cached on the CDN for the next person).

Is there a way to implement this?

This is my current code:

addEventListener("fetch", event => {  
    let newUrl = new URL(event.request.url);  
    newUrl.hostname = "origin.domain.com";
    
    if (newUrl.pathname.toString().includes(".mp4")) {
      newUrl.hostname = "cdnb.domain.com";    
  }
    
    event.respondWith(fetch(newUrl, {    
        method: event.request.method,    
        headers: event.request.headers, 
        body: event.request.body  
        
    }));});

Thanks!

I tried giving this a shot just to see if I could return a constructed response upon the timeout being reached but it’s not working :frowning:

addEventListener("fetch", event => {
  let newUrl = new URL(event.request.url);  
  newUrl.hostname = "origin.domain.com";
      if (newUrl.pathname.toString().includes("file.mp4")) {
      newUrl.hostname = "cdnb.domain.com";
    
      event.respondWith(fetchwithTimeout(
      newUrl, {    
        method: event.request.method,    
        headers: event.request.headers, 
        body: event.request.body     
    }
  ))
  }
  else {
  event.respondWith(fetch(
  newUrl, {    
        method: event.request.method,    
        headers: event.request.headers, 
        body: event.request.body     
    }
  ))
  }
})

async function fetchwithTimeout(request) {
let fetchPromise = fetch(request)
console.log(request.url)
let timeoutPromise = new Promise(resolve => setTimeout(resolve, 1000))
let response = await Promise.race([fetchPromise, timeoutPromise])
console.log(response)

try {

if (response) {
  return response;
}
else {
  new Response("Timed Out", {status: 408})
}
}

catch (err) {
  new Response("Timed Out", {status: 408})
}
}

If I give it a high enough timeout (say 1 second) I get a 200 response as if (response) is true but if I set a low timeout I get

“Uncaught (in response) TypeError: Failed to execute function: parameter 1 is not of type ‘Response’.”

Hi hacktek, good question, am also interested in handling such scenarios. Kindly please share a solution if you find one!