// Command gunk-example-server-gw is a grpc-gateway example implementation for // the gunk-example-server. package main import ( "context" "flag" "fmt" "io" "net/http" "os" "strings" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" codequestpb "github.com/hhhapz/codequest/api/v1" "github.com/peterbourgon/ff/v3" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" "google.golang.org/protobuf/encoding/protojson" ) func main() { fs := flag.NewFlagSet("codequest", flag.ExitOnError) addr := fs.String("l", "0.0.0.0:8080", "listen address") endpoint := fs.String("endpoint", "localhost:10000", "grpc endpoint") if err := ff.Parse(fs, os.Args[1:], ff.WithEnvVarPrefix("CQ")); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if err := run(context.Background(), *addr, *endpoint); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } } // run creates and runs the gateway instance. func run(ctx context.Context, addr, endpoint string) error { // build gateway mux gw, opts := runtime.NewServeMux( runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{ MarshalOptions: protojson.MarshalOptions{ EmitUnpopulated: true, }, }), ), []grpc.DialOption{grpc.WithInsecure()} for _, f := range []func(context.Context, *runtime.ServeMux, string, []grpc.DialOption) error{ codequestpb.RegisterAuthServiceHandlerFromEndpoint, codequestpb.RegisterUserServiceHandlerFromEndpoint, codequestpb.RegisterQuestServiceHandlerFromEndpoint, } { if err := f(ctx, gw, endpoint, opts); err != nil { return err } } log := grpclog.NewLoggerV2(io.Discard, os.Stderr, os.Stderr) grpclog.SetLoggerV2(log) log.Info("Serving gateway on http://", addr) server := &http.Server{ Addr: addr, Handler: allowCORS(gw), } return server.ListenAndServe() } // allowCORS allows Cross Origin Resoruce Sharing from any origin. // Don't do this without consideration in production systems. func allowCORS(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if origin := r.Header.Get("Origin"); origin != "" { w.Header().Set("Access-Control-Allow-Origin", origin) if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" { preflightHandler(w, r) return } } h.ServeHTTP(w, r) }) } // preflightHandler adds the necessary headers in order to serve // CORS from any origin using the methods "GET", "HEAD", "POST", "PUT", "DELETE" // We insist, don't do this without consideration in production systems. func preflightHandler(w http.ResponseWriter, r *http.Request) { headers := []string{"Content-Type", "Accept", "Authorization"} w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ",")) methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE"} w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ",")) }