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 {}
+}