grpc-dotnet icon indicating copy to clipboard operation
grpc-dotnet copied to clipboard

Customizing gRPC URLs?

Open JamesNK opened this issue 6 years ago • 14 comments

How strict is the gRPC standard with respect to URLs?

Today we generate URLs that always place them at the root of the web app.

e.g. /Count.Counter/AccumulateCount

Is there flexibility to have them prefixed with a URL segment in a web app?

e.g. /api/Count.Counter/AccumulateCount

If this is allowed then we should provide a way to customize service URLs in the startup experience.


UPDATE 2021/04/21 ✔️

Calling a service in a sub-directory documented here: https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot#calling-grpc-services-hosted-in-a-sub-directory

JamesNK avatar Feb 19 '19 22:02 JamesNK

The way I understand it, URL prefixes don't work well with generated clients (see grpc/grpc/issues/14900).

dhhoang avatar Feb 19 '19 23:02 dhhoang

https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md

Some gRPC implementations may allow the Path format shown above to be overridden, but this functionality is strongly discouraged. gRPC does not go out of its way to break users that are using this kind of override, but we do not actively support it, and some functionality (e.g., service config support) will not work when the path is not of the form shown above.

JamesNK avatar Feb 19 '19 23:02 JamesNK

Question related to #43 Will the managed client be generated as well (based on .proto file)? There doesn't seem to be a way to specify a custom url prefix in the .proto file .

dhhoang avatar Feb 22 '19 18:02 dhhoang

I could be configuration in the client. You'll already need to specify a base address. A prefix could be part of that, e.g. https://localhost:1111/prefix

JamesNK avatar Feb 22 '19 18:02 JamesNK

Right, but other generated clients might not work well with it. Let's say I have some .proto files and and build a Aspnet core server, and then generate my C#/Ruby/Java client with the Grpc core tool. As per https://github.com/grpc/grpc/issues/14900#issuecomment-378071244 the client would set the HTTP2 :path header based on the default format. The prefix wouldn't be compatible then.

dhhoang avatar Feb 22 '19 19:02 dhhoang

We'll be writing a managed client at some point. If we want to support a prefix then we'd include it in request's :path

JamesNK avatar Feb 22 '19 19:02 JamesNK

My grpc server is hosted by Service Fabric as a guest executable service. The accessing url of this service via the reverse proxy from outside the cluster becomes:

http://servicefabric:19081/GuestExeSample/GrpcHello

In order to using current grpc client to access this service, I have to transfer the url to another port without path through nginx.

server {
    listen 9001 http2;
    location / {
         grpc_pass grpc://servicefabric:19081/GuestExeSample/GrpcHello;
    }
}	

It's a good idea that a managed client supports a prefix. @JamesNK

kinglionsoft avatar Mar 08 '19 08:03 kinglionsoft

I think this should be considered as a core feature on the Grpc library. Day by day many api gateways are supporting Grpc communication on top of them, so mounting the services on a custom path or on a virtual host is trivial. Support for the clients to use default path prefix would be an ideal feature to include in all the libraries built by the Grpc client. I prefer the prefix to be not part of proto file, rather it should be just a default path prefix in the clients.

ysaakpr avatar Oct 30 '19 13:10 ysaakpr

I'll voice support for this, at least on the client side. The server side could be handled with a proxy like nginx, but I believe the option should exist in a bare server too for the sake of symmetry.

There should be a URL that clients can connect to as follows:

with grpc.insecure_channel('service-host:service-port/service/path') as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)

Just as we do not and cannot hard code the host and port in the stub, we should not hard code the base path. Everything under the base path is still fair game for grpc to do with as it wants. The publisher of the service and the users of a service would have to agree on the path, just as they do the host and port. I don't believe it need to be any more complicated than that.

dmhursh avatar Sep 28 '21 13:09 dmhursh

Customizing gRPC Urls would be really great.

Under this issue a described a use case for the need to customize URLs (though maybe a special one).

MontyGvMC avatar Dec 14 '21 11:12 MontyGvMC

I would also be interested in this feature.

My use-case is the following: I have a service which has both gRPC and REST endpoints. And I'm using Nginx as a reverse proxy. For the REST (HTTP1) endpoints I'm using the proxy_pass directive in the nginx.conf. But that does not work with gRPC, for gRPC I need to use grpc_pass. So in the nginx.conf I need to be able to differentiate between the two parts of the service.

If I could host the gRPC endpoints under a subroute (let's say /grpc), then I'd be able to do this:

http {
  server {
    listen 80;

    location /grpc {
      grpc_pass grpc://localhost;
    }

    location / {
      proxy_pass http://localhost;
    }
  }
}

I'm not sure if I have a way to do this without hosting the gRPC endpoints on a specific subroute.

markvincze avatar Feb 14 '22 15:02 markvincze

I would also be interested in this feature.

My use-case is the following: I have a service which has both gRPC and REST endpoints. And I'm using Nginx as a reverse proxy. For the REST (HTTP1) endpoints I'm using the proxy_pass directive in the nginx.conf. But that does not work with gRPC, for gRPC I need to use grpc_pass. So in the nginx.conf I need to be able to differentiate between the two parts of the service.

If I could host the gRPC endpoints under a subroute (let's say /grpc), then I'd be able to do this:

http {
  server {
    listen 80;

    location /grpc {
      grpc_pass grpc://localhost;
    }

    location / {
      proxy_pass http://localhost;
    }
  }
}

I'm not sure if I have a way to do this without hosting the gRPC endpoints on a specific subroute.

There is a solution to your problem. The paths to grpc services follow a specific format:

host/package.service/method

Using the package name as a prefix to detect gRPC services has worked well for us so far. You just need to make sure your services are either all in the same package or you add all package names as valid prefixes for grpc_pass

Blackclaws avatar Sep 05 '22 11:09 Blackclaws

I would also be interested in this feature. My use-case is the following: I have a service which has both gRPC and REST endpoints. And I'm using Nginx as a reverse proxy. For the REST (HTTP1) endpoints I'm using the proxy_pass directive in the nginx.conf. But that does not work with gRPC, for gRPC I need to use grpc_pass. So in the nginx.conf I need to be able to differentiate between the two parts of the service. If I could host the gRPC endpoints under a subroute (let's say /grpc), then I'd be able to do this:

http {
  server {
    listen 80;

    location /grpc {
      grpc_pass grpc://localhost;
    }

    location / {
      proxy_pass http://localhost;
    }
  }
}

I'm not sure if I have a way to do this without hosting the gRPC endpoints on a specific subroute.

There is a solution to your problem. The paths to grpc services follow a specific format:

host/package.service/method

Using the package name as a prefix to detect gRPC services has worked well for us so far. You just need to make sure your services are either all in the same package or you add all package names as valid prefixes for grpc_pass

Hi @Blackclaws, what about a Service that is common to multiple components, like the HealthCheck service. In this case the service has the same Name and Package on every deployment, how could we route the client to the correct component? Assuming you have a single host and many components with services deployed under that host.

Thanks.

loe-lobo avatar Jun 13 '23 10:06 loe-lobo

Hi @Blackclaws, what about a Service that is common to multiple components, like the HealthCheck service. In this case the service has the same Name and Package on every deployment, how could we route the client to the correct component? Assuming you have a single host and many components with services deployed under that host.

In that case the mentioned idea will not work. What you could do is differentiate based on headers. Those are something you can add to calls easily using interceptors. Nginx can proxy pass based on headers.

You do need a way to differentiate the individual health check services anyway, a header can serve that purpose just as well as a prefix. In general I'd like to mention that maybe what you'd want instead is a health aggregation service that individually connects to each individual HealthCheck service and produces an aggregate result that is exposed to the outside, kind of as a Backend for Frontend type of approach.

Blackclaws avatar Jun 13 '23 10:06 Blackclaws