apache / dubbo-js

The Typescript implementation of Apache Dubbo. An RPC and microservice framework for Node.js and Web development.
https://dubbo.apache.org/
Apache License 2.0
760 stars 161 forks source link

[API Bug]: dubboNodeAdapter #387

Closed CoderSerio closed 6 months ago

CoderSerio commented 6 months ago

Relevant Code

The relevant code is as follows

  1. proto
    
    syntax = "proto3";

package apache.dubbo.demo.helloworld;

message SayRequest { string sentence = 1; }

message SayResponse { string sentence = 1; }

service MyService { rpc Say(SayRequest) returns (SayResponse) {} }

2. dubboRouter
```ts
import { DubboRouter } from "@apachedubbo/dubbo";
import { MyService } from "../../common/gen/example_dubbo";

export default (router: DubboRouter) => {
  router.service(
    MyService,
    {
      async say(req, resp) {
        console.log(`Client say: ${req.sentence}`, req);
        return {
          sentence: `Server say: ${req.sentence}`,
        };
      },
    },
    { serviceGroup: "dubbo", serviceVersion: "1.0.0" }
  );
};
  1. server
    
    import { createServer } from "http";
    import { dubboNodeAdapter } from "@apachedubbo/dubbo-node";
    import dubboRoutes from "./router";

const server = createServer((req, res) => { console.log("get a request!", req); // key code is here dubboNodeAdapter({ routes: dubboRoutes }); res.end("Response"); });

server.listen(8000, () => { console.log("\ndubbo-js server is running on http://localhost:8000...\n"); });

4. client
```ts
import { createPromiseClient } from "@apachedubbo/dubbo";
import { createDubboTransport } from "@apachedubbo/dubbo-node";
import { MyService } from "../../common/gen/example_dubbo";

const transport = createDubboTransport({
  baseUrl: "http://localhost:8000",
  httpVersion: "1.1",
});

const main = async () => {
  const client = createPromiseClient(MyService, transport, {
    serviceVersion: "1.0.0",
    serviceGroup: "dubbo",
  });
  const res = await client.say({ sentence: "Hello World" });
  console.log(res.sentence);
};

main();

Steps to reproduce

  1. run the server
  2. run the client

    the http request from client is received by server successfully, but the API dubboNodeAdapter throws an error

Bug

O9EYDG{)F0E44A8 YJK3045

CoderSerio commented 6 months ago

Well, now I’ve resolved it🥳. This issue occurs because there is a conflict resulting from res.end and dubboNodeAdapter, which both attempting to send response for the same request. When dealing with Dubbo RPC requests within a raw NodeJS server environment, there are two stages to manage:

  1. Handle the preflight OPTIONS request, which must be separately addressed by calling res.end.
  2. Handle the real request with the API dubboNodeAdapter instead of res.end —either using its built-in handling logic or by manually executing the handler provided by it, as appropriate for the situation at hand.

The modified code now looks as follows:

import { createServer } from "http";
import { dubboNodeAdapter } from "@apachedubbo/dubbo-node";
import dubboRoutes from "./router";

const server = createServer((req, res) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  res.setHeader("Access-Control-Allow-Headers", "*");
  res.setHeader("Tri-Service-Group", "dubbo");
  res.setHeader("Tri-Service-Version", "1.0.0");
  if (req.method === "OPTIONS") {
    // handle the preflight request(OPTIONS)
    res.end();
    return;
  }

  console.log("get a request!");
  const handler = dubboNodeAdapter({
    routes: dubboRoutes,
    fallback: (...args) => {
      console.log("fallback", args);
    },
  });
  handler(req, res);
});

server.listen(8000, () => {
  console.log("\ndubbo-js server is running on http://localhost:8000...\n");
});