rewrite api package
This commit is contained in:
@@ -7,7 +7,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/micro/go-micro/v3/api/server/acme"
|
"github.com/micro/go-micro/v3/api/acme"
|
||||||
"github.com/micro/go-micro/v3/logger"
|
"github.com/micro/go-micro/v3/logger"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
@@ -8,7 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
"github.com/micro/go-micro/v3/api/server/acme"
|
"github.com/micro/go-micro/v3/api/acme"
|
||||||
"github.com/micro/go-micro/v3/logger"
|
"github.com/micro/go-micro/v3/logger"
|
||||||
)
|
)
|
||||||
|
|
16
api/api.go
16
api/api.go
@@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -9,23 +10,24 @@ import (
|
|||||||
"github.com/micro/go-micro/v3/server"
|
"github.com/micro/go-micro/v3/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Api interface {
|
// Gateway is an api gateway interface
|
||||||
|
type Gateway interface {
|
||||||
// Initialise options
|
// Initialise options
|
||||||
Init(...Option) error
|
Init(...Option) error
|
||||||
// Get the options
|
// Get the options
|
||||||
Options() Options
|
Options() Options
|
||||||
// Register a http handler
|
// Register an endpoint
|
||||||
Register(*Endpoint) error
|
Register(*Endpoint) error
|
||||||
// Register a route
|
// Deregister a route
|
||||||
Deregister(*Endpoint) error
|
Deregister(*Endpoint) error
|
||||||
|
// Register http handler
|
||||||
|
Handle(path string, hd http.Handler)
|
||||||
|
// Start serving requests
|
||||||
|
Serve() error
|
||||||
// Implementation of api
|
// Implementation of api
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct{}
|
|
||||||
|
|
||||||
type Option func(*Options) error
|
|
||||||
|
|
||||||
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
// RPC Method e.g. Greeter.Hello
|
// RPC Method e.g. Greeter.Hello
|
||||||
|
@@ -5,70 +5,62 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/gorilla/handlers"
|
"github.com/micro/go-micro/v3/api"
|
||||||
"github.com/micro/go-micro/v3/api/server"
|
|
||||||
"github.com/micro/go-micro/v3/api/server/cors"
|
|
||||||
"github.com/micro/go-micro/v3/logger"
|
"github.com/micro/go-micro/v3/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type httpServer struct {
|
type httpServer struct {
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
opts server.Options
|
opts api.Options
|
||||||
|
|
||||||
mtx sync.RWMutex
|
mtx sync.RWMutex
|
||||||
address string
|
address string
|
||||||
exit chan chan error
|
exit chan chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(address string, opts ...server.Option) server.Server {
|
// NewGateway returns a new HTTP api gateway
|
||||||
var options server.Options
|
func NewGateway(opts ...api.Option) api.Gateway {
|
||||||
|
var options api.Options
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &httpServer{
|
return &httpServer{
|
||||||
opts: options,
|
opts: options,
|
||||||
mux: http.NewServeMux(),
|
mux: http.NewServeMux(),
|
||||||
address: address,
|
exit: make(chan chan error),
|
||||||
exit: make(chan chan error),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *httpServer) Address() string {
|
func (s *httpServer) Init(opts ...api.Option) error {
|
||||||
s.mtx.RLock()
|
|
||||||
defer s.mtx.RUnlock()
|
|
||||||
return s.address
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpServer) Init(opts ...server.Option) error {
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&s.opts)
|
o(&s.opts)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *httpServer) Options() api.Options {
|
||||||
|
return s.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *httpServer) Register(ep *api.Endpoint) error { return nil }
|
||||||
|
func (s *httpServer) Deregister(ep *api.Endpoint) error { return nil }
|
||||||
|
|
||||||
func (s *httpServer) Handle(path string, handler http.Handler) {
|
func (s *httpServer) Handle(path string, handler http.Handler) {
|
||||||
// TODO: move this stuff out to one place with ServeHTTP
|
|
||||||
|
|
||||||
// apply the wrappers, e.g. auth
|
|
||||||
for _, wrapper := range s.opts.Wrappers {
|
|
||||||
handler = wrapper(handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap with cors
|
|
||||||
if s.opts.EnableCORS {
|
|
||||||
handler = cors.CombinedCORSHandler(handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap with logger
|
|
||||||
handler = handlers.CombinedLoggingHandler(os.Stdout, handler)
|
|
||||||
|
|
||||||
s.mux.Handle(path, handler)
|
s.mux.Handle(path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *httpServer) Serve() error {
|
||||||
|
if err := s.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
<-s.exit
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *httpServer) Start() error {
|
func (s *httpServer) Start() error {
|
||||||
var l net.Listener
|
var l net.Listener
|
||||||
var err error
|
var err error
|
||||||
@@ -77,10 +69,10 @@ func (s *httpServer) Start() error {
|
|||||||
// should we check the address to make sure its using :443?
|
// should we check the address to make sure its using :443?
|
||||||
l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...)
|
l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...)
|
||||||
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
|
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
|
||||||
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
|
l, err = tls.Listen("tcp", s.opts.Address, s.opts.TLSConfig)
|
||||||
} else {
|
} else {
|
||||||
// otherwise plain listen
|
// otherwise plain listen
|
||||||
l, err = net.Listen("tcp", s.address)
|
l, err = net.Listen("tcp", s.opts.Address)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -90,10 +82,6 @@ func (s *httpServer) Start() error {
|
|||||||
logger.Infof("HTTP API Listening on %s", l.Addr().String())
|
logger.Infof("HTTP API Listening on %s", l.Addr().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mtx.Lock()
|
|
||||||
s.address = l.Addr().String()
|
|
||||||
s.mtx.Unlock()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := http.Serve(l, s.mux); err != nil {
|
if err := http.Serve(l, s.mux); err != nil {
|
||||||
// temporary fix
|
// temporary fix
|
@@ -1,37 +1,27 @@
|
|||||||
package server
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/v3/api/acme"
|
||||||
"github.com/micro/go-micro/v3/api/resolver"
|
"github.com/micro/go-micro/v3/api/resolver"
|
||||||
"github.com/micro/go-micro/v3/api/server/acme"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Option func(o *Options)
|
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
Address string
|
||||||
EnableACME bool
|
EnableACME bool
|
||||||
EnableCORS bool
|
|
||||||
ACMEProvider acme.Provider
|
ACMEProvider acme.Provider
|
||||||
EnableTLS bool
|
EnableTLS bool
|
||||||
ACMEHosts []string
|
ACMEHosts []string
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
Resolver resolver.Resolver
|
Resolver resolver.Resolver
|
||||||
Wrappers []Wrapper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Wrapper func(h http.Handler) http.Handler
|
type Option func(o *Options)
|
||||||
|
|
||||||
func WrapHandler(w ...Wrapper) Option {
|
func Address(a string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Wrappers = append(o.Wrappers, w...)
|
o.Address = a
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func EnableCORS(b bool) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.EnableCORS = b
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@@ -1,44 +0,0 @@
|
|||||||
package cors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CombinedCORSHandler wraps a server and provides CORS headers
|
|
||||||
func CombinedCORSHandler(h http.Handler) http.Handler {
|
|
||||||
return corsHandler{h}
|
|
||||||
}
|
|
||||||
|
|
||||||
type corsHandler struct {
|
|
||||||
handler http.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c corsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
SetHeaders(w, r)
|
|
||||||
|
|
||||||
if r.Method == "OPTIONS" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.handler.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHeaders sets the CORS headers
|
|
||||||
func SetHeaders(w http.ResponseWriter, r *http.Request) {
|
|
||||||
set := func(w http.ResponseWriter, k, v string) {
|
|
||||||
if v := w.Header().Get(k); len(v) > 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Set(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if origin := r.Header.Get("Origin"); len(origin) > 0 {
|
|
||||||
set(w, "Access-Control-Allow-Origin", origin)
|
|
||||||
} else {
|
|
||||||
set(w, "Access-Control-Allow-Origin", "*")
|
|
||||||
}
|
|
||||||
|
|
||||||
set(w, "Access-Control-Allow-Credentials", "true")
|
|
||||||
set(w, "Access-Control-Allow-Methods", "POST, PATCH, GET, OPTIONS, PUT, DELETE")
|
|
||||||
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Namespace")
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHTTPServer(t *testing.T) {
|
|
||||||
testResponse := "hello world"
|
|
||||||
|
|
||||||
s := NewServer("localhost:0")
|
|
||||||
|
|
||||||
s.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
fmt.Fprint(w, testResponse)
|
|
||||||
}))
|
|
||||||
|
|
||||||
if err := s.Start(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rsp, err := http.Get(fmt.Sprintf("http://%s/", s.Address()))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rsp.Body.Close()
|
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(rsp.Body)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(b) != testResponse {
|
|
||||||
t.Fatalf("Unexpected response, got %s, expected %s", string(b), testResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.Stop(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
// Package server provides an API gateway server which handles inbound requests
|
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server serves api requests
|
|
||||||
type Server interface {
|
|
||||||
Address() string
|
|
||||||
Init(opts ...Option) error
|
|
||||||
Handle(path string, handler http.Handler)
|
|
||||||
Start() error
|
|
||||||
Stop() error
|
|
||||||
}
|
|
Reference in New Issue
Block a user