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/blobref"
"camlistore.org/pkg/blobserver" "camlistore.org/pkg/blobserver"
"camlistore.org/pkg/client" // just for NewUploadHandleFromString. move elsewhere? "camlistore.org/pkg/client" // just for NewUploadHandleFromString. move elsewhere?
"camlistore.org/pkg/fileembed"
"camlistore.org/pkg/httputil" "camlistore.org/pkg/httputil"
"camlistore.org/pkg/jsonconfig" "camlistore.org/pkg/jsonconfig"
"camlistore.org/pkg/jsonsign/signhandler" "camlistore.org/pkg/jsonsign/signhandler"
@ -56,8 +57,17 @@ type PublishHandler struct {
JSFiles, CSSFiles []string JSFiles, CSSFiles []string
handlerFinder blobserver.FindHandlerByTyper handlerFinder blobserver.FindHandlerByTyper
staticHandler http.Handler
// 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 closureHandler http.Handler
} }
@ -78,6 +88,7 @@ func newPublishFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Han
scType := conf.OptionalString("scaledImage", "") scType := conf.OptionalString("scaledImage", "")
bootstrapSignRoot := conf.OptionalString("devBootstrapPermanodeUsing", "") bootstrapSignRoot := conf.OptionalString("devBootstrapPermanodeUsing", "")
rootNode := conf.OptionalList("rootPermanode") rootNode := conf.OptionalList("rootPermanode")
ph.sourceRoot = conf.OptionalString("sourceRoot", "")
if err = conf.Validate(); err != nil { if err = conf.Validate(); err != nil {
return return
} }
@ -148,20 +159,32 @@ func newPublishFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Han
} }
} }
camliRootPath, err := osutil.GoPackagePath("camlistore.org") if ph.sourceRoot == "" {
if err != nil { ph.sourceRoot = os.Getenv("CAMLI_DEV_CAMLI_ROOT")
error := "Package camlistore.org not found in $GOPATH (or $GOPATH not defined)." + }
" Needed to find closure dir." if ph.sourceRoot != "" {
return nil, fmt.Errorf(error) 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 return ph, nil
} }
func (ph *PublishHandler) makeClosureHandler(root string) (http.Handler, error) {
return makeClosureHandler(root, "publish")
}
func (ph *PublishHandler) rootPermanode() (*blobref.BlobRef, error) { func (ph *PublishHandler) rootPermanode() (*blobref.BlobRef, error) {
// TODO: caching, but this can change over time (though // TODO: caching, but this can change over time (though
// probably rare). might be worth a 5 second cache or // probably rare). might be worth a 5 second cache or
@ -384,25 +407,26 @@ func (pr *publishRequest) serveHTTP() {
pr.serveSubresImage() pr.serveSubresImage()
case "s": // static case "s": // static
pr.req.URL.Path = pr.subres[len("/=s"):] pr.req.URL.Path = pr.subres[len("/=s"):]
if len(pr.req.URL.Path) > 1 { if len(pr.req.URL.Path) <= 1 {
if m := closurePattern.FindStringSubmatch(pr.req.URL.Path[1:]); m != nil { http.Error(pr.rw, "Illegal URL.", http.StatusNotFound)
pr.req.URL.Path = "/" + m[1] return
pr.ph.closureHandler.ServeHTTP(pr.rw, pr.req) }
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 // 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 // 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 // serve dynamic deps.js from other resources embedded in the server
// when not in dev-server mode. So fix this later, when serveDepsJS // when not in dev-server mode. So fix this later, when serveDepsJS
// can work over embedded resources. // can work over embedded resources.
if pr.req.URL.Path == "/deps.js" { if file == "deps.js" && pr.ph.sourceRoot != "" {
if dir := os.Getenv("CAMLI_DEV_CAMLI_ROOT"); dir != "" { serveDepsJS(pr.rw, pr.req, pr.ph.uiDir)
serveDepsJS(pr.rw, pr.req, dir+"/server/camlistored/ui") return
return
}
} }
pr.ph.staticHandler.ServeHTTP(pr.rw, pr.req) serveStaticFile(pr.rw, pr.req, uistatic.Files, file)
default: default:
pr.rw.WriteHeader(400) pr.rw.WriteHeader(400)
pr.pf("<p>Invalid or unsupported resource request.</p>") 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 return ui, nil
} }
func (ui *UIHandler) makeClosureHandler(root string) (http.Handler, error) {
return makeClosureHandler(root, "ui")
}
// makeClosureHandler returns a handler to serve Closure files. // makeClosureHandler returns a handler to serve Closure files.
// root is either: // root is either:
// 1) empty: use the Closure files compiled in to the binary (if // 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 // 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 // 3) a path on disk to the root of camlistore's source (which
// contains the necessary subset of Closure files) // 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: // dev-server environment variable takes precendence:
if d := os.Getenv("CAMLI_DEV_CLOSURE_DIR"); d != "" { 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 return http.FileServer(http.Dir(d)), nil
} }
if root == "" { if root == "" {
fs, err := closurestatic.FileSystem() fs, err := closurestatic.FileSystem()
if err == os.ErrNotExist { 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 return closureBaseURL, nil
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("error loading embedded Closure zip file: %v", err) 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 return http.FileServer(fs), nil
} }
if strings.HasPrefix(root, "http") { 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 return closureRedirector(root), nil
} }
fi, err := os.Stat(root) fi, err := os.Stat(root)
@ -229,7 +233,7 @@ func (ui *UIHandler) makeClosureHandler(root string) (http.Handler, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("directory doesn't contain closure/goog/base.js; wrong directory?") 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 return http.FileServer(http.Dir(closureRoot)), nil
} }

View File

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