diff --git a/pkg/server/publish.go b/pkg/server/publish.go index db7105307..8dfe01d1e 100644 --- a/pkg/server/publish.go +++ b/pkg/server/publish.go @@ -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("

Invalid or unsupported resource request.

") diff --git a/pkg/server/ui.go b/pkg/server/ui.go index 20adcee78..b5d7ba378 100644 --- a/pkg/server/ui.go +++ b/pkg/server/ui.go @@ -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 } diff --git a/pkg/serverconfig/genconfig.go b/pkg/serverconfig/genconfig.go index 14b8bbdff..cd56e2aab 100644 --- a/pkg/serverconfig/genconfig.go +++ b/pkg/serverconfig/genconfig.go @@ -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) }