Control cloudflare origin server using workers - cloudflare

I'm trying to use a cloudflare worker to dynamically set the origin based on the requesting IP (so we can serve a testing version of the website internally)
I have this
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
if (request.headers.get("cf-connecting-ip") == '185.X.X.X')
{
console.log('internal request change origin');
}
const response = await fetch(request)
console.log('Got response', response)
return response
}
I'm not sure what to set. The request object doesn't seem to have any suitable parameters to change.
Thanks

Normally, you should change the request's URL, like this:
// Parse the URL.
let url = new URL(request.url)
// Change the hostname.
url.hostname = "test-server.example.com"
// Construct a new request with the new URL
// and all other properties the same.
request = new Request(url, request)
Note that this will affect the Host header seen by the origin (it'll be test-server.example.com). Sometimes people want the Host header to remain the same. Cloudflare offers a non-standard extension to accomplish that:
// Tell Cloudflare to connect to `test-server.example.com`
// instead of the hostname specified in the URL.
request = new Request(request,
{cf: {resolveOverride: "test-server.example.com"}})
Note that for this to be allowed, test-server.example.com must be a hostname within your domain. However, you can of course configure that host to be a CNAME.
The resolveOverride feature is documented here: https://developers.cloudflare.com/workers/reference/apis/request/#the-cf-object
(The docs claim it is an "Enterprise only" feature, but this seems to be an error in the docs. Anyone can use this feature. I've filed a ticket to fix that...)

Related

Cloudfront serving png/jpg vs webp based on request headers

I have Cloudfront in front of S3 serving images (png and jpg).
I have all png and jpg images in webp format in the same directory with .webp extension. For example:
png: /path/to/file.png
webp: /path/to/file.png.webp
I'd like to serve the webp file dynamically without changing the markup.
Since browsers flag webp support via Accept header, what i need to do is: if the user has support for webp (via Accept header) Cloudfront would pull the webp version (filename.png.webp), if not it should serve the original file (filename.png)
Is this possible to achieve?
Making Cloudfront serve different resources is easy (when you have done it a couple of times), but my concern is whether the entity making the request (i.e. browser) and possible caching elements between (proxies etc) expects to have different media types on the same request URI. But that is a bit beyond your question. I believe the usual way to handle this problem is with a element where the browser is free to choose an image from different media types like this:
<picture>
<source type="image/svg+xml" srcset="pyramid.svg" />
<source type="image/webp" srcset="pyramid.webp" />
<img
src="pyramid.png"
alt="regular pyramid built from four equilateral triangles" />
</picture>
But if you still want to serve different content from Cloufront for the same URL this is how you do it:
Cloudfront has 4 different points where you can inject a lamdba function for request manipulation (Lambda#Edge).
For your use case we need to create a Lambda#Edge function at the Origin Request location then associate this function with your Cloudfront Distribution.
Below is an example from AWS docs that looks on device type and does URL manipulation. For your use case, something similar can be done by looking at the "Accept" header.
'use strict';
/* This is an origin request function */
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
/*
* Serve different versions of an object based on the device type.
* NOTE: 1. You must configure your distribution to cache based on the
* CloudFront-Is-*-Viewer headers. For more information, see
* the following documentation:
* https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
* https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
* 2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
* request event. To use this example, you must create a trigger for the
* origin request event.
*/
const desktopPath = '/desktop';
const mobilePath = '/mobile';
const tabletPath = '/tablet';
const smarttvPath = '/smarttv';
if (headers['cloudfront-is-desktop-viewer']
&& headers['cloudfront-is-desktop-viewer'][0].value === 'true') {
request.uri = desktopPath + request.uri;
} else if (headers['cloudfront-is-mobile-viewer']
&& headers['cloudfront-is-mobile-viewer'][0].value === 'true') {
request.uri = mobilePath + request.uri;
} else if (headers['cloudfront-is-tablet-viewer']
&& headers['cloudfront-is-tablet-viewer'][0].value === 'true') {
request.uri = tabletPath + request.uri;
} else if (headers['cloudfront-is-smarttv-viewer']
&& headers['cloudfront-is-smarttv-viewer'][0].value === 'true') {
request.uri = smarttvPath + request.uri;
}
console.log(`Request uri set to "${request.uri}"`);
callback(null, request);
};
Next you need to tell Cloudfront that you want to use the Accept header as a part of your cache key (otherwise Cloudfront would only execute your Origin Request lambda once and also not expose this header to your function).
You do this nowadays with cache and origin request policies. Or with legacy settings (Edit Behaviour under your Cloudfront distribution settings) such as:
Worth to note here is that, if you get low cache hit ratio due to different variants of the Accept header you need to pre-process / clean it. The way I would do it is with a Viewer Request Lamdba that gets executed for each request. This new Lambda would then check if the Accept header supports Webp and then add a single NEW header to the request that it passes on to the Origin Request above. That way the Origin Request can cache on this new header (which only has two different possible values)
There's more config/setup needed such as IAM policies to get Lamdba to run etc, but there's lots of great material out there that walks you through the steps. Maybe start here?

Axios with Vue access http instead of https

I am using vue2 and axios to make my ajax calls. In a page I am calling various ajax calls, some go as HTTPS while others go through HTTP, although both codes are similar.
Example:
axios.get('/api/' + app.$variable1 + '/get-something/')
.then(({ data }) =>
{
app.array = [];
for(let i = 0; i < data.length; i++)
{
app.vats.push({
id:data[i]['id'],
name:data[i]['name'],
something_else[i]['something_else']
});
}
})
Question:
How can I force Axios to take HTTPS?
Objs:
I cannot manually add https, as such: "https://www.example.com/1234/12" because I am using relative urls (I have certain id's assigned at url, and reuse them to make my calls).
Server:
1) I am forcing Https through htaccess
2) I am also using Secure Headers which does not allow the browser to get out of "self"
EDIT:
So trying to get down to the issue:
1) In the Mounted method I am calling 4 individual API's. The first two fail due to HTTP, and the last two get through. I tried chaning the order, and its always the first two to fail. I tried to move the code to Created, which makes less sense, and sure enough it did not work.
HELP!!
Add an Axios request interceptor and change the config.url from http to https. All requests will be intercepted and you will have access to the base URL scheme.
const instance = axios.create({
baseURL: 'http://api.com',
})
instance.interceptors.request.use(function(config) {
// change the url scheme from http to https
config.url = config.url.replace('http://', 'https://')
return config
})

StrongLoop Loopback : How to customize HTTP response code and header

I'm looking for a way to customize StrongLoop LoopBack HTTP response code and headers.
I would like to conform to some company business rules regarding REST API.
Typical case is, for a model described in JSON, to have HTTP to respond to POST request with a code 201 + header Content-Location (instead of loopback's default response code 200 without Content-Location header).
Is it possible to do that using LoopBack ?
Unfortunately the way to do this is a little difficult because LoopBack does not easily have hooks to modify all responses coming out of the API. Instead, you will need to add some code to each model in a boot script which hooks in using the afterRemote method:
Inside /server/boot/ add a file (the name is not important):
module.exports = function(app) {
function modifyResponse(ctx, model, next) {
var status = ctx.res.statusCode;
if (status && status === 200) {
status = 201;
}
ctx.res.set('Content-Location', 'the internet');
ctx.res.status(status).end();
}
app.models.ModelOne.afterRemote('**', modifyResponse);
app.models.ModelTwo.afterRemote('**', modifyResponse);
};

What's the easiest way to display content depending on the URL parameter value in DocPad

I'd like to check for an URL parameter and then display the confirmation message depending on it.
E.g. if I a GET request is made to /form?c=thankyou docpad shows the form with thank you message
I think there is two basic ways to do this.
look at the url on the server side (routing) and display differing content according to URL parameters
Look at the parameter on the client side using JavaScript and either inject or show a dom element (eg div) that acts as a message box.
To do this on the server side you would need to intercept incoming requests in the docpad.coffee file in the serverExtend event. Something like this:
events:
# Server Extend
# Used to add our own custom routes to the server before the docpad routes are added
serverExtend: (opts) ->
# Extract the server from the options
{server} = opts
docpad = #docpad
# As we are now running in an event,
# ensure we are using the latest copy of the docpad configuraiton
# and fetch our urls from it
latestConfig = docpad.getConfig()
oldUrls = latestConfig.templateData.site.oldUrls or []
newUrl = latestConfig.templateData.site.url
server.get "/form?c=thankyou", (req,res,next) ->
document = docpad.getCollection('documents').findOne({relativeOutPath: 'index.html'});
docpad.serveDocument({
document: document,
req: req,
res: res,
next: next,
statusCode: 200
});
Similar to an answer I gave at how to handle routes in Docpad
But I think what you are suggesting is more commonly done on the client side, so not really specific to Docpad (assumes jQuery).
if (location.search == "?c=thankyou") {
$('#message-sent').show();//show hidden div
setTimeout(function () {
$('#message-sent').fadeOut(1000);//fade it out after a period of time
}, 1000);
}
This is a similar answer I gave in the following Docpad : show error/success message on contact form
Edit
A third possibility I've just realised is setting the document to be dynamically generated on each request by setting the metadata property dynamic = true. This will also add the request object (req) to the template data passed to the page. See Docpad documentation on this http://docpad.org/docs/meta-data.
One gotcha that gets everyone with setting the page to dynamic is that you must have the docpad-plugin-cleanurls installed - or nothing will happen. Your metadata might look something like this:
---
layout: 'default'
title: 'My title'
dynamic: true
---
And perhaps on the page (html.eco):
<%if #req.url == '/?c=thankyou':%>
<h1>Got It!!!</h1>
<%end%>

Using Node JS to proxy http and modify response

I'm trying to write a front end to an API service with Node JS.
I'd like to be able to have a user point their browser at my node server and make a request. The node script would modify the input to the request, call the api service, then modify the output and pass back to the user.
I like the solution here (with Express JS and node-http-proxy) as it passes the cookies and headers directly from the user through my site to the api server.
proxy request in node.js / express
I see how to modify the input to the request, but i can't figure out how to modify the response. Any suggestions?
transformer-proxy could be useful here. I'm the author of this plugin and I'm answering here because I found this page when looking for the same question and wasn't satisfied with harmon as I don't want to manipulate HTML.
Maybe someone else is looking for this and finds it useful.
Harmon is designed to plug into node-http-proxy https://github.com/No9/harmon
It uses trumpet and so is stream based to work around any buffering problems.
It uses an element and attribute selector to enable manipulation of a response.
This can be used to modify output response.
See here: https://github.com/nodejitsu/node-http-proxy/issues/382#issuecomment-14895039
http-proxy-interceptor is a middleware I wrote for this very purpose. It allows you to modify the http response using one or more transform streams. There are tons of stream-based packages available (like trumpet, which harmon uses), and by using streams you can avoid buffering the entire response.
var httpProxy = require('http-proxy');
var modifyResponse = require('http-proxy-response-rewrite');
var proxy = httpProxy.createServer({
target:'target server IP here',
});
proxy.listen(8001);
proxy.on('error', function (err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong. And we are reporting a custom error message.');
});
proxy.on('proxyRes', function (proxyRes, req, res) {
modifyResponse(res, proxyRes.headers['content-encoding'], function (body) {
if (body && (body.indexOf("<process-order-response>")!= -1)) {
var beforeTag = "</receipt-text>"; //tag after which u can add data to
// response
var beforeTagBody = body.substring(0,(body.indexOf(beforeTag) + beforeTag.length));
var requiredXml = " <ga-loyalty-rewards>\n"+
"<previousBalance>0</previousBalance>\n"+
"<availableBalance>0</availableBalance>\n"+
"<accuruedAmount>0</accuruedAmount>\n"+
"<redeemedAmount>0</redeemedAmount>\n"+
"</ga-loyalty-rewards>";
var afterTagBody = body.substring(body.indexOf(beforeTag)+ beforeTag.length)+
var res = [];
res.push(beforeTagBody, requiredXml, afterTagBody);
console.log(res.join(""));
return res.join("");
}
return body;
});
});