Istio + Envoy grpc_json_transcoder_filter

Here is the solution for using http-grpc bridge:

create an EnvoyFilter

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: echo-filtering
spec:
workloadLabels:
app: echo
filters:
- listenerMatch:
portNumber: 9009
listenerType: SIDECAR_INBOUND
insertPosition:
index: BEFORE
relativeTo: envoy.router
filterName: envoy.grpc_http1_bridge
filterType: HTTP
filterConfig:
services:
- com.test.echo.EchoService

Then access as shown below:

5 byte header. first byte zero and then 4 bytes bigindian for length.

Note the code below is scrollable:

public static void echoHttpTest() throws java.io.IOException {

System.out.println("echoHttpTest");

CloseableHttpClient httpClient = HttpClients.createDefault();

HttpPost postRequest = new HttpPost("http://10.10.10.10:” + "31380" + "/com.test.EchoService/echo");

//Set the API media type in http content-type header
postRequest.addHeader("content-type", "application/grpc");

String message = "hello";
byte[] zeroByte = {0};
byte[] messageBytes = EchoRequest
        .newBuilder()
        .setMessage(message)
        .build()
        .toByteArray();

byte[] lengthBytes = ByteBuffer.allocate(4).putInt(messageBytes.length).array();

ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(zeroByte);
baos.write(lengthBytes);
baos.write(messageBytes);

ByteArrayEntity bae = new ByteArrayEntity(baos.toByteArray());
postRequest.setEntity(bae);

//Send the request; It will immediately return the response in HttpResponse object if any
HttpResponse response = httpClient.execute(postRequest);

int statusCode = response.getStatusLine().getStatusCode();
if (statusCode >= 300) {
    throw new RuntimeException("Failed with HTTP error code : " + statusCode);
}

Header statusHeader = response.getFirstHeader("grpc-status");
System.out.println("grpc-status: " + (statusHeader != null ? statusHeader.getValue() : "n/a"));
Header msgHeader = response.getFirstHeader("grpc-message");
System.out.println("grpc-messsage: " + (msgHeader != null ? msgHeader.getValue() : "n/a"));

System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(response.getLastHeader("Content-Encoding"));
System.out.println(response.getLastHeader("Content-Length"));
System.out.println("----------------------------------------");

HttpEntity entity = response.getEntity();


//HttpEntity entity = response.getEntity();
InputStream in = entity.getContent();

ByteBuffer header = ByteBuffer.allocate(5);
System.out.println("header ");
int n =0;
try {
    n = in.read(header.array());
} catch(Exception e) {
    System.out.println(e.getMessage());
}

if (n != 5) {
    System.out.println("n="+n);
    throw new RuntimeException("expected 5 bytes, got " + n);
}
if (header.get(0) != 0) {
    throw new RuntimeException("unexpected compressed payload");
}
int size = header.getInt(1);

ByteBuffer payload = ByteBuffer.allocate(size);
n = in.read(payload.array());
if (n != size) {
    throw new RuntimeException("expected " + size + " bytes, got " + n);
}

EchoResponse echoResponse = EchoResponse.parseFrom(payload.array());
System.out.println("HTTP: " + echoResponse.getMessage());

httpClient.getConnectionManager().shutdown();

}

You want grpc-code: 0 which is success. And grpc-message is another header. My grpc server is grpc-java and it gzip encodes the grpc-code on success. Otherwise on error I grpc-code appearing in the header along with message.

I consider the bridge to be the most stable way to access grpc over http. It is used internally at Lyft as their preferred way to surface grpc service to clients over http

1 Like