blob: c454b6812b6009e2c64080f44ba69aec44984d91 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Daemon proxyd listens for connections from Vanadium services (typically
// behind NATs) and proxies these services to the outside world.
package main
import (
"flag"
"fmt"
"net/http"
_ "net/http/pprof"
"time"
"v.io/v23"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/x/lib/vlog"
"v.io/x/ref/lib/signals"
"v.io/x/ref/profiles/static"
)
var (
pubAddress = flag.String("published-address", "", "Network address the proxy publishes. If empty, the value of --address will be used")
healthzAddr = flag.String("healthz-address", "", "Network address on which the HTTP healthz server runs. It is intended to be used with a load balancer. The load balancer must be able to reach this address in order to verify that the proxy server is running")
name = flag.String("name", "", "Name to mount the proxy as")
)
func main() {
ctx, shutdown := v23.Init()
defer shutdown()
listenSpec := v23.GetListenSpec(ctx)
if len(listenSpec.Addrs) != 1 {
vlog.Fatalf("proxyd can only listen on one address: %v", listenSpec.Addrs)
}
if listenSpec.Proxy != "" {
vlog.Fatalf("proxyd cannot listen through another proxy")
}
proxyShutdown, proxyEndpoint, err := static.NewProxy(ctx, listenSpec.Addrs[0].Protocol, listenSpec.Addrs[0].Address, *pubAddress, *name)
if err != nil {
vlog.Fatal(err)
}
defer proxyShutdown()
if len(*name) > 0 {
// Print out a directly accessible name for the proxy table so
// that integration tests can reliably read it from stdout.
fmt.Printf("NAME=%s\n", proxyEndpoint.Name())
}
if len(*healthzAddr) != 0 {
go startHealthzServer(*healthzAddr)
}
// Start an RPC Server that listens through the proxy itself. This
// server will serve reserved methods only.
server, err := v23.NewServer(ctx)
if err != nil {
vlog.Fatalf("NewServer failed: %v", err)
}
defer server.Stop()
ls := rpc.ListenSpec{Proxy: proxyEndpoint.Name()}
if _, err := server.Listen(ls); err != nil {
vlog.Fatalf("Listen(%v) failed: %v", ls, err)
}
var monitoringName string
if len(*name) > 0 {
monitoringName = *name + "-mon"
}
if err := server.ServeDispatcher(monitoringName, &nilDispatcher{}); err != nil {
vlog.Fatalf("ServeDispatcher(%v) failed: %v", monitoringName, err)
}
<-signals.ShutdownOnSignals(ctx)
}
type nilDispatcher struct{}
func (nilDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
return nil, nil, nil
}
// healthzHandler implements net/http.Handler
type healthzHandler struct{}
func (healthzHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}
// startHealthzServer starts a HTTP server that simply returns "ok" to every
// request. This is needed to let the load balancer know that the proxy server
// is running.
func startHealthzServer(addr string) {
s := http.Server{
Addr: addr,
Handler: healthzHandler{},
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
if err := s.ListenAndServe(); err != nil {
vlog.Fatal(err)
}
}