javascriptnode.jsgrpcgrpc-node

How to Resolve `net::ERR_INVALID_HTTP_RESPONSE` Error When Sending Data to gRPC Server from Browser?


I'm attempting to send data to http://localhost:50051/helloworld.Greeter/SayHello, but I'm encountering the following error in the browser console:

POST http://localhost:50051/helloworld.Greeter/SayHello net::ERR_INVALID_HTTP_RESPONSE
....
(index):49 Error: TypeError: Failed to fetch

There are no errors shown in the server console.

Server Code (server.mjs):
import grpc from "@grpc/grpc-js";
import protoLoader from "@grpc/proto-loader";
import path from "path";
import { fileURLToPath } from "url";

const PROTO_PATH = path.join(process.cwd(), "./helloworld.proto");
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {});
const helloProto = grpc.loadPackageDefinition(packageDefinition).helloworld;

function sayHello(call, callback) {
  callback(null, { message: "Hello " + call.request.name });
}

function main() {
  const server = new grpc.Server();
  server.addService(helloProto.Greeter.service, { sayHello: sayHello });
  server.bindAsync(
    "0.0.0.0:50051",
    grpc.ServerCredentials.createInsecure(),
    () => {
      server.start();
      console.log("Server running at http://0.0.0.0:50051");
    }
  );
}

main();

I run the server using node server.mjs.

Client Code (index.html):
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>gRPC Client</title>
  <script src="https://cdn.jsdelivr.net/npm/protobufjs/dist/protobuf.min.js"></script>
</head>
<body>
  <script>
    // Load the protobuf definitions
    const protoPath = "helloworld.proto";
    protobuf.load(protoPath, (err, root) => {
      if (err) throw err;

      const HelloRequest = root.lookupType("helloworld.HelloRequest");
      const HelloReply = root.lookupType("helloworld.HelloReply");

      console.log({ f: HelloRequest });
      // Create a HelloRequest message
      const payload = { name: "World" };
      const errMsg = HelloRequest.verify(payload);
      if (errMsg) throw Error(errMsg);

      const message = HelloRequest.create(payload);
      const buffer = HelloRequest.encode(message).finish();

      console.log({ buffer });
      // Send the request using fetch
      fetch("http://localhost:50051/helloworld.Greeter/SayHello", {
        method: "POST",
        headers: {
          "Content-Type": "application/x-protobuf",
        },
        body: buffer,
      })
      .then((response) => {
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.arrayBuffer();
      })
      .then((responseBuffer) => {
        const replyMessage = HelloReply.decode(
          new Uint8Array(responseBuffer)
        );
        console.log(`Greeting: ${replyMessage.message}`);
      })
      .catch((error) => {
        console.error("Error:", error);
      });
    });
  </script>
</body>
</html>

I run the client using npx http-server -o.

Proto File (helloworld.proto):
syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
Dependencies:
"dependencies": {
  "@grpc/grpc-js": "^1.11.1",
  "@grpc/proto-loader": "^0.7.13",
  "cors": "^2.8.5",
  "express": "^4.19.2"
}

I'm not seeing any errors on the server side. What could be causing this issue, and how can I resolve it?


Solution

  • You cannot use a browser as a gRPC client because browsers don't support HTTP/2 and this prorocol is required by gRPC.

    You can use a technology such as gRPC-Web to provide a proxy for your web client.