I have a server that is heavily based on this tutorial. It works fine after the additional changes I applied to it. But now that I'm trying to add socket.io
to it, it now is having some issue. Upon testing the code for socket.io
that I added, it seems like it's affecting the mechanism that makes the client code(endpoints) call the server side code(db query, processing). The call reaches the client side of the server as the log for the an endpoint call appears on the terminal, but it doesn't seem to be calling the server side.
Here's the code for the socket server:
package helpers
import (
"fmt"
"net/http"
socketio "github.com/googollee/go-socket.io"
"github.com/googollee/go-socket.io/engineio"
"github.com/googollee/go-socket.io/engineio/transport"
"github.com/googollee/go-socket.io/engineio/transport/polling"
"github.com/googollee/go-socket.io/engineio/transport/websocket"
)
var allowOriginFunc = func(r *http.Request) bool {
return true
}
func StartSocket() {
server := socketio.NewServer(&engineio.Options{
Transports: []transport.Transport{
&polling.Transport{
CheckOrigin: allowOriginFunc,
},
&websocket.Transport{
CheckOrigin: allowOriginFunc,
},
},
})
server.OnConnect("/", func(s socketio.Conn) error {
s.SetContext("")
fmt.Println("connected:", s.ID())
return nil
})
server.OnEvent("/", "notice", func(s socketio.Conn, msg string) {
fmt.Println("notice:", msg)
s.Emit("reply", "have "+msg)
})
server.OnError("/", func(s socketio.Conn, e error) {
fmt.Println("socket error:", e)
})
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
fmt.Println("closed", reason)
})
go server.Serve()
defer server.Close()
http.Handle("/socket.io/", server)
http.Handle("/", http.FileServer(http.Dir("./asset")))
fmt.Println("Socket server serving at localhost:8000...")
fmt.Print(http.ListenAndServe(":8000", nil))
}
// main.go server side
package main
import (
"flag"
"fmt"
"log"
"net"
pb "github.com/<me>/<project_name>/api/proto/out"
"github.com/<me>/<project_name>/cmd/server/handlers"
"github.com/<me>/<project_name>/cmd/server/helpers"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
func init() {
helpers.DatabaseConnection()
}
var (
tls = flag.Bool("tls", true, "Connection uses TLS if true, else plain TCP")
certFile = flag.String("cert_file", "", "The TLS cert file")
keyFile = flag.String("key_file", "", "The TLS key file")
port = flag.Int("port", 50051, "The server port")
)
func main() {
flag.Parse()
// helpers.StartSocket()
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
var opts []grpc.ServerOption
if *tls {
if *certFile == "" {
*certFile = "service.pem"
}
if *keyFile == "" {
*keyFile = "service.key"
}
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
if err != nil {
log.Fatalf("Failed to generate credentials: %v", err)
}
opts = []grpc.ServerOption{grpc.Creds(creds)}
}
mServ := grpc.NewServer(opts...)
fmt.Println("gRPC server running ...")
pb.RegisterSomethingServiceServer(mServ, &handlers.SomethingServer{})
log.Printf("Server listening at %v", lis.Addr())
if err := mServ.Serve(lis); err != nil {
log.Fatalf("failed to serve : %v", err)
}
}
// main.go client-side
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"log"
"sort"
"strings"
"github.com/<me>/<project_name>/cmd/client/handlers"
"github.com/gin-gonic/gin"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
)
func main() {
flag.Parse()
creds, err := credentials.NewClientTLSFromFile("service.pem", "")
if err != nil {
log.Fatalf("could not process the credentials: %v", err)
}
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
var ginGine = gin.Default()
StartClient(conn, ginGine)
}
func StartClient(conn *grpc.ClientConn, ginGine *gin.Engine) {
handlers.SomethingApiHandler(conn, ginGine)
ginGine.Run(":5000")
}
For the full client and server code, you can check the tutorial I linked above. If I don't call StartSocket()
, everything works fine.
When calling the an endpoint of my API, I get this error, and it's throw by the code calling the server side code:
"rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp [::1]:50051: connectex: No connection could be made because the target machine actively refused it.""
Here's what the code looks like:
ginGine.POST("/account/login", func(ctx *gin.Context) {
var account models.Account
err := ctx.ShouldBind(&account)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error1": err.Error(),
})
return
}
res, err := srvClient.service.ValidateAccount(ctx, &pb.ValidateAccountRequest{
Account: &pb.Account{
Id: account.ID,
FirstName: account.FirstName,
LastName: account.LastName,
UserName: account.UserName,
Password: account.Password,
},
})
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error2": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"status": res.Status,
"access_token": res.AccessToken,
})
})
error2 is what gets returned on the API call
This line of code is blocking and function StartSocket()
never returns:
fmt.Print(http.ListenAndServe(":8000", nil))
To test, you can add log after it and that log message will be not printed.
You need to run StartSocket()
in a separate not blocking goroutine.
P.S. In example, that line is log.Fatal(http.ListenAndServe(":8000", nil))
. Your code swallows an error.