import ballerina/http;
import ballerina/io;endpoint http:Listener serverEP {
    port:9090
};
endpoint http:Client clientEndPoint {
    targets: [
       {
            url: "http://localhost:9090"
       }
    ],
    forwarded:"enable"
};@http:ServiceConfig {
    basePath: "/proxy"
}
service<http:Service> proxy bind serverEP {
    @http:ResourceConfig {
        path:"/"
    }
    sample (endpoint conn, http:Request req) {
        var response = clientEndPoint -> forward("/sample", req);
        match response {
            http:Response clientResponse => {
                _ = conn -> respond(clientResponse);
            }
            http:HttpConnectorError err => {
                io:println("Error occurred while invoking the service");
            }
        }
    }
}
@http:ServiceConfig {
    basePath: "/sample"
}
service<http:Service> sample bind serverEP {    @http:ResourceConfig {
        path:"/"
    }
    sampleResource (endpoint conn, http:Request req) {
        http:Response res = new;
        string|() header;
        header = req.getHeader("forwarded");
        match header {
            string headerVal => {
                res.setStringPayload("forwarded header value : " + headerVal);
            }
            any | () => {
                res.setStringPayload("forwarded header value not found");
            }
        }
         _ = conn -> respond(res);
    }
}

HTTP Forwarded Extension

Example depicts the client connector Option to enable/disable/transition the Forwarded/X-Forwarded-- headers in outbound request.

import ballerina/http;
import ballerina/io;
endpoint http:Listener serverEP {
    port:9090
};
endpoint http:Client clientEndPoint {
    targets: [
       {
            url: "http://localhost:9090"
       }
    ],
    forwarded:"enable"
};

Configure client connector forwarded/x-forwarded– header behaviour by adding disable (default value), enable or transition. Transition config converts available x-forwarded– headers to forwarded header.

@http:ServiceConfig {
    basePath: "/proxy"
}
service<http:Service> proxy bind serverEP {
    @http:ResourceConfig {
        path:"/"
    }
    sample (endpoint conn, http:Request req) {
        var response = clientEndPoint -> forward("/sample", req);
        match response {
            http:Response clientResponse => {
                _ = conn -> respond(clientResponse);
            }
            http:HttpConnectorError err => {
                io:println("Error occurred while invoking the service");
            }
        }
    }
}

Proxy server forward the inbound request to a backend with forwarded config enabled.

@http:ServiceConfig {
    basePath: "/sample"
}
service<http:Service> sample bind serverEP {

Sample backend which respond with forwarded header value.

    @http:ResourceConfig {
        path:"/"
    }
    sampleResource (endpoint conn, http:Request req) {
        http:Response res = new;
        string|() header;
        header = req.getHeader("forwarded");
        match header {
            string headerVal => {
                res.setStringPayload("forwarded header value : " + headerVal);
            }
            any | () => {
                res.setStringPayload("forwarded header value not found");
            }
        }
         _ = conn -> respond(res);
    }
}
$ ballerina run http-forwarded-extension.bal

To run the service, put the code in http-forwarded-extension.bal and use $BALLERINA_HOME/bin.

ballerina: initiating service(s) in 'http-forwarded-extension.bal'
ballerina: started HTTP/WS server connector 0.0.0.0:9090

Service deployment:

$ curl -v http://localhost:9090/proxy/ -H "Forwarded:by=192.2.1.10;host=www.abc.com;proto=http"

To invoke the resource, use following client.

forwarded header value : for=192.2.1.10; by=127.0.0.1; host=www.abc.com; proto=http

Server response: