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.

handlers.go 3.39 KB
Newer Older
1 2 3 4 5 6 7 8 9
package main

import (
	"encoding/json"
	"fmt"
	"golang.org/x/time/rate"
	"io/ioutil"
	"log"
	"net/http"
10
	"path"
11 12 13
	"time"
)

14 15 16
var IndexPage string
var SuccessPage string
var FailurePage string
17 18 19 20 21

// TestResult represents the result of a test, sent back to the client as JSON
// object.
type TestResult struct {
	Functional bool    `json:"functional"`
22
	Error      string  `json:"error,omitempty"`
23 24 25
	Time       float64 `json:"time"`
}

26 27 28 29
type TestRequest struct {
	BridgeLine string `json:"bridge_line"`
}

30 31 32 33
// limiter implements a rate limiter.  We allow 1 request per second on average
// with bursts of up to 5 requests per second.
var limiter = rate.NewLimiter(1, 5)

34 35 36 37 38 39 40 41
// LoadHtmlTemplates loads all HTML templates from the given directory.
func LoadHtmlTemplates(dir string) {

	IndexPage = LoadHtmlTemplate(path.Join(dir, "index.html"))
	SuccessPage = LoadHtmlTemplate(path.Join(dir, "success.html"))
	FailurePage = LoadHtmlTemplate(path.Join(dir, "failure.html"))
}

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
// LoadHtmlTemplate reads the content of the given filename and returns it as
// string.  If the function is unable to read the file, it logs a fatal error.
func LoadHtmlTemplate(filename string) string {

	content, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	return string(content)
}

func SendResponse(w http.ResponseWriter, response string) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, response)
}

func SendHtmlResponse(w http.ResponseWriter, response string) {

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	SendResponse(w, response)
}

func SendJSONResponse(w http.ResponseWriter, response string) {

	w.Header().Set("Content-Type", "application/json")
67
	log.Printf("Test result: %s", response)
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
	SendResponse(w, response)
}

func Index(w http.ResponseWriter, r *http.Request) {

	SendHtmlResponse(w, IndexPage)
}

func createJsonResult(err error, start time.Time) string {

	end := time.Now()
	result := &TestResult{
		Functional: err == nil,
		Error:      "",
		Time:       float64(end.Sub(start).Milliseconds()) / 1000}
	if err != nil {
		result.Error = err.Error()
	}

	jsonResult, err := json.Marshal(result)
	if err != nil {
		log.Printf("Bug: %s", err)
	}

	return string(jsonResult)
}

95
func BridgeState(w http.ResponseWriter, r *http.Request) {
96 97

	start := time.Now()
98 99 100 101 102 103 104 105 106 107 108 109 110 111

	b, err := ioutil.ReadAll(r.Body)
	defer r.Body.Close()
	if err != nil {
		log.Printf("Failed to read HTTP body: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	req := &TestRequest{}
	if err := json.Unmarshal(b, &req); err != nil {
		log.Printf("Failed to unmarshal HTTP body %q: %s", b, err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
112 113
	}

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	if req.BridgeLine == "" {
		log.Printf("Got request with empty bridge line.")
		http.Error(w, "no bridge line given", http.StatusBadRequest)
		return
	}
	err = bootstrapTorOverBridge(req.BridgeLine)
	SendJSONResponse(w, createJsonResult(err, start))
}

func BridgeStateWeb(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	// Rate-limit Web requests to prevent someone from abusing this service
	// as a port scanner.
	if limiter.Allow() == false {
		SendHtmlResponse(w, "Rate limit exceeded.")
		return
	}
132 133
	bridgeLine := r.Form.Get("bridge_line")
	if bridgeLine == "" {
134
		SendHtmlResponse(w, "No bridge line given.")
135 136
		return
	}
137 138
	if err := bootstrapTorOverBridge(bridgeLine); err == nil {
		SendHtmlResponse(w, SuccessPage)
139
	} else {
140
		SendHtmlResponse(w, FailurePage)
141 142
	}
}