apache / apisix

The Cloud-Native API Gateway
https://apisix.apache.org/blog/
Apache License 2.0
14.48k stars 2.52k forks source link

Setting gRPC response status via Lua #11402

Open federicotdn opened 3 months ago

federicotdn commented 3 months ago

Description

Hi,

I am using APISIX 3.8.0 as an Ingress controller for Kubernetes. I have set up a gRPC route with a single plugin; a serverless-pre-function for the rewrite phase.

If the endpoint were HTTP, I would be able to write:

return 404, "Not found"

anywhere in the plugin function, and that would correctly return a 404. However gRPC has its own set of status codes; is it possible to set these via Lua? If I use the above code for the gRPC endpoint, then client side I just get Unimplemented code each time.

Thanks!

Environment

zhoujiexiong commented 3 months ago

@federicotdn

Could you show the config similar with your scenario?

federicotdn commented 3 months ago

Hi, our ApisixRoute looks a bit like this, unfortunately I can't share all of the details:

apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata: ...
spec:
  http:
  - match:
      paths:
      - /foo.Bar/*
    name: grpc
    plugins:
    - config:
        functions:
        - |
          return function(_, ctx)
             local core = require("apisix.core")
             local some_header = core.request.header(ctx, "x-foobar")

             if some_header ~= "some_value" then
                return 404, "Not found"
             end
          end
        phase: rewrite
      enable: true
      name: serverless-pre-function
    upstreams:
    - name: my-upstream
      weight: 1
zhoujiexiong commented 3 months ago

I think you would expect NotFound instead of Unimplemented for the case of grpc endpoint. And we could add some more to response trailers according to H2/gRPC spec via LUA.

Attach my test output FYR:

with response trailers => NotFound(Excepted)

$ grpcurl -vv -plaintext -import-path proto  -proto helloworld.proto  -d '{"name":"apisix"}' 127.0.0.1:9081 helloworld.Greeter.GetErrResp

Resolved method descriptor:
rpc GetErrResp ( .helloworld.HelloRequest ) returns ( .helloworld.HelloReply );

Request metadata to send:
(empty)

Response headers received:
(empty)

Response trailers received:
content-length: 0
content-type: application/grpc
date: Sun, 04 Aug 2024 13:48:04 GMT
grpc-status-details-bin: CAUSCU5vdCBGb3VuZBpXCip0eXBlLmdvb2dsZWFwaXMuY29tL2hlbGxvd29ybGQuRXJyb3JEZXRhaWwSKQgBEhxUaGUgc2VydmVyIGlzIG91dCBvZiBzZXJ2aWNlGgdzZXJ2aWNl
server: APISIX/3.9.0
Sent 1 request and received 0 responses
ERROR:
  Code: NotFound
  Message: Not Found
  Details:
  1)    {
          "@type": "type.googleapis.com/helloworld.ErrorDetail",
          "code": "1",
          "message": "The server is out of service",
          "type": "service"
        }

without response trailers => Unimplemented(CURRENT)

$ grpcurl -vv -plaintext -import-path proto  -proto helloworld.proto  -d '{"name":"apisix"}' 127.0.0.1:9081 helloworld.Greeter.GetErrResp

Resolved method descriptor:
rpc GetErrResp ( .helloworld.HelloRequest ) returns ( .helloworld.HelloReply );

Request metadata to send:
(empty)

Response headers received:
(empty)

Response trailers received:
(empty)
Sent 1 request and received 0 responses
ERROR:
  Code: Unimplemented
  Message: unexpected HTTP status code received from server: 404 (Not Found); transport: received unexpected content-type "text/plain; charset=utf-8"
federicotdn commented 2 months ago

Yes! For this particular case maybe NotFound would be better, but I imagine other users will want to reply with other status codes depending on their use case. Are there plans to implement this functionality to allow Lua scripts to use these codes?

zhoujiexiong commented 2 months ago

Maybe the more general way is similar with forward-auth plugin:

Forwards client request to gRPC backend using gRPC protocol to verify 1st, if failed, just respond to downstream with the data that reply from gRPC backend. That way, we have more choices to write the logic.

@federicotdn Welcome to PR. :D

see also - https://www.envoyproxy.io/docs/envoy/v1.28.0/configuration/http/http_filters/ext_authz_filter

federicotdn commented 2 months ago

Forwards client request to gRPC backend using gRPC protocol to verify 1st, if failed, just respond to downstream with the data that reply from gRPC backend. That way, we have more choices to write the logic.

I see that the response from the authorization backend is sent to the downstream client when the authorization fails - is this whole system "gRPC aware" so to say? That is, what would encompass an "authorization failure" when by authorization backend uses gRPC?

@federicotdn Welcome to PR. :D

Maybe I will try! I would like to implement the ability to respond with gRPC status codes from a Lua plugin. Where would be a good starting point for looking into this?

zhoujiexiong commented 2 months ago

Need to spend some time researching. Any info I will sync here. :-)