server

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 11, 2021 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package server provides a convienence type for merging GRPC/REST/HTTP services on a single port. This avoids issues like needing CORS for JS/WASM clients to talk REST to your service because it runs on a different port. It handles issues such as:

  • Routing all three services on a single port
  • Applying TLS for all services
  • REST Gateway to GRPC setup
  • Can not use TLS while using HTTP and GRPC services (a nightmare to figure out why it doesn't work)
  • Sane HTTP/REST default compression if client supported(gzip)
  • GRPC gzip compression support (though this is based on the client request using gzip)
  • Applying common HTTP handler wrappers to the Gateway and HTTP handlers (or individually)
  • STAYING OUT OF THE WAY to allow you to customize your GRPC server, REST Gateway and HTTP.

This package is meant to be used internally in a common GRPC platform package for a company. That parent package can automatically setup health handlers, have options for ACL processing, automatically tie into monitoring, etc...

Important Notes:

  • Traffic going to REST MUST have Content-Type set to "application/grpc-gateway".
  • The gateway/client located in this Repo automatically sets this.
  • We do not compress content being served with file types (as discovered by file extensions) that are already compressed

Usage example:

// Basic GRPC server setup.
grpcServer := grpc.NewSever()
myServer := myService.NewServer()
pb.RegisterMyServiceService(grpcServer, myServer) // Must happen prior to New() call.

// GRPC gateway setup, if doing a REST fronend. If you haven't done this before, you
// will need to follow the protocol buffer compiler setup, which is beyond the scope of this.
// https://grpc-ecosystem.github.io/grpc-gateway/docs/usage.html
gwMux := runtime.NewServeMux()

// Setup an HTTP Server serving all other pages.
httpMux := http.NewServeMux()
httpMux.Handle(
	"/",
	http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("Hello World!"))
		},
	),
)

logCallType := func(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
		switch r.Header.Get("content-type") {
		case "application/grpc-gateway":
			log.Println("request serviced by REST")
		default:
			log.Println("request is a normal HTTP request")
		}
		next.ServeHTTP(w, r) // Call the wrapped http.Handler
	})
}

united, err := New(
	"208.244.233.1:8080",
	grpcServer,
	// This runs without TLS. Do not do this in production!!!!!!!!
	WithInsecure(),
	// This instantiates a GRPC REST Gateway on the same port for REST clients.
	Gateway(
		gwMux,
		pb.RegisterMyServerHandlerFromEndpoint,
		nil,
		[]grpc.DialOption{grpc.WithInsecure()},
	),
	// This serves an HTTP server handling everything that isn't GRPC on the same port.
	// This assumes that httpMux variable exists somewhere above.
	HTTP(httpMux, nil),
	// This wraps REST and HTTP calls with logCallType(). This does not wrap GRPC calls,
	// those must be done prior to New(). See the note on WrapHandlers().
	WrapHandlers(logCallType),
)
if err != nil {
	// Do something
}

// This provides a custom *http.Server. This is not required.
united.SetHTTPServer(
	&http.Server{
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
	},
)

// This starts all the servers. This will return, so if you do this in main() you
// will need a select{} or something to prevent main() from returning.
if err := united.Start(); err != nil {
	// Do something
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Compressor

type Compressor func(w http.ResponseWriter) ResponseWriter

Compressor provides a function that composes an io.WriteCloser representing a compressor and the http.ResponseWriter into our local ResponseWriter that implements http.ResponseWriter with an additional Close() method for closing the compressor writes.

type Decompressor

type Decompressor func(r io.Reader) (io.Reader, error)

Decompressor takes an io.Reader and either compresses the content or decompresses the content to the returned io.ReadCloser.

type GRPC

type GRPC struct {
	// contains filtered or unexported fields
}

GRPC wraps a GRPC server and optionally a GRPC Gateway and HTTP server on a single port.

func New

func New(address string, serv *grpc.Server, options ...Option) (*GRPC, error)

New is the constructor for GRPC. pb.Register<Name>Service() must have been called on the serv argument.

func (*GRPC) ListeningOn

func (g *GRPC) ListeningOn() net.Addr

ListeningOn is the address this GRPC server is currently listening on. Will return nil if it is not serving.

func (*GRPC) Port

func (g *GRPC) Port() int

Port returns the port the server is running on.

func (*GRPC) SetHTTPServer

func (g *GRPC) SetHTTPServer(serv *http.Server)

SetHTTPServer allows you to provide a custom *http.Server. The fields Addr, Handler and TLSConfig in the http.Server will be overridden no matter what is provided here. If not provided, a default one is provided with just Handler and TLSConfig configured. This does not work after Start() has been called.

func (*GRPC) Start

func (g *GRPC) Start() error

Start starts the server and enables listening for requests.

func (*GRPC) Stop

func (g *GRPC) Stop()

Stop stops the server gracefully and stops accepting new requests.

type GWRegistrationFunc

type GWRegistrationFunc func(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) error

GWRegistrationFunc is a function in your <service>.pb.gw.go file that is used to register a GRPC REST gateway to talk to the GRPC service. It is usually named Register<service>HandlerFromEndpoint().

type HTTPWrapper

type HTTPWrapper func(next http.Handler) http.Handler

HTTPWrapper provides an http.Hanlder that wraps another http.Handler. It is the responsibility of the HTTPWrapper implementation to call the wrapped Handler (or not to). A good reference on this pattern can be found at https://medium.com/@matryer/the-http-handler-wrapper-technique-in-golang-updated-bc7fbcffa702 .

type Option

type Option func(g *GRPC)

Option provides an optional argument for the GRPC constructor.

func Gateway

func Gateway(mux *runtime.ServeMux, fn GWRegistrationFunc, wrappers []HTTPWrapper, dialOptions []grpc.DialOption) Option

Gateway provides a GRPC REST gateway proxy that will answer all calls that have the header Content-Type set to "application/grpc-gateway". fn is a functions that registers this gateway with the GRPC server (see comments on GWRegistrationFunc type). wrappers are http.Handler(s) that you want to wrap the gateway (like authoriztion or acl handlers). If you want to wrap both the gateway and an HTTP server in the same handlers instead use the WrapHandlers() option. Note: handlers that do response compression or decompression should be added with the HTTPCompress() and HTTPDecompress() options. dialOptions provides options that are needed to dial your GRPC service from the gateway. You may need to provide dialOptions like grpc.WithInsecure() if you are using the WithInsecure() option above.

func HTTP

func HTTP(mux *http.ServeMux, wrappers []HTTPWrapper) Option

HTTP installs an http.ServeMux to handle all calls that do not have "application/grpc-gateway" or "application/grpc". wrappers provides http.Handler(s) that you want to wrap the mux in. If you want to install wrapper handlers on both the REST gateway and this ServeMux, you should use WrapHandlers(). Note: handlers that do response compression should be added with the HTTPCompress() option.

func HTTPCompress

func HTTPCompress(encodingType string, compressor Compressor) Option

HTTPCompress compresses responses to HTTP clients if the client sent an Accept-Encoding for the type. Order of preference for the compress method will be: 1) The same as encoding on the request if provided, 2) The order they were added with this option.

func HTTPDecompress

func HTTPDecompress(encodingType string, decompressor Decompressor) Option

HTTPDecompress decompresses requests coming from the clients for REST matching on the client request's Encoding-Type. This puts a http.Handler before all other handlers provided to do the decompression. "gzip" and "deflate" are automatically provided.

func WithInsecure

func WithInsecure() Option

WithInsecure is required in order to run without a TLS certificate.

func WithTLS

func WithTLS(certs []tls.Certificate) Option

WithTLS secures all services (GRPC/REST/HTTP) with the TLS certificates passed. If this option is not used, WithInsecure must be used.

func WrapHandlers

func WrapHandlers(handlers ...HTTPWrapper) Option

WrapHandlers wraps both the muxer passed with Gateway() and the the muxer passed to HTTP() with the handlers passed here. Note: handlers that do response compression should be added with the HTTPCompress() option. To wrap GRPC itself, you must use an UnaryServerInterceptor (defined at https://godoc.org/google.golang.org/grpc#UnaryServerInterceptor). You wrap it on the server using the UnaryInterceptor() ServerOption (https://godoc.org/google.golang.org/grpc#UnaryInterceptor) when doing grpc.NewServer().

type ResponseWriter

type ResponseWriter interface {
	io.WriteCloser
	http.ResponseWriter
}

ResponseWriter is a composition of an io.WriteCloser and http.ResponseWriter. This is used when trying to define a new response compression. See examples in the compress.go file.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL