Starseamoon
Starseamoon

Reputation: 51

How to make a HTTP request from server to client using grpc in golang

Problem Statement

I have a client (which dials to the server) and server (that listens for incoming requests) written in golang and with the RPC calls defined. I am trying to initiate an HTTP request on the server side which would in turn execute the RPC call for streaming and send a JSON response back to the user

Challenge

I was able to handle both grpc and HTTP requests on different ports but having issues with passing parameters from the HTTP request onto the RPC call on the server side

Server Code

log.Println("Listening for connections from client ........")
lis, err := net.Listen("tcp", ":9000")
if err != nil {
    log.Fatalf("failed to listen: %v", err)
}

s := testApi.Server{}

grpcServer := grpc.NewServer()

testApi.RegisterTestApiServiceServer(grpcServer, &s)

if err := grpcServer.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %s", err)
}


 func main() {
  go runGrpc()

  log.Printf("*------ Waiting for requests from users ------*")
  router := mux.NewRouter().StrictSlash(true)
  router.HandleFunc("/exchangeId/{test_id}", ConnectAndExchange).Methods("GET")
  log.Fatal(http.ListenAndServe(":8080", router))

}


 func ConnectAndExchange(w http.ResponseWriter, r *http.Request){
   vars := mux.Vars(r)
   test_id, _ := strconv.Atoi(vars["test_id"])
   log.Println("Test id request from user : ", test_id)

func (s * Server) ConnectAndStream(channelStream TestApiService_ConnectAndStreamServer) error {

// Question: This Id has to come from http request above- test_id
var id int32 = 1234566
// id := a.ConnectAndExchange
log.Println("Id from sam user ", id)

// var id int32 = 1234566   
for i := 1; i <= 2; i++ {
    id += 1
    log.Println("Speed Server is sending data : ", id)
    channelStream.Send(&Input{Id: id})
}


for i := 1; i <= 2; i++ {
    log.Println("now time to receive")
    client_response, err := channelStream.Recv()
    log.Println("Response from samd client : ", client_response.Id)

    if err != nil {
        log.Println("Error while receiving from samd : ", err)
    }
}

return nil

}

I am stuck with being able to pass the test_id from the curl request to the RPC call as above. Any input is greatly appreciated

Note Client - Dials in and connects to the server and starts receiving and sending data (bi-directional streaming)

Upvotes: 1

Views: 4030

Answers (1)

Stephen Dunne
Stephen Dunne

Reputation: 459

Both the Http and GRPC client are part of the same server application. So why call the RPC method from the Http handler? The Http handler should have access to the same backend functionality.

Your question is slightly unclear but if you are trying to have your client establish a GRPC connection to the server via the HTTP handler this will not work. The GRPC connection established in this situation is between the server and its self.

Edit - thanks for the clarification. Now I understand better the flow that you are trying to achieve. Your http handler method can make the outgoing grpc call to the server and return the response back via the http.ResponseWriter

For simplicity I have used the hello world example on https://github.com/grpc/grpc-go/tree/master/examples/helloworld

Running the code sample below and hitting http://localhost:1000/exchangeId/Test will show the output

Starting
*------ Waiting for http requests from users on port 1000 ------*
server listening at 127.0.0.1:1001
Test id request from user :  Test
Server Received: Test
Greeting: Hello Test

Code sample:

import (
    "context"
    "log"
    "net"
    "net/http"
    "time"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    pb "google.golang.org/grpc/examples/helloworld/helloworld"

    "github.com/gorilla/mux"
)

var (
    grpcserver = "localhost:1001"
)

func main() {
    log.Print("Starting")
    go StartGrpcServer()

    log.Printf("*------ Waiting for http requests from users on port 1000 ------*")
    router := mux.NewRouter().StrictSlash(true)
    router.HandleFunc("/exchangeId/{test_id}", ConnectAndExchange).Methods("GET")
    log.Fatal(http.ListenAndServe(":1000", router))
}

type server struct {
    pb.UnimplementedGreeterServer
}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("Server Received: %v", in.GetName())
    return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func StartGrpcServer() {
    lis, err := net.Listen("tcp", grpcserver)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    log.Printf("server listening at %v", lis.Addr())
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

func ConnectAndExchange(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    test_id := vars["test_id"]
    log.Println("Test id request from user : ", test_id)

    // Set up a connection to the server.
    conn, err := grpc.Dial(grpcserver, grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    // Contact the server and print out its response.
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    resp, err := c.SayHello(ctx, &pb.HelloRequest{Name: test_id})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", resp.GetMessage())

    w.Write([]byte(resp.GetMessage()))
}

Upvotes: 1

Related Questions