github.com/jgarto/itcv@v0.0.0-20180826224514-4eea09c1aa0d/examples/sites/latency/latency.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "math/rand" 6 "sort" 7 "time" 8 9 "honnef.co/go/js/dom" 10 "myitcv.io/react" 11 "myitcv.io/react/jsx" 12 ) 13 14 //go:generate immutableGen 15 16 type location string 17 18 var ( 19 locations = [...]location{ 20 "Oregon", 21 "California", 22 "Ohio", 23 "Virginia", 24 "Ireland", 25 "Frankfurt", 26 "London", 27 "Mumbai", 28 "Singapore", 29 "Seoul", 30 "Tokyo", 31 "Sydney", 32 } 33 ) 34 35 type _Imm_latencies map[location]latency 36 37 type latency struct { 38 dns time.Duration 39 tcp time.Duration 40 tls time.Duration 41 wait time.Duration 42 download time.Duration 43 total time.Duration 44 } 45 46 type LatencyDef struct { 47 react.ComponentDef 48 } 49 50 type LatencyState struct { 51 reqId int 52 output bool 53 54 url string 55 altUrl string 56 57 *latencies 58 } 59 60 func Latency() *LatencyElem { 61 return buildLatencyElem() 62 } 63 64 func (l LatencyDef) Render() react.Element { 65 var c react.Element 66 67 if l.State().output { 68 c = l.renderOutput() 69 } else { 70 c = l.renderInput() 71 } 72 73 return react.Div(&react.DivProps{ClassName: "App"}, 74 react.Div(&react.DivProps{ClassName: "Content center full column"}, 75 jsx.HTMLElem(` 76 <div class="Title margin center"> 77 <span class="text">Latency</span> 78 <span class="subtext">Global latency testing tool</span> 79 </div> 80 `), 81 c, 82 jsx.HTMLElem(` 83 <div class="Title margin center"> 84 <br/> 85 <span class="subtext" style="font-size:smaller; font-style: italic">(randomly generated results)</span> 86 <span class="subtext" style="font-size:smaller; font-style: italic"> 87 Real, original version <a href="https://latency.apex.sh/" target="_blank">https://latency.apex.sh/</a> 88 </span> 89 </div> 90 `), 91 ), 92 ) 93 } 94 95 func (l LatencyDef) renderInput() react.Element { 96 return react.Form(&react.FormProps{ClassName: "LatencyForm"}, 97 react.Div(&react.DivProps{ClassName: "group"}, 98 react.Input(&react.InputProps{ 99 Type: "text", 100 ID: "url", 101 Placeholder: "url to test (can be anything)", 102 Value: l.State().url, 103 OnChange: urlChange{l}, 104 }), 105 react.Input(&react.InputProps{ 106 Type: "text", 107 ID: "altUrl", 108 Placeholder: "comparison url (not used)", 109 Value: l.State().altUrl, 110 OnChange: altUrlChange{l}, 111 }), 112 ), 113 react.Button( 114 &react.ButtonProps{ 115 ClassName: "Button small", 116 OnClick: check{l}, 117 }, 118 react.S("Start"), 119 ), 120 ) 121 } 122 123 const ( 124 // gross hack for now 125 resultWidth = 500.0 126 ) 127 128 func (l *LatencyDef) renderOutput() react.Element { 129 var regions []react.Element 130 131 ls := l.State().latencies 132 133 maxTot := time.Duration(0) 134 135 for _, lat := range ls.Range() { 136 if lat.total > maxTot { 137 maxTot = lat.total 138 } 139 } 140 141 awfulTime := maxTot / 3 142 okTime := maxTot * 2 / 3 143 144 for _, v := range locations { 145 regClass := "Region" 146 147 timings := []react.Element{ 148 react.Span(&react.SpanProps{ClassName: "total"}, react.S("0ms")), 149 } 150 151 res, ok := ls.Get(v) 152 if ok { 153 if res.total < awfulTime { 154 regClass += " with-timings speed-awful" 155 } else if res.total < okTime { 156 regClass += " with-timings speed-ok" 157 } else { 158 regClass += " with-timings speed-fast" 159 } 160 161 genTiming := func(f time.Duration, n, l string) *react.SpanElem { 162 w := fmt.Sprintf("%.3fpx", float64(f)/float64(maxTot)*resultWidth) 163 rs := fmt.Sprintf("%v (%v)", l, f) 164 165 return react.Span( 166 &react.SpanProps{ 167 ClassName: "timing " + n, 168 Style: &react.CSS{Width: w}, 169 }, 170 react.S(rs), 171 ) 172 } 173 174 timings = []react.Element{ 175 genTiming(res.dns, "dns", "DNS"), 176 genTiming(res.tcp, "tcp", "TCP"), 177 genTiming(res.tls, "tls", "TLS"), 178 genTiming(res.wait, "wait", "Wait"), 179 genTiming(res.download, "download", "Download"), 180 react.Span(&react.SpanProps{ClassName: "total"}, react.S(fmt.Sprintf("%v", res.total))), 181 } 182 } 183 184 rd := react.Div(&react.DivProps{ClassName: regClass}, 185 react.Span(&react.SpanProps{ClassName: "name"}, react.S(v)), 186 react.Div(&react.DivProps{ClassName: "Results"}, 187 react.Div(&react.DivProps{ClassName: "Timings"}, timings...), 188 ), 189 ) 190 191 regions = append(regions, rd) 192 } 193 194 return react.Div(&react.DivProps{ClassName: "Regions"}, 195 regions..., 196 ) 197 } 198 199 func (l *LatencyDef) reset(e *react.SyntheticMouseEvent) { 200 s := l.State() 201 s.output = false 202 s.reqId++ 203 l.SetState(s) 204 205 e.PreventDefault() 206 } 207 208 type urlChange struct{ l LatencyDef } 209 type altUrlChange struct{ l LatencyDef } 210 type check struct{ l LatencyDef } 211 212 func (u urlChange) OnChange(se *react.SyntheticEvent) { 213 target := se.Target().(*dom.HTMLInputElement) 214 s := u.l.State() 215 s.url = target.Value 216 u.l.SetState(s) 217 } 218 219 func (a altUrlChange) OnChange(se *react.SyntheticEvent) { 220 target := se.Target().(*dom.HTMLInputElement) 221 s := a.l.State() 222 s.altUrl = target.Value 223 a.l.SetState(s) 224 } 225 226 // this could clearly be replace by something that actually checks 227 // the realy latencies instead of randomly generating them 228 func (c check) OnClick(e *react.SyntheticMouseEvent) { 229 l := c.l 230 231 reqId := l.State().reqId 232 233 for _, v := range locations { 234 loc := v 235 to := rand.Int63n(3000) * int64(time.Millisecond) 236 237 go func() { 238 <-time.After(time.Duration(to)) 239 s := l.State() 240 241 if s.reqId == reqId { 242 lat := latency{} 243 244 ints := make([]int64, 4) 245 246 for i := range ints { 247 ints[i] = rand.Int63n(to) 248 } 249 250 ints = append([]int64{0}, ints...) 251 ints = append(ints, to) 252 253 sort.Slice(ints, func(i, j int) bool { 254 return ints[i] < ints[j] 255 }) 256 257 vs := make([]time.Duration, len(ints)-1) 258 259 for i := range vs { 260 vs[i] = time.Duration(ints[i+1] - ints[i]) 261 } 262 263 lat.dns = vs[0] 264 lat.tcp = vs[1] 265 lat.tls = vs[2] 266 lat.wait = vs[3] 267 lat.download = vs[4] 268 lat.total = time.Duration(to) 269 270 s.latencies = s.latencies.Set(loc, lat) 271 272 l.SetState(s) 273 } 274 275 }() 276 } 277 278 s := l.State() 279 s.output = true 280 s.latencies = newLatencies() 281 l.SetState(s) 282 283 e.PreventDefault() 284 }