func Start(dir string, port int, tlsCrt, tlsKey, jwtPubKey, jwtPrivateKey string) { var err error HttpDB, err = db.OpenDB(dir) if err != nil { panic(err) } // These endpoints are always available and do not require JWT auth http.HandleFunc("/", Welcome) http.HandleFunc("/version", Version) http.HandleFunc("/memstats", MemStats) if jwtPrivateKey != "" { // JWT support ServeJWTEnabledEndpoints(jwtPubKey, jwtPrivateKey) } else { // No JWT ServeEndpoints() } if tlsCrt != "" { tdlog.Noticef("Will listen on all interfaces (HTTPS), port %d.", port) if err := http.ListenAndServeTLS(fmt.Sprintf(":%d", port), tlsCrt, tlsKey, nil); err != nil { tdlog.Panicf("Failed to start HTTPS service - %s", err) } } else { tdlog.Noticef("Will listen on all interfaces (HTTP), port %d.", port) http.ListenAndServe(fmt.Sprintf(":%d", port), nil) } }
func ServeJWTEnabledEndpoints(jwtPubKey, jwtPrivateKey string) { var e error if publicKey, e = ioutil.ReadFile(jwtPubKey); e != nil { tdlog.Panicf("JWT: Failed to read public key file - %s", e) } else if privateKey, e = ioutil.ReadFile(jwtPrivateKey); e != nil { tdlog.Panicf("JWT: Failed to read private key file - %s", e) } jwtInitSetup() // collection management (stop-the-world) http.HandleFunc("/create", jwtWrap(Create)) http.HandleFunc("/rename", jwtWrap(Rename)) http.HandleFunc("/drop", jwtWrap(Drop)) http.HandleFunc("/all", jwtWrap(All)) http.HandleFunc("/scrub", jwtWrap(Scrub)) http.HandleFunc("/sync", jwtWrap(Sync)) // query http.HandleFunc("/query", jwtWrap(Query)) http.HandleFunc("/count", jwtWrap(Count)) // document management http.HandleFunc("/insert", jwtWrap(Insert)) http.HandleFunc("/get", jwtWrap(Get)) http.HandleFunc("/getpage", jwtWrap(GetPage)) http.HandleFunc("/update", jwtWrap(Update)) http.HandleFunc("/delete", jwtWrap(Delete)) http.HandleFunc("/approxdoccount", jwtWrap(ApproxDocCount)) // index management (stop-the-world) http.HandleFunc("/index", jwtWrap(Index)) http.HandleFunc("/indexes", jwtWrap(Indexes)) http.HandleFunc("/unindex", jwtWrap(Unindex)) // misc http.HandleFunc("/shutdown", jwtWrap(Shutdown)) http.HandleFunc("/dump", jwtWrap(Dump)) // does not require JWT auth http.HandleFunc("/getjwt", getJWT) http.HandleFunc("/checkjwt", checkJWT) tdlog.Noticef("JWT is enabled. API endpoints require JWT authorization.") }
// If necessary, create the JWT identity collection, indexes, and the default/special user identity "admin". func jwtInitSetup() { // Create collection if HttpDB.Use(JWT_COL_NAME) == nil { if err := HttpDB.Create(JWT_COL_NAME); err != nil { tdlog.Panicf("JWT: failed to create JWT identity collection - %v", err) } } jwtCol := HttpDB.Use(JWT_COL_NAME) // Create indexes on ID attribute indexPaths := make(map[string]struct{}) for _, oneIndex := range jwtCol.AllIndexes() { indexPaths[strings.Join(oneIndex, db.INDEX_PATH_SEP)] = struct{}{} } if _, exists := indexPaths[JWT_USER_ATTR]; !exists { if err := jwtCol.Index([]string{JWT_USER_ATTR}); err != nil { tdlog.Panicf("JWT: failed to create collection index - %v", err) } } // Create default user "admin" adminQuery := map[string]interface{}{ "eq": JWT_USER_ADMIN, "in": []interface{}{JWT_USER_ATTR}} adminQueryResult := make(map[int]struct{}) if err := db.EvalQuery(adminQuery, jwtCol, &adminQueryResult); err != nil { tdlog.Panicf("JWT: failed to query admin user ID - %v", err) } if len(adminQueryResult) == 0 { if _, err := jwtCol.Insert(map[string]interface{}{ JWT_USER_ATTR: JWT_USER_ADMIN, JWT_PASS_ATTR: "", JWT_COLLECTIONS_ATTR: []interface{}{}, JWT_ENDPOINTS_ATTR: []interface{}{}}); err != nil { tdlog.Panicf("JWT: failed to create default admin user - %v", err) } tdlog.Notice("JWT: successfully initialized DB for JWT features. The default user 'admin' has been created.") } }
func main() { var err error var defaultMaxprocs int if defaultMaxprocs, err = strconv.Atoi(os.Getenv("GOMAXPROCS")); err != nil { defaultMaxprocs = runtime.NumCPU() } // Parse CLI parameters var mode, dir string var port, maxprocs int var profile bool flag.StringVar(&mode, "mode", "", "[httpd|bench|bench2|example]") flag.StringVar(&dir, "dir", "", "(HTTP API) database directory") flag.IntVar(&port, "port", 8080, "(HTTP API) port number") flag.StringVar(&webcp.WebCp, "webcp", "admin", "(HTTP API) web control panel route (without leading slash)") flag.IntVar(&maxprocs, "gomaxprocs", defaultMaxprocs, "GOMAXPROCS") flag.IntVar(&benchSize, "benchsize", 400000, "Benchmark sample size") flag.BoolVar(&profile, "profile", false, "Write profiler results to prof.out") flag.BoolVar(&tdlog.VerboseLog, "verbose", false, "Turn verbose logging on/off") flag.BoolVar(&benchCleanup, "benchcleanup", true, "Whether to clean up (delete benchmark DB) after benchmark") flag.Parse() // User must specify a mode to run if mode == "" { flag.PrintDefaults() return } // Set appropriate GOMAXPROCS runtime.GOMAXPROCS(maxprocs) tdlog.Noticef("GOMAXPROCS is set to %d", maxprocs) if maxprocs < runtime.NumCPU() { tdlog.Noticef("GOMAXPROCS (%d) is less than number of CPUs (%d), this may reduce performance. You can change it via environment variable GOMAXPROCS or by passing CLI parameter -gomaxprocs", maxprocs, runtime.NumCPU()) } // Start profiler if enabled if profile { resultFile, err := os.Create("perf.out") if err != nil { log.Panicf("Cannot create profiler result file %s", resultFile) } pprof.StartCPUProfile(resultFile) defer pprof.StopCPUProfile() } switch mode { case "httpd": // Run HTTP API server if dir == "" { tdlog.Panicf("Please specify database directory, for example -dir=/tmp/db") } if port == 0 { tdlog.Panicf("Please specify port number, for example -port=8080") } db, err := db.OpenDB(dir) if err != nil { panic(err) } httpapi.Start(db, port) case "example": // Run embedded usage examples embeddedExample() case "bench": // Benchmark scenarios benchmark() case "bench2": benchmark2() default: flag.PrintDefaults() return } }
// Start HTTP server and block until the server shuts down. Panic on error. func Start(dir string, port int, tlsCrt, tlsKey, jwtPubKey, jwtPrivateKey, bind, authToken string) { var err error HttpDB, err = db.OpenDB(dir) if err != nil { panic(err) } // These endpoints are always available and do not require authentication http.HandleFunc("/", Welcome) http.HandleFunc("/version", Version) http.HandleFunc("/memstats", MemStats) // Install API endpoint handlers that may require authorization var authWrap func(http.HandlerFunc) http.HandlerFunc if authToken != "" { tdlog.Noticef("API endpoints now require the pre-shared token in Authorization header.") authWrap = func(originalHandler http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if "token "+authToken != r.Header.Get("Authorization") { http.Error(w, "", http.StatusUnauthorized) return } originalHandler(w, r) } } } else if jwtPubKey != "" && jwtPrivateKey != "" { tdlog.Noticef("API endpoints now require JWT in Authorization header.") var publicKeyContent, privateKeyContent []byte if publicKeyContent, err = ioutil.ReadFile(jwtPubKey); err != nil { panic(err) } else if publicKey, err = jwt.ParseRSAPublicKeyFromPEM(publicKeyContent); err != nil { panic(err) } else if privateKeyContent, err = ioutil.ReadFile(jwtPrivateKey); err != nil { panic(err) } else if privateKey, err = jwt.ParseRSAPrivateKeyFromPEM(privateKeyContent); err != nil { panic(err) } jwtInitSetup() authWrap = jwtWrap // does not require JWT auth http.HandleFunc("/getjwt", getJWT) http.HandleFunc("/checkjwt", checkJWT) } else { tdlog.Noticef("API endpoints do not require Authorization header.") authWrap = func(originalHandler http.HandlerFunc) http.HandlerFunc { return originalHandler } } // collection management (stop-the-world) http.HandleFunc("/create", authWrap(Create)) http.HandleFunc("/rename", authWrap(Rename)) http.HandleFunc("/drop", authWrap(Drop)) http.HandleFunc("/all", authWrap(All)) http.HandleFunc("/scrub", authWrap(Scrub)) http.HandleFunc("/sync", authWrap(Sync)) // query http.HandleFunc("/query", authWrap(Query)) http.HandleFunc("/count", authWrap(Count)) // document management http.HandleFunc("/insert", authWrap(Insert)) http.HandleFunc("/get", authWrap(Get)) http.HandleFunc("/getpage", authWrap(GetPage)) http.HandleFunc("/update", authWrap(Update)) http.HandleFunc("/delete", authWrap(Delete)) http.HandleFunc("/approxdoccount", authWrap(ApproxDocCount)) // index management (stop-the-world) http.HandleFunc("/index", authWrap(Index)) http.HandleFunc("/indexes", authWrap(Indexes)) http.HandleFunc("/unindex", authWrap(Unindex)) // misc (stop-the-world) http.HandleFunc("/shutdown", authWrap(Shutdown)) http.HandleFunc("/dump", authWrap(Dump)) iface := "all interfaces" if bind != "" { iface = bind } if tlsCrt != "" { tdlog.Noticef("Will listen on %s (HTTPS), port %d.", iface, port) if err := http.ListenAndServeTLS(fmt.Sprintf("%s:%d", bind, port), tlsCrt, tlsKey, nil); err != nil { tdlog.Panicf("Failed to start HTTPS service - %s", err) } } else { tdlog.Noticef("Will listen on %s (HTTP), port %d.", iface, port) http.ListenAndServe(fmt.Sprintf("%s:%d", bind, port), nil) } }
func main() { rand.Seed(time.Now().UTC().UnixNano()) var err error var defaultMaxprocs int if defaultMaxprocs, err = strconv.Atoi(os.Getenv("GOMAXPROCS")); err != nil { defaultMaxprocs = runtime.NumCPU() * 2 } // Parse CLI parameters var mode, dir string var port, maxprocs, benchSize int var profile bool flag.StringVar(&mode, "mode", "", "http|bench|bench2|bench3|example]") flag.StringVar(&dir, "dir", "", "database directory") flag.IntVar(&port, "port", 0, "listening port number") flag.IntVar(&maxprocs, "gomaxprocs", defaultMaxprocs, "GOMAXPROCS") flag.IntVar(&benchSize, "benchsize", 400000, "Benchmark sample size") flag.BoolVar(&profile, "profile", false, "write profiler results to prof.out") flag.BoolVar(&tdlog.VerboseLog, "verbose", true, "verbose logging true/false (default is true)") flag.Parse() // User must specify a mode to run if mode == "" { flag.PrintDefaults() return } // Setup appropriate GOMAXPROCS parameter runtime.GOMAXPROCS(maxprocs) tdlog.Printf("GOMAXPROCS is set to %d", maxprocs) if maxprocs < runtime.NumCPU() { tdlog.Printf("GOMAXPROCS (%d) is less than number of CPUs (%d), this may affect performance. You can change it via environment variable GOMAXPROCS or by passing CLI parameter -gomaxprocs", maxprocs, runtime.NumCPU()) } // Start profiler if enabled if profile { resultFile, err := os.Create("perf.out") if err != nil { tdlog.Panicf("Cannot create profiler result file %s", resultFile) } pprof.StartCPUProfile(resultFile) defer pprof.StopCPUProfile() } switch mode { case "http": // Run HTTP service (API V3) if dir == "" { tdlog.Fatal("Please specify database directory, for example -dir=/tmp/db") } if port == 0 { tdlog.Fatal("Please specify port number, for example -port=8080") } db, err := db.OpenDB(dir) if err != nil { tdlog.Fatal(err) } v3.Start(db, port) case "bench": // Benchmark scenarios benchmark(benchSize) case "bench2": benchmark2(benchSize) case "bench3": benchmark3(benchSize) case "example": // Embedded usage example embeddedExample() default: flag.PrintDefaults() return } }