github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-hub/http.go (about) 1 // Copyright 2016 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package main 5 6 import ( 7 "fmt" 8 "html/template" 9 "net" 10 "net/http" 11 "sort" 12 "strings" 13 14 "github.com/google/syzkaller/pkg/log" 15 ) 16 17 func (hub *Hub) initHTTP(addr string) { 18 http.HandleFunc("/", hub.httpSummary) 19 20 ln, err := net.Listen("tcp4", addr) 21 if err != nil { 22 log.Fatalf("failed to listen on %v: %v", addr, err) 23 } 24 log.Logf(0, "serving http on http://%v", ln.Addr()) 25 go func() { 26 err := http.Serve(ln, nil) 27 log.Fatalf("failed to serve http: %v", err) 28 }() 29 } 30 31 func (hub *Hub) httpSummary(w http.ResponseWriter, r *http.Request) { 32 hub.mu.Lock() 33 defer hub.mu.Unlock() 34 35 data := &UISummaryData{ 36 Log: log.CachedLogOutput(), 37 } 38 total := UIManager{ 39 Name: "total", 40 Corpus: len(hub.st.Corpus.Records), 41 Repros: len(hub.st.Repros.Records), 42 } 43 for name, mgr := range hub.st.Managers { 44 total.Added += mgr.Added 45 total.Deleted += mgr.Deleted 46 total.New += mgr.New 47 total.SentRepros += mgr.SentRepros 48 total.RecvRepros += mgr.RecvRepros 49 data.Managers = append(data.Managers, UIManager{ 50 Name: name, 51 HTTP: mgr.HTTP, 52 Domain: mgr.Domain, 53 Corpus: len(mgr.Corpus.Records), 54 Added: mgr.Added, 55 Deleted: mgr.Deleted, 56 New: mgr.New, 57 SentRepros: mgr.SentRepros, 58 RecvRepros: mgr.RecvRepros, 59 }) 60 } 61 sort.Slice(data.Managers, func(i, j int) bool { 62 return data.Managers[i].Name < data.Managers[j].Name 63 }) 64 data.Managers = append([]UIManager{total}, data.Managers...) 65 if err := summaryTemplate.Execute(w, data); err != nil { 66 log.Logf(0, "failed to execute template: %v", err) 67 http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) 68 return 69 } 70 } 71 72 func compileTemplate(html string) *template.Template { 73 return template.Must(template.New("").Parse(strings.ReplaceAll(html, "{{STYLE}}", htmlStyle))) 74 } 75 76 type UISummaryData struct { 77 Managers []UIManager 78 Log string 79 } 80 81 type UIManager struct { 82 Name string 83 HTTP string 84 Domain string 85 Corpus int 86 Added int 87 Deleted int 88 New int 89 Repros int 90 SentRepros int 91 RecvRepros int 92 } 93 94 var summaryTemplate = compileTemplate(` 95 <!doctype html> 96 <html> 97 <head> 98 <title>syz-hub</title> 99 {{STYLE}} 100 </head> 101 <body> 102 <b>syz-hub</b> 103 <br><br> 104 105 <table> 106 <caption>Managers:</caption> 107 <tr> 108 <th>Name</th> 109 <th>URL</th> 110 <th>Domain</th> 111 <th>Corpus</th> 112 <th>Added</th> 113 <th>Deleted</th> 114 <th>New</th> 115 <th>Repros</th> 116 <th>Sent</th> 117 <th>Recv</th> 118 </tr> 119 {{range $m := $.Managers}} 120 <tr> 121 <td>{{$m.Name}}</td> 122 <td><a href="{{$m.HTTP}}">{{$m.HTTP}}</a></td> 123 <td>{{$m.Domain}}</td> 124 <td>{{$m.Corpus}}</td> 125 <td>{{$m.Added}}</td> 126 <td>{{$m.Deleted}}</td> 127 <td>{{$m.New}}</td> 128 <td>{{$m.Repros}}</td> 129 <td>{{$m.SentRepros}}</td> 130 <td>{{$m.RecvRepros}}</td> 131 </tr> 132 {{end}} 133 </table> 134 <br><br> 135 136 Log: 137 <br> 138 <textarea id="log_textarea" readonly rows="50"> 139 {{.Log}} 140 </textarea> 141 <script> 142 var textarea = document.getElementById("log_textarea"); 143 textarea.scrollTop = textarea.scrollHeight; 144 </script> 145 146 </body></html> 147 `) 148 149 const htmlStyle = ` 150 <style type="text/css" media="screen"> 151 table { 152 border-collapse:collapse; 153 border:1px solid; 154 } 155 table caption { 156 font-weight: bold; 157 } 158 table td { 159 border:1px solid; 160 padding: 3px; 161 } 162 table th { 163 border:1px solid; 164 padding: 3px; 165 } 166 textarea { 167 width:100%; 168 } 169 </style> 170 `