Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for grpc-web #206

Merged
merged 19 commits into from
Jul 31, 2022
Merged

Adds support for grpc-web #206

merged 19 commits into from
Jul 31, 2022

Conversation

drowzy
Copy link
Contributor

@drowzy drowzy commented Sep 2, 2021

Adds support for grpc-web.

  • Adds two new functions to the GRPC.Codec behaviour in order to base64 decode / encode the payload.

There's an example repo to see it in action.

closes #193

@drowzy drowzy mentioned this pull request Sep 2, 2021
@sleipnir
Copy link
Collaborator

sleipnir commented Sep 2, 2021

This is awesome

@AleksandarFilipov
Copy link

AleksandarFilipov commented Feb 5, 2022

Coming from an embedded perspective, being able to use this saves a lot of cpu cycles compared to my previous proxies.

Since the client now can be a web browser the library need to handle CORS. To get this operational I needed to do quite a lot of coors catchup resulting in the following changes from this

def server_headers(%{codec: GRPC.Codec.WebText = codec}) do
%{"content-type" => "application/grpc-web-#{codec.name}"}
end
def server_headers(%{codec: codec}) do
%{"content-type" => "application/grpc+#{codec.name}"}
end

to:

  def server_headers(%{codec: GRPC.Codec.WebText = codec}) do
    %{"content-type" => "application/grpc-web-#{codec.name}",
      "Access-Control-Allow-Origin" => "*",
      "Access-Control-Allow-Headers" => "Content-Type, x-grpc-web, x-user-agent",
    }
  end

  def server_headers(%{codec: codec}) do
    %{"content-type" => "application/grpc+#{codec.name}",
     "Access-Control-Allow-Origin" => "*",
     "Access-Control-Allow-Headers" => "Content-Type, x-grpc-web, x-user-agent",
  }
  end

I'm assuming there is already a mechanism in place on this but I can't wrap my around it so please advice me. Some conclusions.

  • cowboy can be configured for CORS but i don't see how to get pass the configuration properly.
  • middleware is supported, possibly this can be managed using middleware, for some reason it doesn't feel as "the right way"

@drowzy Possibly you have the answer up your sleeve :)

@sleipnir
Copy link
Collaborator

sleipnir commented Feb 7, 2022

Coming from an embedded perspective, being able to use this saves a lot of cpu cycles compared to my previous proxies.

Since the client now can be a web browser the library need to handle CORS. To get this operational I needed to do quite a lot of coors catchup resulting in the following changes from this
.....

I'm assuming there is already a mechanism in place on this but I can't wrap my around it so please advice me. Some conclusions.

* cowboy can be configured for CORS but i don't see how to get pass the configuration properly.

I think perhaps the best place to implement this is directly in Cowboy Handler https://github.com/elixir-grpc/grpc/blob/master/lib/grpc/adapter/cowboy/handler.ex

@sleipnir
Copy link
Collaborator

sleipnir commented Feb 7, 2022

Any predictions for this to be approved? I think this library seems to have been abandoned by its creators

Copy link
Contributor

@polvalente polvalente left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great overall! I've left some comments so we can iterate a bit :)

@AleksandarFilipov
Copy link

wonderful to see some activity here! Let's try and get some nice solution the the cors issue described above, possibly in a separate issue.

Short recap,
Adding this support suggested that web-client can access the services. Given the (cors) nature of web clients, they will likely be blocked if the accessing web-client is hosted separately. I've posted a solution, it caters for my needs, however it's not generic enough to be PR candidate.

We need to be able to provide headers in a configurable way. I've played with Interceptors (for other purposes), however I don't see a clean match since it will affect all services - regardless if they use grpc-web or not.

@polvalente
Copy link
Contributor

polvalente commented Jul 18, 2022

@AleksandarFilipov I think we could use the interceptors in a similar way to how Tesla modifies the current response given the result of next.(stream, req)

https://github.com/elixir-tesla/tesla/blob/master/lib/tesla/middleware/compression.ex

In this example, they decide if the resp body needs to be decompressed based on the result tuple tag. We can provide a custom interceptor that injects response headers based on a predicate MFA tuple (that receives the stream and returns true if the headers should be injected). Something like:

defmodule MyServer do
  use GRPC.Endpoint

  intercept GRPC.Interceptors.ResponseHeaders, should_inject: {__MODULE__, :"add_cors?", 1}, headers: [{"Access-Control-Allow-Origin", "*"}, {"Access-Control-Allow-Headers", "Content-Type, x-grpc-web, x-user-agent"}]

  def add_cors?(...), do: ...
end

The one thing that might need to change (I'm not that familiar with the code base yet) is that this only works if intercepts are called before stream_reply.

I'm almost sure that intercepts are only called after the reply, though. If so, we'll need to introduce something like reply_intercept that will be called right before the reply, but the overall idea remains the same.
This idea would allow us to remove the Codec from the server_headers match as well, while still allowing us to modify the headers (the whole connection, really), as we see fit.

Shall we move this into a new issue?

data
|> IO.iodata_to_binary()
|> pack_for_channel()
|> List.wrap()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
|> List.wrap()

We don't need this because a binary is also a valid iodata instance :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@polvalente polvalente merged commit ce73826 into elixir-grpc:master Jul 31, 2022
@sleipnir
Copy link
Collaborator

Awesome work guys!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

gRPC Web Support
4 participants