GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

main.go 3.59 KB
Newer Older
1 2 3
package main

import (
4
	"context"
5 6 7 8 9
	"flag"
	"io"
	"log"
	"net/http"
	"os"
10
	"os/signal"
11
	"sync"
12
	"syscall"
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
	"time"

	"git.torproject.org/pluggable-transports/snowflake.git/common/safelog"
	"github.com/gorilla/mux"
)

type Route struct {
	Name        string
	Method      string
	Pattern     string
	HandlerFunc http.HandlerFunc
}

type Routes []Route

var routes = Routes{
	Route{
30 31 32 33
		"BridgeState",
		"GET",
		"/bridge-state",
		BridgeState,
34
	},
35 36 37 38 39 40
	Route{
		"BridgeStateWeb",
		"GET",
		"/result",
		BridgeStateWeb,
	},
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
}

// Logger logs when we receive requests, and the execution time of handling
// these requests.  We don't log client IP addresses or the given obfs4
// parameters.
func Logger(inner http.Handler, name string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		inner.ServeHTTP(w, r)

		log.Printf(
			"%s\t%s\t%s\t%s",
			r.Method,
			r.RequestURI,
			name,
			time.Since(start),
		)
	})
}

// NewRouter creates and returns a new request router.
func NewRouter() *mux.Router {

	router := mux.NewRouter().StrictSlash(true)
	for _, route := range routes {
		var handler http.Handler

		handler = route.HandlerFunc
		handler = Logger(handler, route.Name)

		router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(handler)
	}

	return router
}

func main() {

	var addr string
85
	var web bool
86
	var certFilename, keyFilename string
87
	var cacheFile string
88
	var templatesDir string
89
	var numSecs int
90

Philipp Winter's avatar
Philipp Winter committed
91
	flag.StringVar(&addr, "addr", ":5000", "Address to listen on.")
92
	flag.BoolVar(&web, "web", false, "Enable the web interface (in addition to the JSON API).")
93 94
	flag.StringVar(&certFilename, "cert", "", "TLS certificate file.")
	flag.StringVar(&keyFilename, "key", "", "TLS private key file.")
95
	flag.StringVar(&cacheFile, "cache", "bridgestrap-cache.bin", "Cache file that contains test results.")
96
	flag.StringVar(&templatesDir, "templates", "templates", "Path to directory that contains our web templates.")
97
	flag.IntVar(&numSecs, "seconds", 2, "Number of seconds after two subsequent requests are handled.")
98 99 100 101 102 103 104
	flag.Parse()

	var logOutput io.Writer = os.Stderr
	// Send the log output through our scrubber first.
	log.SetOutput(&safelog.LogScrubber{Output: logOutput})
	log.SetFlags(log.LstdFlags | log.LUTC)

105 106
	LoadHtmlTemplates(templatesDir)

107 108 109 110 111 112 113 114 115 116 117
	if web {
		log.Println("Enabling web interface.")
		routes = append(routes,
			Route{
				"Index",
				"GET",
				"/",
				Index,
			})
	}

118 119 120
	if err := cache.ReadFromDisk(cacheFile); err != nil {
		log.Printf("Could not read cache because: %s", err)
	}
121 122 123 124 125

	var srv http.Server
	srv.Addr = addr
	srv.Handler = NewRouter()
	log.Printf("Starting service on port %s.", addr)
126
	go func() {
127 128 129 130
		if certFilename != "" && keyFilename != "" {
			srv.ListenAndServeTLS(certFilename, keyFilename)
		} else {
			srv.ListenAndServe()
131 132 133
		}
	}()

134 135 136
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT)
	signal.Notify(signalChan, syscall.SIGTERM)
137 138 139 140

	var wg sync.WaitGroup
	shutdown := make(chan bool)
	go dispatchRequests(shutdown, &wg, numSecs)
141 142
	log.Printf("Waiting for signal to shut down.")
	<-signalChan
143
	shutdown <- true
144 145

	log.Printf("Received signal to shut down.")
146 147
	// Give our Web server a maximum of a minute to finish handling open
	// connections and shut down gracefully.
148 149 150 151 152 153 154 155 156
	t := time.Now().Add(time.Minute)
	ctx, cancel := context.WithDeadline(context.Background(), t)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		log.Printf("Failed to shut down Web server: %s", err)
	}

	if err := cache.WriteToDisk(cacheFile); err != nil {
		log.Printf("Failed to write cache to disk: %s", err)
157
	}
158
	wg.Wait()
159
}