func TestHttpServer(t *testing.T) { bytes, err := ioutil.ReadFile("../../test/stories/binder_stories.js") if err != nil { t.Errorf("Read file error: %v", err) return } var scont binderStoriesContainer if err := json.Unmarshal(bytes, &scont); err != nil { t.Errorf("Story parse error: %v", err) return } logger, stats := loggerAndStats() auth, storage := authAndStore(logger, stats) curator, err := curator.New(curator.NewConfig(), logger, stats, auth, storage) if err != nil { t.Errorf("Curator error: %v", err) } ts := httptest.NewServer(websocket.Handler( WebsocketHandler(curator, time.Second, logger, stats), )) defer ts.Close() time.Sleep(50 * time.Millisecond) wsURL := strings.Replace(ts.URL, "http", "ws", 1) origin := "http://127.0.0.1/" url := wsURL + "/leaps/socket" for _, story := range scont.Stories { ws, err := websocket.Dial(url, "", origin) if err != nil { t.Errorf("client connect error: %v", err) return } websocket.JSON.Send(ws, leapHTTPClientMessage{ Command: "create", UserID: "test", Document: &store.Document{ Content: story.Content, }, }) var initResponse leapHTTPServerMessage if err := websocket.JSON.Receive(ws, &initResponse); err != nil { t.Errorf("Init receive error: %v", err) return } switch initResponse.Type { case "document": case "error": t.Errorf("Server returned error: %v", initResponse.Error) return default: t.Errorf("unexpected message type from server init: %v", initResponse.Type) return } feeds := make(chan text.OTransform) wg := sync.WaitGroup{} wg.Add(10) go senderClient(initResponse.Document.ID, url, feeds, t) for j := 0; j < 10; j++ { go goodStoryClient(initResponse.Document.ID, url, &story, &wg, t) } time.Sleep(50 * time.Millisecond) for j := 0; j < len(story.Transforms); j++ { feeds <- story.Transforms[j] } wg.Wait() } curator.Close() }
func main() { var ( err error closeChan = make(chan bool) ) flag.Usage = func() { fmt.Println(`Usage: leaps [flags...] [path/to/share] If a path is not specified the current directory is shared instead. `) flag.PrintDefaults() } flag.Parse() targetPath := "." if flag.NArg() == 1 { targetPath = flag.Arg(0) } // Logging and metrics aggregation logConf := log.NewLoggerConfig() logConf.Prefix = "leaps" logConf.LogLevel = *logLevel logger := log.NewLogger(os.Stdout, logConf) statConf := metrics.NewConfig() statConf.Prefix = "leaps" stats, err := metrics.New(statConf) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Metrics init error: %v\n", err)) return } defer stats.Close() logger.Infoln("Launching a leaps instance, use CTRL+C to close.") // Document storage engine docStore, err := store.NewFile(".") if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Document store error: %v\n", err)) return } // Authenticator storeConf := acl.NewFileExistsConfig() storeConf.Path = targetPath storeConf.ShowHidden = *showHidden authenticator := acl.NewFileExists(storeConf, logger) // Curator of documents curatorConf := curator.NewConfig() curator, err := curator.New(curatorConf, logger, stats, authenticator, docStore) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Curator error: %v\n", err)) return } defer curator.Close() handle("/endpoints", "Lists all available endpoints (including this one).", func(w http.ResponseWriter, r *http.Request) { data, reqErr := json.Marshal(endpoints) if reqErr != nil { logger.Errorf("Failed to serve endpoints: %v\n", reqErr) http.Error(w, reqErr.Error(), http.StatusInternalServerError) return } w.Header().Add("Content-Type", "application/json") w.Write(data) }) handle("/files", "Returns a list of available files and a map of users per document.", func(w http.ResponseWriter, r *http.Request) { var reqErr error var users map[string][]string if users, reqErr = curator.GetUsers(time.Second); reqErr == nil { var data []byte data, reqErr = json.Marshal(struct { Paths []string `json:"paths"` Users map[string][]string `json:"users"` }{ Paths: authenticator.GetPaths(), Users: users, }) if reqErr == nil { w.Write(data) } } if reqErr != nil { http.Error(w, reqErr.Error(), http.StatusInternalServerError) logger.Errorf("Failed to serve users: %v\n", reqErr) return } w.Header().Add("Content-Type", "application/json") }) handle("/stats", "Lists all aggregated metrics as a json blob.", stats.JSONHandler()) wwwPath := gopath.Join("/", *subdirPath) stripPath := "" if wwwPath != "/" { wwwPath = wwwPath + "/" stripPath = wwwPath } if len(*debugWWWDir) > 0 { logger.Warnf("Serving web files from alternative www dir: %v\n", *debugWWWDir) http.Handle(wwwPath, http.StripPrefix(stripPath, http.FileServer(http.Dir(*debugWWWDir)))) } else { http.Handle(wwwPath, http.StripPrefix(stripPath, http.FileServer(assetFS()))) } http.Handle(gopath.Join("/", *subdirPath, "/leaps/ws"), websocket.Handler(leaphttp.WebsocketHandler(curator, time.Second, logger, stats))) go func() { logger.Infof("Serving HTTP requests at: %v%v\n", *httpAddress, *subdirPath) if httperr := http.ListenAndServe(*httpAddress, nil); httperr != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("HTTP listen error: %v\n", httperr)) } closeChan <- true }() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) // Wait for termination signal select { case <-sigChan: case <-closeChan: } }