I was trying to build a very basic web page in React that would send gRPC request to a backend written in Rust.
I followed these guides:
https://daily.dev/blog/build-a-chat-app-using-grpc-and-reactjs
https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld
However, I cannot get the gRPC request to reach the envoy proxy in the docker container (envoy is the only component running inside a container so far).
This is my proto file:
syntax = "proto3";
package Base;
service Hello {
rpc HelloWorld(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {}
message HelloResponse {
string message = 1;
}
This is my App.js file (basically as it gets created by the react bootstrapper, I just added the function and the button)
import logo from './logo.svg';
import './App.css';
import { HelloClient } from './base_grpc_web_pb';
import { HelloRequest } from './base_pb';
const client = new HelloClient("http://" + window.location.hostname + ":8080", null, null);
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<button
onClick={sendRequest}
style={{
padding: "7px 38px",
fontSize: "1.2em",
boxSizing: "content-box",
borderRadius: "4px",
}}
>
Join
</button>
</div>
);
}
function sendRequest() {
const request = new HelloRequest();
console.log("CLICK");
client.helloWorld(request, {}, (err, response) => {
if (err) return console.log("BBBBB error", err);
console.log("AAAAA RESPONSE", response.getMessage());
});
}
export default App;
This is my envoy configuration (I am on Ubuntu 22.04, so no need to override the address with host.docker.internal
):
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 0.0.0.0, port_value: 9901 }
static_resources:
listeners:
- name: proxy
address:
socket_address: { address: 0.0.0.0, port_value: 8080 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: auto
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match: { prefix: "/" }
route:
cluster: hellors
timeout: 0s
max_stream_duration:
grpc_timeout_header_max: 0s
cors:
allow_origin_string_match:
- prefix: "*"
allow_methods: GET, PUT, DELETE, POST, OPTIONS
allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
max_age: "1728000"
expose_headers: custom-header-1,grpc-status,grpc-message
http_filters:
- name: envoy.filters.http.grpc_web
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
- name: envoy.filters.http.cors
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: hellors
connect_timeout: 0.25s
type: logical_dns
http2_protocol_options: {}
lb_policy: round_robin
# win/mac hosts: Use address: host.docker.internal instead of address: localhost in the line below
load_assignment:
cluster_name: cluster_0
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 0.0.0.0
port_value: 9090
Dockerfile for envoy:
FROM envoyproxy/envoy-dev:latest
COPY envoy.yaml /etc/envoy/envoy.yaml
RUN chmod go+r /etc/envoy/envoy.yaml
In the console, after I click the button I get this error message:
http://localhost:8080/Base.Hello/HelloWorld [HTTP/1.1 503 Service Unavailable 5ms]
Followed by
message: "Http response at 400 or 500 level", [...]
If I try to curl the port it seems like it isn't listening:
>$ curl -v http://localhost:8080
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 503 Service Unavailable
< content-length: 145
< content-type: text/plain
< date: Thu, 28 Apr 2022 16:03:55 GMT
< server: envoy
<
* Connection #0 to host localhost left intact
upstream connect error or disconnect/reset before headers. reset reason: connection failure, transport failure reason: delayed connect error: 111
At this point I am quite stuck, all the configuration files seem ok, yet I still can't reach the envoy proxy. Any suggestions?
I have stumbled upon this post that suggests to use nginx, however I don't understand why that would be needed, especially since the gRPC examples I was looking at don't use it.
In your case, the Http response at 400 or 500 level
is probably caused by the fact that your gRPC service is not reachable from Envoy.
You said that Envoy is running in a Docker container. If so, the cluster hellors
in your Envoy config:
clusters:
- name: hellors
load_assignment:
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 0.0.0.0
port_value: 9090
cannot be reached because you specify the address 0.0.0.0
. Docker containers use their own networks, so inside that network, the 0.0.0.0:9090
is not defined, because your gRPC service is running on your host.
You may want to try running Envoy in your host network (--net=host).
Or if your gRPC service can be containerized, you can run Envoy and the gRPC service in the same Docker network.