Merge "publish: find ui and closure resources similarly to ui handler"

This commit is contained in:
Brad Fitzpatrick 2013-06-29 19:34:36 +00:00 committed by Gerrit Code Review
commit 8b4ebd59b7
3 changed files with 64 additions and 30 deletions

View File

@ -36,6 +36,7 @@ import (
"camlistore.org/pkg/blobref"
"camlistore.org/pkg/blobserver"
"camlistore.org/pkg/client" // just for NewUploadHandleFromString. move elsewhere?
"camlistore.org/pkg/fileembed"
"camlistore.org/pkg/httputil"
"camlistore.org/pkg/jsonconfig"
"camlistore.org/pkg/jsonsign/signhandler"
@ -56,8 +57,17 @@ type PublishHandler struct {
JSFiles, CSSFiles []string
handlerFinder blobserver.FindHandlerByTyper
staticHandler http.Handler
handlerFinder blobserver.FindHandlerByTyper
// sourceRoot optionally specifies the path to root of Camlistore's
// source. If empty, the UI files must be compiled in to the
// binary (with go run make.go). This comes from the "sourceRoot"
// publish handler config option.
sourceRoot string
uiDir string // if sourceRoot != "", this is sourceRoot+"/server/camlistored/ui"
// closureHandler serves the Closure JS files.
closureHandler http.Handler
}
@ -78,6 +88,7 @@ func newPublishFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Han
scType := conf.OptionalString("scaledImage", "")
bootstrapSignRoot := conf.OptionalString("devBootstrapPermanodeUsing", "")
rootNode := conf.OptionalList("rootPermanode")
ph.sourceRoot = conf.OptionalString("sourceRoot", "")
if err = conf.Validate(); err != nil {
return
}
@ -148,20 +159,32 @@ func newPublishFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Han
}
}
camliRootPath, err := osutil.GoPackagePath("camlistore.org")
if err != nil {
error := "Package camlistore.org not found in $GOPATH (or $GOPATH not defined)." +
" Needed to find closure dir."
return nil, fmt.Errorf(error)
if ph.sourceRoot == "" {
ph.sourceRoot = os.Getenv("CAMLI_DEV_CAMLI_ROOT")
}
if ph.sourceRoot != "" {
ph.uiDir = filepath.Join(ph.sourceRoot, filepath.FromSlash("server/camlistored/ui"))
// Ignore any fileembed files:
Files = &fileembed.Files{
DirFallback: filepath.Join(ph.sourceRoot, filepath.FromSlash("pkg/server")),
}
uistatic.Files = &fileembed.Files{
DirFallback: ph.uiDir,
}
}
closureDir := filepath.Join(camliRootPath, "tmp", "closure-lib", "closure")
ph.closureHandler = http.FileServer(http.Dir(closureDir))
ph.staticHandler = http.FileServer(uistatic.Files)
ph.closureHandler, err = ph.makeClosureHandler(ph.sourceRoot)
if err != nil {
return nil, fmt.Errorf(`Invalid "sourceRoot" value of %q: %v"`, ph.sourceRoot, err)
}
return ph, nil
}
func (ph *PublishHandler) makeClosureHandler(root string) (http.Handler, error) {
return makeClosureHandler(root, "publish")
}
func (ph *PublishHandler) rootPermanode() (*blobref.BlobRef, error) {
// TODO: caching, but this can change over time (though
// probably rare). might be worth a 5 second cache or
@ -384,25 +407,26 @@ func (pr *publishRequest) serveHTTP() {
pr.serveSubresImage()
case "s": // static
pr.req.URL.Path = pr.subres[len("/=s"):]
if len(pr.req.URL.Path) > 1 {
if m := closurePattern.FindStringSubmatch(pr.req.URL.Path[1:]); m != nil {
pr.req.URL.Path = "/" + m[1]
pr.ph.closureHandler.ServeHTTP(pr.rw, pr.req)
return
}
if len(pr.req.URL.Path) <= 1 {
http.Error(pr.rw, "Illegal URL.", http.StatusNotFound)
return
}
file := pr.req.URL.Path[1:]
if m := closurePattern.FindStringSubmatch(file); m != nil {
pr.req.URL.Path = "/" + m[1]
pr.ph.closureHandler.ServeHTTP(pr.rw, pr.req)
return
}
// TODO: this assumes that deps.js either dev server, or that deps.js
// is embedded in the binary. We want to NOT embed deps.js, but also
// serve dynamic deps.js from other resources embedded in the server
// when not in dev-server mode. So fix this later, when serveDepsJS
// can work over embedded resources.
if pr.req.URL.Path == "/deps.js" {
if dir := os.Getenv("CAMLI_DEV_CAMLI_ROOT"); dir != "" {
serveDepsJS(pr.rw, pr.req, dir+"/server/camlistored/ui")
return
}
if file == "deps.js" && pr.ph.sourceRoot != "" {
serveDepsJS(pr.rw, pr.req, pr.ph.uiDir)
return
}
pr.ph.staticHandler.ServeHTTP(pr.rw, pr.req)
serveStaticFile(pr.rw, pr.req, uistatic.Files, file)
default:
pr.rw.WriteHeader(400)
pr.pf("<p>Invalid or unsupported resource request.</p>")

View File

@ -188,6 +188,10 @@ func uiFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, er
return ui, nil
}
func (ui *UIHandler) makeClosureHandler(root string) (http.Handler, error) {
return makeClosureHandler(root, "ui")
}
// makeClosureHandler returns a handler to serve Closure files.
// root is either:
// 1) empty: use the Closure files compiled in to the binary (if
@ -195,26 +199,26 @@ func uiFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, er
// 2) a URL prefix: base of Camlistore to get Closure to redirect to
// 3) a path on disk to the root of camlistore's source (which
// contains the necessary subset of Closure files)
func (ui *UIHandler) makeClosureHandler(root string) (http.Handler, error) {
func makeClosureHandler(root, handlerName string) (http.Handler, error) {
// dev-server environment variable takes precendence:
if d := os.Getenv("CAMLI_DEV_CLOSURE_DIR"); d != "" {
log.Printf("ui: serving Closure from dev-server's $CAMLI_DEV_CLOSURE_DIR: %v", d)
log.Printf("%v: serving Closure from dev-server's $CAMLI_DEV_CLOSURE_DIR: %v", handlerName, d)
return http.FileServer(http.Dir(d)), nil
}
if root == "" {
fs, err := closurestatic.FileSystem()
if err == os.ErrNotExist {
log.Printf("ui: no configured setting or embedded resources; serving Closure via %v", closureBaseURL)
log.Printf("%v: no configured setting or embedded resources; serving Closure via %v", handlerName, closureBaseURL)
return closureBaseURL, nil
}
if err != nil {
return nil, fmt.Errorf("error loading embedded Closure zip file: %v", err)
}
log.Printf("ui: serving Closure from embedded resources")
log.Printf("%v: serving Closure from embedded resources", handlerName)
return http.FileServer(fs), nil
}
if strings.HasPrefix(root, "http") {
log.Printf("ui: serving Closure using redirects to %v", root)
log.Printf("%v: serving Closure using redirects to %v", handlerName, root)
return closureRedirector(root), nil
}
fi, err := os.Stat(root)
@ -229,7 +233,7 @@ func (ui *UIHandler) makeClosureHandler(root string) (http.Handler, error) {
if err != nil {
return nil, fmt.Errorf("directory doesn't contain closure/goog/base.js; wrong directory?")
}
log.Printf("ui: serving Closure from disk: %v", closureRoot)
log.Printf("%v: serving Closure from disk: %v", handlerName, closureRoot)
return http.FileServer(http.Dir(closureRoot)), nil
}

View File

@ -46,7 +46,9 @@ type configPrefixesParams struct {
var tempDir = os.TempDir
func addPublishedConfig(prefixes jsonconfig.Obj, published jsonconfig.Obj) ([]interface{}, error) {
func addPublishedConfig(prefixes jsonconfig.Obj,
published jsonconfig.Obj,
sourceRoot string) ([]interface{}, error) {
pubPrefixes := []interface{}{}
for k, v := range published {
p, ok := v.(map[string]interface{})
@ -83,6 +85,9 @@ func addPublishedConfig(prefixes jsonconfig.Obj, published jsonconfig.Obj) ([]in
"cache": "/cache/",
"rootPermanode": []interface{}{"/sighelper/", rootPermanode},
}
if sourceRoot != "" {
handlerArgs["sourceRoot"] = sourceRoot
}
switch template {
case "gallery":
if style == "" {
@ -384,6 +389,7 @@ func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
// If non empty, the ui files will be expected at
// sourceRoot + "/server/camlistored/ui" and the closure library at
// sourceRoot + "/third_party/closure/lib"
// Also used by the publish handler.
sourceRoot = conf.OptionalString("sourceRoot", "")
ownerName = conf.OptionalString("ownerName", "")
@ -490,7 +496,7 @@ func genLowLevelConfig(conf *Config) (lowLevelConf *Config, err error) {
if !runIndex {
return nil, fmt.Errorf("publishing requires an index")
}
published, err = addPublishedConfig(prefixes, publish)
published, err = addPublishedConfig(prefixes, publish, sourceRoot)
if err != nil {
return nil, fmt.Errorf("Could not generate config for published: %v", err)
}