github.com/metacurrency/holochain@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/cmd/bs/bs.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 holo "github.com/holochain/holochain-proto" 8 "github.com/op/go-logging" 9 "github.com/tidwall/buntdb" 10 "github.com/urfave/cli" 11 "net/http" 12 "os" 13 "os/user" 14 "strings" 15 "time" 16 ) 17 18 const ( 19 DefaultPort = 3142 20 ) 21 22 var log = logging.MustGetLogger("main") 23 24 var store *buntdb.DB 25 26 func setupDB(dbpath string) (err error) { 27 if dbpath == "" { 28 dbpath = os.Getenv("HOLOBSPATH") 29 if dbpath == "" { 30 u, err := user.Current() 31 if err != nil { 32 return err 33 } 34 userPath := u.HomeDir 35 dbpath = userPath + "/.hcbootstrapdb" 36 } 37 } 38 store, err = buntdb.Open(dbpath) 39 if err != nil { 40 panic(err) 41 } 42 store.CreateIndex("chain", "*", buntdb.IndexJSON("HID")) 43 return 44 } 45 46 func setupApp() (app *cli.App) { 47 app = cli.NewApp() 48 app.Name = "bs" 49 app.Usage = "holochain bootstrap server" 50 app.Version = "0.0.2" 51 52 var port int 53 var dbpath string 54 55 app.Flags = []cli.Flag{ 56 cli.IntFlag{ 57 Name: "port", 58 Usage: "bootstrap server port", 59 Value: DefaultPort, 60 Destination: &port, 61 }, 62 } 63 64 app.Before = func(c *cli.Context) error { 65 66 log.Infof("app version: %s; Holochain bootstrap server", app.Version) 67 68 err := setupDB(dbpath) 69 return err 70 } 71 72 app.Action = func(c *cli.Context) error { 73 return serve(port) 74 } 75 return 76 } 77 78 func main() { 79 app := setupApp() 80 app.Run(os.Args) 81 } 82 83 type Node struct { 84 Req holo.BSReq 85 Remote string 86 HID string 87 LastSeen time.Time 88 } 89 90 func get(chain string) (result string, err error) { 91 err = store.View(func(tx *buntdb.Tx) error { 92 nodes := make([]holo.BSResp, 0) 93 //hid := fmt.Sprintf(`{"HID":"%s"}`, chain) 94 95 now := time.Now() 96 tx.Ascend("chain", func(key, value string) bool { 97 var nd Node 98 json.Unmarshal([]byte(value), &nd) 99 if nd.HID == chain { 100 if nd.LastSeen.Add(holo.BootstrapTTL).After(now) { 101 log.Infof("Found: %s=>%s", key, value) 102 resp := holo.BSResp{Req: nd.Req, Remote: nd.Remote, LastSeen: nd.LastSeen} 103 nodes = append(nodes, resp) 104 } 105 } 106 return true 107 }) 108 var b []byte 109 b, err = json.Marshal(nodes) 110 if err == nil { 111 result = string(b) 112 } 113 return err 114 }) 115 return 116 } 117 118 func post(chain string, req *holo.BSReq, remote string, seen time.Time) (err error) { 119 err = store.Update(func(tx *buntdb.Tx) error { 120 var b []byte 121 n := Node{Remote: remote, Req: *req, HID: chain, LastSeen: seen} 122 b, err = json.Marshal(n) 123 if err == nil { 124 key := chain + ":" + req.NodeID 125 _, _, err = tx.Set(key, string(b), nil) 126 if err == nil { 127 log.Infof("Set: %s", string(b)) 128 } 129 } 130 return err 131 }) 132 return 133 } 134 135 func h(w http.ResponseWriter, r *http.Request) { 136 var err error 137 log.Infof("%s: processing req:%s\n", r.Method, r.URL.Path) 138 path := strings.Split(r.URL.Path, "/") 139 if r.Method == "GET" { 140 if len(path) != 2 { 141 http.Error(w, "expecting path /<holochainid>", 400) 142 return 143 } 144 chain := string(path[1]) 145 var result string 146 result, err = get(chain) 147 if err == nil { 148 fmt.Fprintf(w, result) 149 } 150 } else if r.Method == "POST" { 151 if len(path) != 3 { 152 http.Error(w, "expecting path /<holochainid>/<peerid>", 400) 153 return 154 } 155 chain := string(path[1]) 156 node := string(path[2]) 157 158 var req holo.BSReq 159 if r.Body == nil { 160 err = errors.New("Please send a request body") 161 } 162 if err == nil { 163 err = json.NewDecoder(r.Body).Decode(&req) 164 if err == nil { 165 if req.NodeID != node { 166 err = errors.New("id in post URL doesn't match Req") 167 } else { 168 err = post(chain, &req, r.RemoteAddr, time.Now()) 169 if err == nil { 170 fmt.Fprintf(w, "ok") 171 } 172 } 173 } 174 } 175 } 176 if err != nil { 177 log.Infof("Error:%s", err.Error()) 178 http.Error(w, err.Error(), 400) 179 } else { 180 log.Infof("Success") 181 } 182 } 183 184 func getCompleteConnectionList(response http.ResponseWriter, request *http.Request) { 185 var err error 186 err = store.View(func(tx *buntdb.Tx) error { 187 nodes := make([]holo.BSResp, 0) 188 //hid := fmt.Sprintf(`{"HID":"%s"}`, chain) 189 190 tx.Ascend("chain", func(key, value string) bool { 191 var node Node 192 json.Unmarshal([]byte(value), &node) 193 194 log.Infof("Found: %s=>%s", key, value) 195 resp := holo.BSResp{Req: node.Req, Remote: node.Remote} 196 197 nodes = append(nodes, resp) 198 199 return true 200 }) 201 var b []byte 202 b, err = json.Marshal(nodes) 203 if err == nil { 204 fmt.Fprintf(response, string(b)) 205 fmt.Fprintf(response, string("oK")) 206 } 207 208 return err 209 }) 210 } 211 212 func serve(port int) (err error) { 213 214 mux := http.NewServeMux() 215 216 mux.HandleFunc("/", h) 217 mux.HandleFunc("/getCompleteConnectionList", getCompleteConnectionList) 218 219 log.Infof("starting up on port %d", port) 220 221 s := &http.Server{ 222 Addr: fmt.Sprintf(":%d", port), // set listen port 223 Handler: mux, 224 ReadTimeout: 10 * time.Second, 225 WriteTimeout: 10 * time.Second, 226 MaxHeaderBytes: 1 << 20, 227 } 228 err = s.ListenAndServe() 229 return 230 }