edge icon indicating copy to clipboard operation
edge copied to clipboard

Device detection

Open barnese3 opened this issue 7 years ago • 6 comments

Feature: device-based caching/dynamic serving

-Using the User-Agent header from requests to deliver cached content based on device type

import { responseCache } from '@fly/cache'

fly.http.respondWith(async function(req) {
	// use "vary: user-agent" header to tell Google that you’re tailoring the content to the User Agent
	req.headers.set("Vary", "User-Agent")

	// get user device type
	let type = req.headers.get("User-Agent").toLowerCase()

	// serve cached or new response based on device type
	if (type.includes("macintosh")) {
		let c = cache("mac", "Hi Mac user!")
		return c

	} else if (type.includes("windows") && !type.includes("mobile")) {
		let c = cache("windows", "Greetings desktop Windows user!")
		return c

	} else if (type.includes("iphone")) {
		let c = cache("iphone", "Howdy iPhone user!")
		return c

	} else if (type.includes("android" && "mobile")) {
		let c = cache("android", "Cheers mobile Android user!")
		return c

	} else {
		let c = cache("device unknown", "Welcome user!")
		return c
	}
})

async function cache(key, response) {
	// create a unique key
	let cacheKey = `${key} version of www.example.com`

	// get cached response if there is one and serve it
	let cachedValue = await responseCache.get(cacheKey)

	if (cachedValue) {
               console.log("response served from cache at key: ", cacheKey)
               return cachedValue
        } else {
              // serve new response and add it to the cache
	     let deviceResponse = new Response(response)
	     await responseCache.set(cacheKey, deviceResponse)
	     return deviceResponse
        }
}

barnese3 avatar Dec 19 '18 20:12 barnese3

Ok so here's what I'd suggest for this. Rather than doing caching, exactly, make a device based segmentation middleware (like the cookie one here: https://github.com/superfly/cdn/blob/cookie-segments/src/middleware/cookie-segments.ts).

That basic model can be used to route people to two different origins, or even trigger other middleware like metrics / reporting.

When we get into caching more, we can make this + other stuff cache aware.

mrkurt avatar Dec 26 '18 16:12 mrkurt

Also I haven't looked closely at this library, but User Agent parsing is so intricate it might be worth using it (or something like it): https://github.com/faisalman/ua-parser-js

mrkurt avatar Dec 26 '18 21:12 mrkurt

Ok so would something like this be a better example maybe?

import proxy from '@fly/proxy'

fly.http.respondWith(async function(req) {

	const parser = require('ua-parser-js')
	const ua = req.headers.get("User-Agent")
	const uaObj = parser(ua)
	const deviceType = uaObj.device.type

	if (deviceType === "mobile") {
		console.log("mobile")
		const app = proxy("http://mobile.example.com/") 
		return app(req)
	} else if (deviceType === "tablet") {
		console.log("tablet")
		const app = proxy("http://tablet.example.com/") 
		return app(req)
	} else {
		console.log("desktop")
		const app = proxy("http://www.example.com/") 
		return app(req)
	}

})

This uses the https://github.com/faisalman/ua-parser-js library to detect the user’s device and then routes to different origins based on the device type.

barnese3 avatar Jan 02 '19 18:01 barnese3

That seems like a good path. We haven't documented this well yet, but we're shooing for apps that use the CDN code and look something like this:

import { proxy, pipeline } from "@fly/fetch";
import { middleware } from "./src/";

const origin = proxy("https://example.com");

const app = pipeline(
  middleware.httpsUpgrader,
  middleware.responseHeaders({ "powered-by": "caffeine"})
);

fly.http.respondWith(app(origin));

To route by device type, it could work like this:

import { proxy, pipeline } from "@fly/fetch";
import { middleware, userAgent } from "./src/";

const backends = {
  mobile: proxy("https://mobile.com"),
  default: proxy("https://example.com")
};

const origin = devices.route(
    userAgent.mobile(backends.mobile),
    backends.default
);

const app = pipeline(
  middleware.httpsUpgrader,
  middleware.responseHeaders({ "powered-by": "caffeine"})
);

fly.http.respondWith(app(origin));

A simpler example might just add device-type to the request headers, too:

import { proxy, pipeline } from "@fly/fetch";
import { middleware, userAgent } from "./src/";

const origin = proxy("https://example.com")

const app = pipeline(
  middleware.httpsUpgrader,
  userAgent.addDeviceHeaders,
  middleware.responseHeaders({ "powered-by": "caffeine"})
);

fly.http.respondWith(app(origin));

mrkurt avatar Jan 02 '19 20:01 mrkurt

Here's an example middleware to add a device type header in a branch: https://github.com/superfly/cdn/compare/user-agent-parsing?expand=1#diff-56064ccff3c2d3c1065db991c8672487

mrkurt avatar Jan 02 '19 21:01 mrkurt

In progress: working on middleware that handles deep linking to apps on mobile devices. It will use the user-agent header from requests to determine the user's device and OS. If user is on mobile Android, they will be routed to Google Play Store to download the app. If user is on mobile iOS, they will be routed to Apple App Store to download the app.

barnese3 avatar Jan 22 '19 19:01 barnese3