# Choosing backend based on tcp payload

**URL:** <https://discourse.haproxy.org/t/choosing-backend-based-on-tcp-payload/1600>\
**Category:** Configuration Samples\
**Created:** [September 22, 2017, 2:58pm UTC](https://discourse.haproxy.org/t/choosing-backend-based-on-tcp-payload/1600 "2017-09-22T14:58:50Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![gagan\_goku](https://sea2.discourse-cdn.com/flex016/user_avatar/discourse.haproxy.org/gagan_goku/32/293_2.png) [@gagan\_goku](https://discourse.haproxy.org/u/gagan_goku)\
**Post date:** [September 22, 2017, 2:58pm UTC](https://discourse.haproxy.org/t/choosing-backend-based-on-tcp-payload/1600/1 "2017-09-22T14:58:50Z")

</div>

My company has multiple development environments based on multiple features being tested. Instead of everyone maintain and point to each other’s respective feature environment, I am thinking of setting up a whitelisted user based routing setup. For example:

 ![42 PM](https://us1.discourse-cdn.com/flex016/uploads/haproxy/original/1X/87ce3badb50777ad96d324921f41f171597e9efe.jpg)

This is easier to do for http requests because haproxy / nginx easily support routing http requests to backends based on the value of say a user\_id header.  
Things get messy when you want to do this for binary protocols like thrift.

Have written a custom router config in haproxy which I wanted to share.

```
frontend thriftrouter
  bind *:10090
  mode tcp
  tcp-request inspect-delay 100ms
  option tcplog
  log global
  log-format "%ci:%cp -> %fi:%fp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq captured_user:%{+Q}[capture.req.hdr(0)] req.len:%[capture.req.hdr(1)]"

  tcp-request content capture req.payload(52,10) len 10
  tcp-request content capture req.len len 10
  tcp-request content accept if WAIT_END

  acl acl_thrift_call req.payload(2,2) -m bin 0001 # Thrift CALL method
  acl acl_magic_field_id req.payload(30,2) -m bin 270f # Magic field number 9999

  # Define access control list for each user
  acl acl_user_u1 req.payload(0,0),hex -m sub 7573657231 # user1
  acl acl_user_u2 req.payload(0,0),hex -m sub 7573657232 # user2

  # Route based on the user. No default backend so that one always has to set it
  use_backend backend_1 if acl_user_u1 acl_magic_field_id acl_thrift_call
  use_backend backend_2 if acl_user_u2 acl_magic_field_id acl_thrift_call

backend backend_1
  mode tcp
  option tcp-check
  server server1 127.0.0.1:9090
backend backend_2
  mode tcp
  option tcp-check
  server server2 107.0.0.1:9091

```

So now the only missing piece is the java code to generate custom metadata into thrift.

```
/**
 * Injects custom data into the egress thrift call.
 *
 * @author Gagandeep Singh
 */
@Slf4j
public class CustomInjectorProtocol extends TProtocolDecorator {
    private static final short FIELD_ID = 9999; // Magic number - this will be field id of custom data

    private final Function<Void, Map<String, String>> function;
    public CustomInjectorProtocol(TProtocol protocol, Function<Void, Map<String, String>> function) {
        super(protocol);
        this.function = function;
    }

    @Override
    public void writeMessageBegin(TMessage tMessage) throws TException {
        super.writeMessageBegin(tMessage);

        Map<String, String> map;
        try {
            map = function.apply(null);
            log.info("Injecting custom data into egress thrift call");
        } catch (Exception e) {
            log.error("Error in applying function: {}", e.getMessage());
            log.debug("Exception: ", e);
            map = new HashMap<>();
        }

        super.writeFieldBegin(new TField("custom_data", TType.MAP, FIELD_ID));
        super.writeMapBegin(new TMap(TType.STRING, TType.STRING, map.size()));
        for (Map.Entry<String, String> entry : map.entrySet()) {
            super.writeString(entry.getKey());
            super.writeString(entry.getValue());
        }
        super.writeMapEnd();
        super.writeFieldEnd();
    }
}

```

Set the user\_id when making the thrift call.

```
TTransport transport = new TSocket("localhost", 10090, 1000000);
transport.open();

TProtocol protocol = new TBinaryProtocol(transport);
TProtocol spanProtocol = new CustomInjectorProtocol(protocol, (v) -> {
    Map<String, String> map = Maps.newHashMap();
    map.put("userid", "|user1|");
    return map;
});
client = new Service.Client(spanProtocol);
```

---

<div class="post-metadata">

**Author:** ![geo7](https://avatars.discourse-cdn.com/v4/letter/g/f1d935/32.png) [@geo7](https://discourse.haproxy.org/u/geo7)\
**Post date:** [October 2, 2021, 2:23pm UTC](https://discourse.haproxy.org/t/choosing-backend-based-on-tcp-payload/1600/2 "2021-10-02T14:23:47Z")

</div>

Is there a way which I can do it in Lua?  
my message is a custom length prefixed (in binary).  
I want to read three different data from TCP payload,  
First the **length** then **message type** then based on message type I should find **client ID** and choose proper backend.
