Skip to main content

CDN Edge redirection

Base on GeoIp information and accept language header, redirect to the right path.

Compute

Use this solution in your CDB Edge Compute service:

use proxy_wasm::traits::*;
use proxy_wasm::types::*;

proxy_wasm::main! {{
    proxy_wasm::set_log_level(LogLevel::Trace);
    proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(HttpHeadersRoot) });
}}

struct HttpHeadersRoot;

impl Context for HttpHeadersRoot {}

impl RootContext for HttpHeadersRoot {
    fn create_http_context(&self, _context_id: u32) -> Option<Box<dyn HttpContext>> {
        Some(Box::new(HttpHeaders {}))
    }

    fn get_type(&self) -> Option<ContextType> {
        Some(ContextType::HttpContext)
    }
}

struct HttpHeaders {}

impl Context for HttpHeaders {}

const INTERNAL_SERVER_ERROR: u32 = 500;
const FOUND: u32 = 302;

impl HttpContext for HttpHeaders {
    fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {

        let Some(country_code) = self.get_property(vec!["request.country"]) else {
            self.send_http_response(
                INTERNAL_SERVER_ERROR,
                vec![],
                Some(b"Malformed request - no country field"),
            );
            println!("Malformed request - no country field");
            return Action::Pause;
        };
        let Ok(country_code) = std::str::from_utf8(&country_code) else {
            self.send_http_response(
                INTERNAL_SERVER_ERROR,
                vec![],
                Some(b"Malformed request - country not utf8 string"),
            );
            println!("Malformed request - country not utf8 string");
            return Action::Pause;
        };
        println!("country: {}|", country_code);



        let Some(fowarded_host) = self.get_http_request_header("X-Forwarded-Host") else {
            println!("no fowarded_host field");
            self.send_http_response(INTERNAL_SERVER_ERROR, vec![], Some(b"Malformed request - no fowarded_host field"));
            return Action::Pause;
        };
        println!("fowarded_host: {}|", fowarded_host.as_str());

        let Some(fowarded_proto) = self.get_http_request_header("X-Forwarded-Proto") else {
            println!("no fowarded_proto field");
            self.send_http_response(INTERNAL_SERVER_ERROR, vec![], Some(b"Malformed request - no fowarded_proto field"));
            return Action::Pause;
        };
        println!("fowarded_proto: {}|", fowarded_proto.as_str());

        let Some(accept_language) = self.get_http_request_header("accept-language") else {
            println!("no accept_language field");
            self.send_http_response(INTERNAL_SERVER_ERROR, vec![], Some(b"Malformed request - no accept_language field"));
            return Action::Pause;
        };
        println!("accept_language: {}|", accept_language.as_str());

        // Détermination du chemin selon les règles`
        let location_path = if accept_language.contains("pt-br") {
            "/br"
        } else if accept_language.contains("es") {
            "/es"
        } else {
            match country_code {
                "FR" => "/fr",
                "BE" => "/be-en",
                "LU" => "/lu-en",
                _ => "/en",
            }
        };

        let redirection_location = format!("{}://{}{}", fowarded_proto, fowarded_host, location_path);
        println!("redirection_location: {}|", redirection_location);


        self.add_http_response_header("Location", redirection_location.as_str());
        self.send_http_response(FOUND,  vec![], Some(b"redirection"));
        return Action::Pause;
    }
}
[package]
name = "cdn-geo-redirect"
version = "1.0.0"
edition = "2021"
description = "Proxy-Wasm example: Retrieve geoIp information and use them to redirect."


[lib]
crate-type = ["cdylib"]
path = "src/cdn-geo-redirect.rs"

[dependencies]
fastedge = "0.2.0"
proxy-wasm = "0.2.1"
headers = "0.4.0"