Initial Commit
diff --git a/grpc.go b/grpc.go
new file mode 100644
index 0000000..ddc978f
--- /dev/null
+++ b/grpc.go
@@ -0,0 +1,143 @@
+package main
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+
+ "github.com/golang/glog"
+ "github.com/q3k/statusz"
+ "golang.org/x/net/trace"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+ "google.golang.org/grpc/reflection"
+
+ pb "code.hackerspace.pl/q3k/arista-proxy/proto"
+)
+
+type serverOpts struct {
+ listenAddress string
+ debugAddress string
+ tlsCAPath string
+ tlsCertificatePath string
+ tlsKeyPath string
+ pkiRealm string
+}
+
+type server struct {
+ arista *aristaClient
+ opts *serverOpts
+
+ grpc struct {
+ listen net.Listener
+ server *grpc.Server
+ }
+ http struct {
+ listen net.Listener
+ server *http.Server
+ }
+}
+
+func newServer(opts *serverOpts, arista *aristaClient) (*server, error) {
+ return &server{
+ opts: opts,
+ arista: arista,
+ }, nil
+}
+
+func (s *server) trace(ctx context.Context, f string, args ...interface{}) {
+ tr, ok := trace.FromContext(ctx)
+ if !ok {
+ fmtd := fmt.Sprintf(f, args...)
+ glog.Warningf("No trace in %v: %s", ctx, fmtd)
+ return
+ }
+ tr.LazyPrintf(f, args...)
+}
+
+func (s *server) setupGRPC(options ...grpc.ServerOption) error {
+ serverCert, err := tls.LoadX509KeyPair(s.opts.tlsCertificatePath, s.opts.tlsKeyPath)
+ if err != nil {
+ return fmt.Errorf("while loading keypair: %v", err)
+ }
+
+ certPool := x509.NewCertPool()
+ ca, err := ioutil.ReadFile(s.opts.tlsCAPath)
+ if err != nil {
+ return fmt.Errorf("while loading ca certificate: %v", err)
+ }
+ if ok := certPool.AppendCertsFromPEM(ca); !ok {
+ return fmt.Errorf("while appending ca certificate to pool: %v", err)
+ }
+
+ lis, err := net.Listen("tcp", s.opts.listenAddress)
+ if err != nil {
+ return fmt.Errorf("while listening on main port: %v", err)
+ }
+
+ creds := credentials.NewTLS(&tls.Config{
+ ClientAuth: tls.RequireAndVerifyClientCert,
+ Certificates: []tls.Certificate{serverCert},
+ ClientCAs: certPool,
+ })
+
+ s.grpc.listen = lis
+ options = append([]grpc.ServerOption{grpc.Creds(creds)}, options...)
+ s.grpc.server = grpc.NewServer(options...)
+
+ return nil
+}
+
+func (s *server) setupDebugHTTP(mux http.Handler) error {
+ lis, err := net.Listen("tcp", s.opts.debugAddress)
+ if err != nil {
+ return fmt.Errorf("while listening on main port: %v", err)
+ }
+
+ s.http.listen = lis
+ s.http.server = &http.Server{
+ Addr: s.opts.debugAddress,
+ Handler: mux,
+ }
+
+ return nil
+}
+
+func (s *server) serveForever() {
+ grpc.EnableTracing = true
+
+ if err := s.setupGRPC(grpc.UnaryInterceptor(s.unaryInterceptor)); err != nil {
+ glog.Exitf("Could not setup GRPC server: %v", err)
+ }
+ pb.RegisterAristaProxyServer(s.grpc.server, s)
+ reflection.Register(s.grpc.server)
+
+ go func() {
+ if err := s.grpc.server.Serve(s.grpc.listen); err != nil {
+ glog.Exitf("Could not start GRPC server: %v", err)
+ }
+ }()
+ glog.Infof("Listening for GRPC on %v", s.opts.listenAddress)
+
+ httpMux := http.NewServeMux()
+ httpMux.HandleFunc("/debug/status", statusz.StatusHandler)
+ httpMux.HandleFunc("/debug/requests", trace.Traces)
+ httpMux.HandleFunc("/", statusz.StatusHandler)
+
+ if err := s.setupDebugHTTP(httpMux); err != nil {
+ glog.Exitf("Could not setup HTTP server: %v", err)
+ }
+
+ go func() {
+ if err := s.http.server.Serve(s.http.listen); err != nil {
+ glog.Exitf("Could not start HTTP server: %v", err)
+ }
+ }()
+ glog.Infof("Listening for HTTP on %v", s.opts.debugAddress)
+
+ select {}
+}