hackathon/cmd/gw/main.go

93 lines
2.8 KiB
Go

// 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, ","))
}