123 lines
3.2 KiB
Go
123 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
|
"github.com/sundowndev/grpc-api-example/gen"
|
|
notesv1 "github.com/sundowndev/grpc-api-example/proto/notes/v1"
|
|
"github.com/sundowndev/grpc-api-example/server"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"google.golang.org/grpc/grpclog"
|
|
"io"
|
|
"io/fs"
|
|
"mime"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
var (
|
|
httpServerEndpoint = flag.String("http-server-endpoint", "localhost:8000", "HTTP server endpoint")
|
|
grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:9090", "gRPC server endpoint")
|
|
)
|
|
|
|
func getOpenAPIHandler() (http.Handler, error) {
|
|
err := mime.AddExtensionType(".svg", "image/svg+xml")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Use subdirectory in embedded files
|
|
subFS, err := fs.Sub(gen.OpenAPI, "openapiv2")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't create sub filesystem: %v", err)
|
|
}
|
|
return http.FileServer(http.FS(subFS)), nil
|
|
}
|
|
|
|
func httpServer(ctx context.Context, addr string) error {
|
|
// Register gRPC server endpoint
|
|
// Note: Make sure the gRPC server is running properly and accessible
|
|
mux := runtime.NewServeMux()
|
|
opts := []grpc.DialOption{
|
|
grpc.WithTransportCredentials(insecure.NewCredentials()), // TODO: Replace with your own certificate!
|
|
}
|
|
|
|
// Register services
|
|
err := notesv1.RegisterNotesServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
oa, err := getOpenAPIHandler()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
gwServer := &http.Server{
|
|
Addr: *httpServerEndpoint,
|
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if strings.HasPrefix(r.URL.Path, "/api") {
|
|
mux.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
oa.ServeHTTP(w, r)
|
|
}),
|
|
}
|
|
|
|
// Start HTTP server (and proxy calls to gRPC server endpoint)
|
|
return gwServer.ListenAndServe()
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
// Adds gRPC internal logs. This is quite verbose, so adjust as desired!
|
|
log := grpclog.NewLoggerV2(os.Stdout, io.Discard, io.Discard)
|
|
grpclog.SetLoggerV2(log)
|
|
|
|
srv, err := server.NewServer(insecure.NewCredentials()) // TODO: Replace with your own certificate!
|
|
if err != nil {
|
|
log.Fatal("failed to initialize server: %v", err)
|
|
}
|
|
|
|
// Serve gRPC Server
|
|
log.Info("Serving gRPC on https://", *grpcServerEndpoint)
|
|
go func() {
|
|
if err := srv.Listen(*grpcServerEndpoint); err != nil && !errors.Is(err, net.ErrClosed) {
|
|
log.Fatalf("listen: %s\n", err)
|
|
}
|
|
}()
|
|
|
|
// Serve HTTP gateway
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
go func() {
|
|
if err := httpServer(ctx, *httpServerEndpoint); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
log.Fatalf("http server: %s\n", err)
|
|
}
|
|
}()
|
|
|
|
// Wait for interrupt signal to gracefully shut down the server with
|
|
// a timeout of 5 seconds.
|
|
quit := make(chan os.Signal)
|
|
// kill (no param) default send syscanll.SIGTERM
|
|
// kill -2 is syscall.SIGINT
|
|
// kill -9 is syscall. SIGKILL but can't be caught, so don't need to add it
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
<-quit
|
|
|
|
cancel()
|
|
if err := srv.Close(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
log.Info("graceful shut down succeeded")
|
|
}
|