Move Fastly config to CloudFlare Workers - cloudflare

How can the following code be re-written to work with the CF Workers feature?
# Start
if(req.url ~ "^/app" ) {
set req.url = regsub(req.url, "^/app/", "/");
set req.http.X-DR-SUBDIR = "app";
}
#end condition

Cloudflare Workers implements the Service Worker standard, so you will need to reimplement the VCL code snippet you posted in terms of a Service Worker.
Before I show you how to do that, consider what happens when a request for https://example.com/apple arrives at the proxy. I would expect the first regex for ^/app to match, but the second one for ^/app/ not to match -- i.e., the request would be passed through with no change to the URL, but with the addition of an X-DR-SUBDIR: app header.
I suspect that behavior is a bug, so I'll first implement a worker as if the first regex were ^/app/.
addEventListener("fetch", event => {
let request = event.request
// Unlike VCL's req.url, request.url is an absolute URL string,
// so we need to parse it to find the start of the path. We'll
// need it as a separate object in order to mutate it, as well.
let url = new URL(request.url)
if (url.pathname.startsWith("/app/")) {
// Rewrite the URL and set the X-DR-SUBDIR header.
url.pathname = url.pathname.slice("/app".length)
// Copying the request with `new Request()` serves two purposes:
// 1. It is the only way to actually change the request's URL.
// 2. It makes `request.headers` mutable. (The headers property
// on the original `event.request` is always immutable, meaning
// our call to `request.headers.set()` below would throw.)
request = new Request(url, request)
request.headers.set("X-DR-SUBDIR", "app")
}
event.respondWith(fetch(request))
})
To revisit the https://example.com/apple case, if we really wanted a Cloudflare Worker which pedantically reproduces the VCL snippet's behavior, we could change these lines (comments elided):
if (url.pathname.startsWith("/app/")) {
url.pathname = url.pathname.slice("/app".length)
// ...
}
to these:
if (url.pathname.startsWith("/app")) {
if (url.pathname.startsWith("/app/")) {
url.pathname = url.pathname.slice("/app".length)
}
// ...
}

Related

How to set http or https based on window.location.protocol

I am working on a flutter web application.I want to prevent the CORS error whether my API Endpoint runs on https:// or http:// How can I modify my EndPoint Class below to set http or https based on the current window.location.protocol?
Here is my service class:
import 'package:universal_html/html.dart';
class ApiEndPoint {
Location currentLocation = window.location;
static
const host = 'api.xyz.com';
static
const http = 'http://';
static
const https = 'https://';
static String baseUrl = "";
// get base {
// if (currentLocation.protocol == 'https') {
// return baseUrl = "$https$host/api";
// } else if (currentLocation.protocol == 'http') {
// return baseUrl = "$http$host/api";
// }
// }
}
I'm not sure I understand your problem:
If currentLocation.host != apihost (for instance currentLocation could be https://app.xyz.com) you'll have to allow CORS on your API host anyways. Why then still support unsecure http:// protocol for your API. It's no problem to access https:// resources from an unsecured website. Just always use https://api.xyz.com and allow CORS origins http://app.xyz.com and https://app.xyz.com
If currentLocation.host == apihost just ditch the protocol://host part from your apirequests and just access /api/.... The browser (resp. webview) will add the correct protocol and host anyways ...
Other than that, what's wrong with your current approach? It should work. But you could probably simplify it to
get base {
return baseUrl = "${currentLocation.protocol}$host/api"
}
and ditch the const static http and const static https string constants.
I don't have any experience with flutter app architecture, nor do I know the architecture of your app. So I don't know whether it's possible to have multiple instances of that ApiEndPoint with different window.locations. This will probably lead to a problem with your static String baseUrl, as a static variable is shared among all instances of a class. So when you have different window.locations at the same time, this will be inconsistent. So maybe you should ditch the baseUrl variable alltogether and just do
get base {
return "${currentLocation.protocol}$host/api"
}
as this will always have the correct protocol for the location of the current instance.
EDIT Regarding your comment:
Of course you cannot access the base property in a static way. Ie ApiEndPoint.base won't work, because base is not a static property. So in principle you have 2 options (depending on your architecture)
Change base (and of course also currentLocation) to be static. Then you can access it vie ApiEndPoint.base
static Location currentLocation = window.location;
static get base {
return "${currentLocation.protocol}$host/api"
}
Create an instance of your ApiEndPoint class, and access the base property only via that instance
final api = ApiEndPoint();
...
final baseUrl = api.base;
In any case, you should read about static keyword ...

How to unregister middleware in Telegraf?

When I add bot.hears(...), it registers middleware for handling matching text messages. But now it will handle those messages even if they are sent any time, even if not expected.
So if I am creating a stateful service, I would like to listen to particular messages only at appropriate time.
How can I unregister middleware, so that it does not hear any more previously handled messages?
I turned out I was looking for Scenes. How to use them is described on Github.
I'll just post a slightly modified code from the links above:
const { Telegraf, Scenes, session } = require('telegraf')
const contactDataWizard = new Scenes.WizardScene(
'CONTACT_DATA_WIZARD_SCENE_ID', // first argument is Scene_ID, same as for BaseScene
(ctx) => {
ctx.reply('Please enter guest\'s first name', Markup.removeKeyboard());
ctx.wizard.state.contactData = {};
return ctx.wizard.next();
},
(ctx) => {
// validation example
if (ctx.message.text.length < 2) {
ctx.reply('Please enter real name');
return;
}
ctx.wizard.state.contactData.firstName = ctx.message.text;
ctx.reply('And last name...');
return ctx.wizard.next();
},
);
const stage = new Scenes.Stage();
stage.register(contactDataWizard);
bot.use(session());
bot.use(stage.middleware());
But I still don't know how to generally implement it, so I need to find it out in the Scenes code of Telegraf.

Ktor custom feature swap url

Im trying to add a custom feature in Ktor. It's basically a url swapper (we have a scenario where domains might be changed during anytime & can't update the client everytime).
We get the swapper list available and need a CustomFeature in Ktor to swap the url based on list. However, the context.request or request.url - everything is val and Im not able to assign new url to the request.
In Retrofit, it used to work like this
if (currentUrl.contains(urlSwapper.oldUrl)) {
val newUrl = currentUrl.replace(urlSwapper.oldUrl, urlSwapper.newUrl)
val newHttpUrl = request.url.newBuilder(newUrl)!!.build()
// build a new request with the new url. replace it
request = request.newBuilder().url(newHttpUrl).build()
break
}
}
In Ktor feature, Im trying something like this
scope.requestPipeline.intercept(HttpRequestPipeline.Transform) {
val currentUrl =
context.url.protocol.name + "://" + context.url.host + context.url.encodedPath
for (urlSwapper in feature.urlSwappers) {
if (currentUrl.contains(urlSwapper.oldUrl)) {
val newUrl = currentUrl.replace(urlSwapper.oldUrl, urlSwapper.newUrl)
val newHttpUrl = Url(newUrl)
context.url(url = newHttpUrl)
break
}
}
proceedWith(subject)
}
}
Is this the right way to do this ?
Generally yes, this is the right way. I have a few recommendations:
Intercept the sendPipeline instead of the requestPipeline.
An example:
client.sendPipeline.intercept(HttpSendPipeline.State) {
context.url(url = newUrl)
}
Get rid of proceedWith(subject) call because it's redundant.
Try to use Url objects instead of strings. You can get the current URL without affecting context by cloning UrlBuilder and building Url from it: context.url.clone().build()

Cannot redirect another page in ASP.NET Core MVC project Controller

I would like to press a button to connect a GET api, do something and redirect the page.
const Http = new XMLHttpRequest();
Http.open("GET", '/Startup/Action/Test');
Http.send();
Http.onreadystatechange = (e) => {
console.log(Http.responseText)
}
Then in my Controller:
[HttpGet("Startup/Action/{Handle}")]
public IActionResult Action(string Handle)
{
this.ViewData["Result"] = Handle;
return this.Ok(Handle);
// return LocalRedirect("~/Startup/");
// return Redirect("http://localhost:50429/");
// return View("~/Views/Startup/Index.cshtml");
// return Redirect("/");
// return RedirectToAction("Index", "Startup");
}
I get response and see it in console written by the javascript XMLHttpRequest event. However I cannot redirect to just another page. I tried all options commented above as return value.
With AJAX you don't perform the redirect server-side, you perform it client-side after the result has been received. Something like this:
Http.onreadystatechange = (e) => {
if(xhr.readyState === 4 && xhr.status === 200) {
window.location.href = '#Url.Action("Index", "Startup")';
} else {
// non-success reaponse? do something else?
}
}
The above code would redirect following a successful AJAX response. Note that this JavaScript code would need to be on a view itself in order to use the Url.Action() helper. If the JavaScript code is in a separate file, one option could be to return as a string in the AJAX response the URL to which you are redirecting.
Note however that what you are doing is a bit different here:
this.ViewData["Result"] = Handle;
I could be wrong, but I suspect this won't carry over on a redirect. TempData might? But ultimately the question then becomes... What exactly are you trying to accomplish? You are sending a value to the server and then trying to redirect the user to another page to include that value. Well, the client already knows that value, and since it's AJAX the client needs to perform the redirect, so is this AJAX operation even necessary at all?
It's not entirely clear to me what the overall goal here is, and I suspect the overall setup could be simplified significantly. But it looks like AJAX is needed at all here. You can revert your server-side code to performing the redirect and, instead of using AJAX, just redirect the client to that server-side action entirely:
window.location.href = '/Startup/Action/Test';
or:
winfow.location.href = '#Url.Action("Action", "Startup", new { Handle = "Test" })';
And then redirect in your server-side action as you originally tried:
[HttpGet("Startup/Action/{Handle}")]
public IActionResult Action(string Handle)
{
this.ViewData["Result"] = Handle;
return RedirectToAction("Index", "Startup");
}
It's still possible, and again I'm not certain, that ViewData isn't correct here and you may want to use TempData on a redirect. Though, again, it's equally likely that whatever you're trying to accomplish can be done so more effectively in the first place. Such as skipping this Action action method entirely and just passing the Handle value directly to the Index action, whatever it does it with that value.

Selenium build list of 404s

Is it possible to have Selenium crawl a TLD and incrementally export a list of any 404's found?
I'm stuck on a Windows machine for a few hrs and want to run some tests before back to the comfort of *nix...
I don't know Python very well, nor any of its commonly used libraries, but I'd probably do something like this (using C# code for the example, but the concept should apply):
// WARNING! Untested code here. May not completely work, and
// is not guaranteed to even compile.
// Assume "driver" is a validly instantiated WebDriver instance
// (browser used is irrelevant). This API is driver.get in Python,
// I think.
driver.Url = "http://my.top.level.domain/";
// Get all the links on the page and loop through them,
// grabbing the href attribute of each link along the way.
// (Python would be driver.find_elements_by_tag_name)
List<string> linkUrls = new List<string>();
ReadOnlyCollection<IWebElement> links = driver.FindElement(By.TagName("a"));
foreach(IWebElement link in links)
{
// Nice side effect of getting the href attribute using GetAttribute()
// is that it returns the full URL, not relative ones.
linkUrls.Add(link.GetAttribute("href"));
}
// Now that we have all of the link hrefs, we can test to
// see if they're valid.
List<string> validUrls = new List<string>();
List<string> invalidUrls = new List<string>();
foreach(string linkUrl in linkUrls)
{
HttpWebRequest request = WebRequest.Create(linkUrl) as HttpWebRequest;
request.Method = "GET";
// For actual .NET code, you'd probably want to wrap this in a
// try-catch, and use a null check, in case GetResponse() throws,
// or returns a type other than HttpWebResponse. For Python, you
// would use whatever HTTP request library is common.
// Note also that this is an extremely naive algorithm for determining
// validity. You could just as easily check for the NotFound (404)
// status code.
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
if (response.StatusCode == HttpStatusCode.OK)
{
validUrls.Add(linkUrl);
}
else
{
invalidUrls.Add(linkUrl);
}
}
foreach(string invalidUrl in invalidUrls)
{
// Here is where you'd log out your invalid URLs
}
At this point, you have a list of valid and invalid URLs. You could wrap this all up into a method that you could pass your TLD URL into, and call it recursively with each of the valid URLs. The key bit here is that you're not using Selenium to actually determine the validity of the links. And you wouldn't want to "click" on the links to navigate to the next page, if you're truly doing a recursive crawl. Rather, you'd want to navigate directly to the links found on the page.
There are other approaches you might take, like running everything through a proxy, and capturing the response codes that way. It depends a little on how you expect to structure your solution.