Example #1
0
// TestLinuxSendfileChild isn't a real test. It's used as a helper process
// for TestLinuxSendfile.
func TestLinuxSendfileChild(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	defer os.Exit(0)
	fd3 := os.NewFile(3, "ephemeral-port-listener")
	ln, err := net.FileListener(fd3)
	if err != nil {
		panic(err)
	}
	mux := http.NewServeMux()

	fs := &FileServer{
		http.Dir("testdata"),
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
	}

	mux.Handle("/", fs)
	mux.HandleFunc("/quit", func(http.ResponseWriter, *http.Request) {
		os.Exit(0)
	})
	s := &http.Server{Handler: mux}
	err = s.Serve(ln)
	if err != nil {
		panic(err)
	}
}
Example #2
0
func TestServeIndexHtml(t *testing.T) {
	defer afterTest(t)
	const want = "index.html says hello\n"

	fs := &FileServer{
		http.Dir("."),
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
	}
	ts := httptest.NewServer(fs)
	defer ts.Close()

	for _, path := range []string{"/testdata/", "/testdata/index.html"} {
		res, err := http.Get(ts.URL + path)
		if err != nil {
			t.Fatal(err)
		}
		b, err := ioutil.ReadAll(res.Body)
		if err != nil {
			t.Fatal("reading Body:", err)
		}
		if s := string(b); s != want {
			t.Errorf("for path %q got %q, want %q", path, s, want)
		}
		res.Body.Close()
	}
}
Example #3
0
func TestFSRedirect(t *testing.T) {
	defer afterTest(t)
	ts := httptest.NewServer(
		http.StripPrefix(
			"/test",
			&FileServer{
				http.Dir("."),
				inject.CopyInject{},
				ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
			},
		),
	)
	defer ts.Close()

	for _, data := range fsRedirectTestData {
		res, err := http.Get(ts.URL + data.original)
		if err != nil {
			t.Fatal(err)
		}
		res.Body.Close()
		if g, e := res.Request.URL.Path, data.redirect; g != e {
			t.Errorf("redirect from %s: got %s, want %s", data.original, g, e)
		}
	}
}
Example #4
0
func TestFileServerCleans(t *testing.T) {
	defer afterTest(t)
	ch := make(chan string, 1)
	fs := &FileServer{
		&testFileSystem{
			func(name string) (http.File, error) {
				ch <- name
				return nil, errors.New("file does not exist")
			},
		},
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
	}
	tests := []struct {
		reqPath, openArg string
	}{
		{"/foo.txt", "/foo.txt"},
		{"//foo.txt", "/foo.txt"},
		{"/../foo.txt", "/foo.txt"},
	}
	req, _ := http.NewRequest("GET", "http://example.com", nil)
	for n, test := range tests {
		rec := httptest.NewRecorder()
		req.URL.Path = test.reqPath
		fs.ServeHTTP(rec, req)
		if got := <-ch; got != test.openArg {
			t.Errorf("test %d: got %q, want %q", n, got, test.openArg)
		}
	}
}
Example #5
0
func TestFileServerZeroByte(t *testing.T) {
	defer afterTest(t)
	fs := &FileServer{
		"version",
		http.Dir("."),
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
		[]routespec.RouteSpec{},
		"",
	}
	ts := httptest.NewServer(fs)
	defer ts.Close()

	res, err := http.Get(ts.URL + "/..\x00")
	if err != nil {
		t.Fatal(err)
	}
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatal("reading Body:", err)
	}
	if res.StatusCode == 200 {
		t.Errorf("got status 200; want an error. Body is:\n%s", string(b))
	}
}
Example #6
0
func TestDevdRouteHandler(t *testing.T) {
	logger := termlog.NewLog()
	logger.Quiet()
	r := Route{"", "/", fsEndpoint("./testdata")}
	templates := ricetemp.MustMakeTemplates(rice.MustFindBox("templates"))

	devd := Devd{
		Routes:      make([]string, 0, 0),
		OpenBrowser: false,
		CertFile:    "",
		Address:     "",
		Port:        0,

		// Shaping
		Latency:  0,
		DownKbps: 0,
		UpKbps:   0,

		// Livereload
		LivereloadRoutes: false,
		Watch:            make([]string, 0, 0),
		Excludes:         make([]string, 0, 0),

		// Logging
		IgnoreLogs: make([]string, 0, 0),
	}

	h := devd.RouteHandler(logger, r, templates, nil)
	ht := handlerTester{t, h}

	AssertCode(t, ht.Request("GET", "/", nil), 200)
}
Example #7
0
File: cli_test.go Project: npk/devd
func TestDevdHandler(t *testing.T) {
	logger := termlog.DummyLogger{}
	r := Route{"", "/", fsEndpoint("./testdata")}
	templates := ricetemp.MustMakeTemplates(rice.MustFindBox("templates"))
	h := devdHandler(&logger, r, templates, true, nil, true, 0)
	ht := handlerTester{t, h}

	AssertCode(t, ht.Request("GET", "/", nil), 200)
}
Example #8
0
// ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(w http.ResponseWriter, r *http.Request, name string) {
	dir, file := filepath.Split(name)
	logger := termlog.DummyLogger{}

	fs := FileServer{
		http.Dir(dir),
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
	}
	fs.serveFile(logger, w, r, file, false)
}
Example #9
0
func TestDevdRouteHandler(t *testing.T) {
	logger := termlog.NewLog()
	logger.Quiet()
	r := Route{"", "/", fsEndpoint("./testdata")}
	templates := ricetemp.MustMakeTemplates(rice.MustFindBox("templates"))

	devd := Devd{LivereloadRoutes: true}
	h := devd.RouteHandler(logger, r, templates)
	ht := handlerTester{t, h}

	AssertCode(t, ht.Request("GET", "/", nil), 200)
}
Example #10
0
// ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(w http.ResponseWriter, r *http.Request, name string) {
	dir, file := filepath.Split(name)
	logger := termlog.NewLog()
	logger.Quiet()

	fs := FileServer{
		"version",
		http.Dir(dir),
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
		[]routespec.RouteSpec{},
		"",
	}
	fs.serveFile(logger, w, r, file, false)
}
Example #11
0
func TestDevdHandler(t *testing.T) {
	logger := termlog.NewLog()
	logger.Quiet()
	templates := ricetemp.MustMakeTemplates(rice.MustFindBox("templates"))

	devd := Devd{LivereloadRoutes: true, WatchPaths: []string{"./"}}
	devd.AddRoutes([]string{"./"})
	h, err := devd.Handler(logger, templates)
	if err != nil {
		t.Error(err)
	}
	ht := handlerTester{t, h}

	AssertCode(t, ht.Request("GET", "/", nil), 200)
	AssertCode(t, ht.Request("GET", "/nonexistent", nil), 404)
}
Example #12
0
func TestFileServerImplicitLeadingSlash(t *testing.T) {
	defer afterTest(t)
	tempDir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatalf("TempDir: %v", err)
	}
	defer mustRemoveAll(tempDir)
	if err := ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("Hello world"), 0644); err != nil {
		t.Fatalf("WriteFile: %v", err)
	}
	fs := &FileServer{
		"version",
		http.Dir(tempDir),
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
		[]routespec.RouteSpec{},
		"",
	}

	ts := httptest.NewServer(http.StripPrefix("/bar/", fs))
	defer ts.Close()
	get := func(suffix string) string {
		res, err := http.Get(ts.URL + suffix)
		if err != nil {
			t.Fatalf("Get %s: %v", suffix, err)
		}
		b, err := ioutil.ReadAll(res.Body)
		if err != nil {
			t.Fatalf("ReadAll %s: %v", suffix, err)
		}
		_ = res.Body.Close()
		return string(b)
	}
	if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") {
		t.Logf("expected a directory listing with foo.txt, got %q", s)
	}
	if s := get("/bar/foo.txt"); s != "Hello world" {
		t.Logf("expected %q, got %q", "Hello world", s)
	}
}
Example #13
0
func TestDirectoryIfNotModified(t *testing.T) {
	defer afterTest(t)
	const indexContents = "I am a fake index.html file"
	fileMod := time.Unix(1000000000, 0).UTC()
	fileModStr := fileMod.Format(http.TimeFormat)
	dirMod := time.Unix(123, 0).UTC()
	indexFile := &fakeFileInfo{
		basename: "index.html",
		modtime:  fileMod,
		contents: indexContents,
	}
	fsys := fakeFS{
		"/": &fakeFileInfo{
			dir:     true,
			modtime: dirMod,
			ents:    []*fakeFileInfo{indexFile},
		},
		"/index.html": indexFile,
	}

	fs := &FileServer{
		fsys,
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
	}

	ts := httptest.NewServer(fs)
	defer ts.Close()

	res, err := http.Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatal(err)
	}
	if string(b) != indexContents {
		t.Fatalf("Got body %q; want %q", b, indexContents)
	}
	res.Body.Close()

	lastMod := res.Header.Get("Last-Modified")
	if lastMod != fileModStr {
		t.Fatalf("initial Last-Modified = %q; want %q", lastMod, fileModStr)
	}

	req, _ := http.NewRequest("GET", ts.URL, nil)
	req.Header.Set("If-Modified-Since", lastMod)

	res, err = http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal(err)
	}
	if res.StatusCode != 304 {
		t.Fatalf("Code after If-Modified-Since request = %v; want 304", res.StatusCode)
	}
	res.Body.Close()

	// Advance the index.html file's modtime, but not the directory's.
	indexFile.modtime = indexFile.modtime.Add(1 * time.Hour)

	res, err = http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal(err)
	}
	if res.StatusCode != 200 {
		t.Fatalf("Code after second If-Modified-Since request = %v; want 200; res is %#v", res.StatusCode, res)
	}
	res.Body.Close()
}
Example #14
0
func main() {
	httpIP := kingpin.Flag("address", "Address to listen on").
		Short('A').
		Default("127.0.0.1").
		String()

	allInterfaces := kingpin.Flag("a", "Listen on all addresses").
		Short('a').
		Bool()

	certFile := kingpin.Flag("cert", "Certificate bundle file - enables TLS").
		Short('c').
		PlaceHolder("PATH").
		Default("").
		ExistingFile()

	throttleDownKbps := kingpin.Flag(
		"down",
		"Throttle downstream from the client to N kilobytes per second",
	).
		PlaceHolder("N").
		Short('d').
		Default("0").
		Int()

	logHeaders := kingpin.Flag("logheaders", "Log headers").
		Short('H').
		Default("false").
		Bool()

	ignoreHeaders := kingpin.Flag(
		"ignore",
		"Disable logging matching requests. Regexes are matched over 'host/path'",
	).
		Short('I').
		PlaceHolder("REGEX").
		Strings()

	livereloadRoutes := kingpin.Flag("livereload", "Enable livereload for static files").
		Short('l').
		Default("false").
		Bool()

	latency := kingpin.Flag("latency", "Add N milliseconds of round-trip latency").
		PlaceHolder("N").
		Short('n').
		Default("0").
		Int()

	openBrowser := kingpin.Flag("open", "Open browser window on startup").
		Short('o').
		Default("false").
		Bool()

	httpPort := kingpin.Flag(
		"port",
		"Port to listen on - if not specified, devd will auto-pick a sensible port",
	).
		Short('p').
		Int()

	enableTimer := kingpin.Flag("logtime", "Log timing").
		Short('T').
		Default("false").
		Bool()

	throttleUpKbps := kingpin.Flag(
		"up",
		"Throttle upstream from the client to N kilobytes per second",
	).
		PlaceHolder("N").
		Short('u').
		Default("0").
		Int()

	watch := kingpin.Flag("watch", "Watch path to trigger livereload").
		PlaceHolder("PATH").
		Short('w').
		Strings()

	debug := kingpin.Flag("debug", "Debugging for devd development").
		Default("false").
		Bool()

	excludes := kingpin.Flag("exclude", "Glob pattern for files to exclude from livereload.").
		PlaceHolder("PATTERN").
		Short('x').
		Strings()

	routes := kingpin.Arg(
		"route",
		`Routes have the following forms:
			[SUBDOMAIN]/<PATH>=<DIR>
			[SUBDOMAIN]/<PATH>=<URL>
			<DIR>
			<URL>
		`,
	).Required().Strings()

	kingpin.Version(version)

	kingpin.Parse()

	logger := termlog.NewLog()

	if *debug {
		logger.Enable("debug")
	}
	if *enableTimer {
		logger.Enable("timer")
	}
	if *throttleDownKbps == 0 {
		*throttleDownKbps = slowdown.MaxRate
	}
	if *throttleUpKbps == 0 {
		*throttleUpKbps = slowdown.MaxRate
	}

	if *allInterfaces {
		*httpIP = "0.0.0.0"
	}

	tlsEnabled := false
	if *certFile != "" {
		tlsEnabled = true
	}

	var hl net.Listener
	var err error
	if *httpPort > 0 {
		hl, err = net.Listen("tcp", fmt.Sprintf("%v:%d", *httpIP, *httpPort))
	} else {
		hl, err = pickPort(*httpIP, portLow, portHigh, tlsEnabled)
	}
	if err != nil {
		kingpin.Fatalf("Could not bind to port: %s", err)
		return
	}

	templates := ricetemp.MustMakeTemplates(rice.MustFindBox("templates"))
	if err != nil {
		kingpin.Fatalf("Error loading templates: %s", err)
		return
	}

	ignores := make([]*regexp.Regexp, 0, 0)
	for _, expr := range *ignoreHeaders {
		v, err := regexp.Compile(expr)
		if err != nil {
			kingpin.Fatalf("%s", err)
		}
		ignores = append(ignores, v)
	}

	routeColl := make(routeCollection)
	for _, s := range *routes {
		err := routeColl.Set(s)
		if err != nil {
			kingpin.FatalUsage("Invalid route specification: %s", err)
		}
	}

	mux := http.NewServeMux()
	var livereloadEnabled = false
	if *livereloadRoutes || len(*watch) > 0 {
		livereloadEnabled = true
	}

	for match, route := range routeColl {
		handler := devdHandler(
			logger,
			route,
			templates,
			*logHeaders,
			ignores,
			livereloadEnabled,
			*latency,
		)
		mux.Handle(match, http.StripPrefix(route.Path, handler))
	}

	lr := livereload.NewServer("livereload", logger)
	if livereloadEnabled {
		mux.Handle("/livereload", lr)
		mux.Handle("/livereload.js", http.HandlerFunc(lr.ServeScript))
	}
	if *livereloadRoutes {
		err = WatchRoutes(routeColl, lr)
		if err != nil {
			kingpin.Fatalf("Could not watch routes for livereload: %s", err)
		}
	}
	if len(*watch) > 0 {
		err = WatchPaths(*watch, *excludes, lr, logger)
		if err != nil {
			kingpin.Fatalf("Could not watch path for livereload: %s", err)
		}
	}

	var tlsConfig *tls.Config
	if *certFile != "" {
		tlsConfig, err = getTLSConfig(*certFile)
		if err != nil {
			kingpin.Fatalf("Could not load certs: %s", err)
			return
		}
		hl = tls.NewListener(hl, tlsConfig)
	}
	hl = slowdown.NewSlowListener(
		hl,
		float64(*throttleUpKbps)*1024,
		float64(*throttleDownKbps)*1024,
	)

	url := formatURL(tlsEnabled, *httpIP, hl.Addr().(*net.TCPAddr).Port)
	logger.Say("Listening on %s (%s)", url, hl.Addr().String())
	if *openBrowser {
		go func() {
			webbrowser.Open(url)
		}()
	}
	server := &http.Server{
		Addr:    hl.Addr().String(),
		Handler: hostPortStrip(mux),
	}
	err = server.Serve(hl)
	logger.Shout("Server stopped: %v", err)
}
Example #15
0
func TestNotFoundOverride(t *testing.T) {
	defer afterTest(t)
	ffile := &fakeFileInfo{
		basename: "foo.html",
		modtime:  time.Unix(1000000000, 0).UTC(),
		contents: "I am a fake file",
	}
	fsys := fakeFS{
		"/": &fakeFileInfo{
			dir:     true,
			modtime: time.Unix(123, 0).UTC(),
			ents:    []*fakeFileInfo{},
		},
		"/one": &fakeFileInfo{
			dir:     true,
			modtime: time.Unix(123, 0).UTC(),
			ents:    []*fakeFileInfo{ffile},
		},
		"/one/foo.html": ffile,
	}

	fs := &FileServer{
		"version",
		fsys,
		inject.CopyInject{},
		ricetemp.MustMakeTemplates(rice.MustFindBox("../templates")),
		[]routespec.RouteSpec{
			{Host: "", Path: "/", Value: "foo.html"},
		},
		"",
	}

	ts := httptest.NewServer(fs)
	defer ts.Close()

	res, err := http.Get(ts.URL + "/one/nonexistent.html")
	if err != nil {
		t.Fatal(err)
	}
	_ = res.Body.Close()
	if res.StatusCode != 200 {
		t.Error("Expected to find over-ride file.")
	}

	res, err = http.Get(ts.URL + "/one/two/nonexistent.html")
	if err != nil {
		t.Fatal(err)
	}
	_ = res.Body.Close()
	if res.StatusCode != 200 {
		t.Error("Expected to find over-ride file.")
	}

	res, err = http.Get(ts.URL + "/nonexistent.html")
	if err != nil {
		t.Fatal(err)
	}
	_ = res.Body.Close()
	if res.StatusCode != 404 {
		t.Error("Expected to find over-ride file.")
	}

	res, err = http.Get(ts.URL + "/two/nonexistent.html")
	if err != nil {
		t.Fatal(err)
	}
	_ = res.Body.Close()
	if res.StatusCode != 404 {
		t.Error("Expected to find over-ride file.")
	}

}
Example #16
0
File: cli.go Project: npk/devd
// Serve starts the devd server
func (dd *Devd) Serve() error {
	logger := termlog.NewLog()
	if dd.Debug {
		logger.Enable("debug")
	}
	if dd.EnableTimer {
		logger.Enable("timer")
	}
	if dd.DownKbps == 0 {
		dd.DownKbps = slowdown.MaxRate
	}
	if dd.UpKbps == 0 {
		dd.UpKbps = slowdown.MaxRate
	}

	if dd.AllInterfaces {
		dd.Address = "0.0.0.0"
	}

	tlsEnabled := false
	if dd.CertFile != "" {
		tlsEnabled = true
	}

	var hl net.Listener
	var err error
	if dd.Port > 0 {
		hl, err = net.Listen("tcp", fmt.Sprintf("%v:%d", dd.Address, dd.Port))
	} else {
		hl, err = pickPort(dd.Address, portLow, portHigh, tlsEnabled)
	}
	if err != nil {
		return fmt.Errorf("Could not bind to port: %s", err)
	}

	templates := ricetemp.MustMakeTemplates(rice.MustFindBox("templates"))
	if err != nil {
		return fmt.Errorf("Error loading templates: %s", err)
	}

	ignores := make([]*regexp.Regexp, 0, 0)
	for _, expr := range dd.IgnoreLogs {
		v, err := regexp.Compile(expr)
		if err != nil {
			return fmt.Errorf("%s", err)
		}
		ignores = append(ignores, v)
	}

	routeColl := make(RouteCollection)
	for _, s := range dd.Routes {
		err := routeColl.Set(s)
		if err != nil {
			return fmt.Errorf("Invalid route specification: %s", err)
		}
	}

	mux := http.NewServeMux()
	var livereloadEnabled = false
	if dd.LivereloadRoutes || len(dd.Watch) > 0 {
		livereloadEnabled = true
	}

	for match, route := range routeColl {
		handler := devdHandler(
			logger,
			route,
			templates,
			dd.LogHeaders,
			ignores,
			livereloadEnabled,
			dd.Latency,
		)
		mux.Handle(match, http.StripPrefix(route.Path, handler))
	}

	lr := livereload.NewServer("livereload", logger)
	if livereloadEnabled {
		mux.Handle("/livereload", lr)
		mux.Handle("/livereload.js", http.HandlerFunc(lr.ServeScript))
	}
	if dd.LivereloadRoutes {
		err = WatchRoutes(routeColl, lr)
		if err != nil {
			return fmt.Errorf("Could not watch routes for livereload: %s", err)
		}
	}
	if len(dd.Watch) > 0 {
		err = WatchPaths(dd.Watch, dd.Excludes, lr, logger)
		if err != nil {
			return fmt.Errorf("Could not watch path for livereload: %s", err)
		}
	}

	var tlsConfig *tls.Config
	if dd.CertFile != "" {
		tlsConfig, err = getTLSConfig(dd.CertFile)
		if err != nil {
			return fmt.Errorf("Could not load certs: %s", err)
		}
		hl = tls.NewListener(hl, tlsConfig)
	}
	hl = slowdown.NewSlowListener(
		hl,
		float64(dd.UpKbps)*1024,
		float64(dd.DownKbps)*1024,
	)

	url := formatURL(tlsEnabled, dd.Address, hl.Addr().(*net.TCPAddr).Port)
	logger.Say("Listening on %s (%s)", url, hl.Addr().String())
	if dd.OpenBrowser {
		go func() {
			webbrowser.Open(url)
		}()
	}
	server := &http.Server{
		Addr:    hl.Addr().String(),
		Handler: hostPortStrip(mux),
	}
	err = server.Serve(hl)
	logger.Shout("Server stopped: %v", err)
	return nil
}