blob: ce1ed226726498e723dd84ad6fd266d7988efc12 [file] [log] [blame]
Sergiusz Bazanskia51df9c2019-07-13 16:07:13 +02001package main
2
3// Copyright 2019 Serge Bazanski
4//
5// Permission to use, copy, modify, and/or distribute this software for any
6// purpose with or without fee is hereby granted, provided that the above
7// copyright notice and this permission notice appear in all copies.
8//
9// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
14// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17import (
18 "crypto/rand"
19 "encoding/json"
20 "flag"
21 "io"
22 "io/ioutil"
23 "net"
24 "net/http"
25 "strconv"
26 "strings"
27
28 "github.com/golang/glog"
29)
30
31var (
32 flagBind string
33 flagUseForwardedFor bool
34)
35
36func cors(w http.ResponseWriter) {
37 w.Header().Add("Access-Control-Allow-Origin", "*")
38 w.Header().Add("Access-Control-Allow-Methods", "GET, POST")
39}
40
41func noCache(w http.ResponseWriter) {
42 w.Header().Add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, s-maxage=0")
43 w.Header().Add("Cache-Control", " post-check=0, pre-check=0")
44 w.Header().Add("Pragma", "no-cache")
45}
46
47func remoteIP(r *http.Request) net.IP {
48 remote := strings.TrimSpace(r.RemoteAddr)
49 if flagUseForwardedFor {
50 remote = strings.TrimSpace(r.Header.Get("X-Forwarded-For"))
51 }
52
53 if remote == "" {
54 return nil
55 }
56
57 if strings.HasPrefix(remote, "[") {
58 // Looks like a [::1] v6 address
59 inner := strings.Split(strings.Split(remote, "[")[1], "]")[0]
60 return net.ParseIP(inner)
61 }
62
63 if strings.Count(remote, ":") == 1 {
64 // Looks like a :4321 port suffix
65 inner := strings.Split(remote, ":")[0]
66 return net.ParseIP(inner)
67 }
68
69 return net.ParseIP(remote)
70}
71
72func init() {
73 flag.Set("logtostderr", "true")
74}
75
76func main() {
77 flag.StringVar(&flagBind, "bind", "0.0.0.0:8080", "Address at which to serve HTTP requests")
78 flag.BoolVar(&flagUseForwardedFor, "use_forwarded_for", false, "Honor X-Forwarded-For headers to detect user IP")
79 flag.Parse()
80 http.HandleFunc("/empty", func(w http.ResponseWriter, r *http.Request) {
81 cors(w)
82 noCache(w)
83 w.Header().Add("Connection", "keep-alive")
84 w.WriteHeader(http.StatusOK)
85 io.Copy(ioutil.Discard, r.Body)
86 })
87 http.HandleFunc("/garbage", func(w http.ResponseWriter, r *http.Request) {
88 cors(w)
89 w.Header().Add("Content-Description", "File Tranfer")
90 w.Header().Add("Content-Type", "application/octet-stream")
91 w.Header().Add("Content-Disposition", "attachment; filename=random.dat")
92 w.Header().Add("Content-Transfer-Encoding", "binary")
93 noCache(w)
94 w.WriteHeader(http.StatusOK)
95
96 chunks := 4
97 if val, err := strconv.Atoi(r.Header.Get("ckSize")); err == nil {
98 if val > 0 && val <= 1024 {
99 chunks = val
100 }
101 }
102
103 chunk := make([]byte, 1024*1024)
104 rand.Read(chunk)
105
106 for i := 0; i < chunks; i += 1 {
107 w.Write(chunk)
108 }
109 })
110 http.HandleFunc("/ip", func(w http.ResponseWriter, r *http.Request) {
111 cors(w)
112 noCache(w)
113
114 res := struct {
115 ProcessedString string `json:processedString`
116 RawISPInfo string `json:rawIspInfo`
117 }{
118 ProcessedString: "",
119 RawISPInfo: "",
120 }
121
122 ip := remoteIP(r)
123 if ip != nil {
124 res.ProcessedString = ip.String()
125 }
126
127 w.Header().Set("Content-Type", "application/json")
128
129 json.NewEncoder(w).Encode(res)
130 })
131 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
132 http.ServeFile(w, r, "index.html")
133 })
134 http.HandleFunc("/speedtest.js", func(w http.ResponseWriter, r *http.Request) {
135 http.ServeFile(w, r, "speedtest.js")
136 })
137 http.HandleFunc("/speedtest_worker.js", func(w http.ResponseWriter, r *http.Request) {
138 http.ServeFile(w, r, "speedtest_worker.js")
139 })
140
141 glog.Infof("Starting up at %v", flagBind)
142 err := http.ListenAndServe(flagBind, nil)
143 if err != nil {
144 glog.Exit(err)
145 }
146}