Example #1
0
func NewHandler(cfg HandlerConfig) (*Handler, error) {
	name := cfg.Program
	if cfg.Prefix == "" {
		return nil, fmt.Errorf("app: could not initialize Handler for %q: empty Prefix", name)
	}

	listen, backendURL, apiHost := cfg.Listen, cfg.BackendURL, cfg.APIHost
	var err error
	if listen == "" {
		if cfg.ServerListen == "" {
			return nil, fmt.Errorf(`app: could not initialize Handler for %q: neither "Listen" or "ServerListen" defined`, name)
		}
		listen, err = randListen(cfg.ServerListen)
		if err != nil {
			return nil, err
		}
	}
	if backendURL == "" {
		if cfg.ServerBaseURL == "" {
			return nil, fmt.Errorf(`app: could not initialize Handler for %q: neither "BackendURL" or "ServerBaseURL" defined`, name)
		}
		backendURL, err = baseURL(cfg.ServerBaseURL, listen)
		if err != nil {
			return nil, err
		}
	}
	if apiHost == "" {
		if cfg.ServerBaseURL == "" {
			return nil, fmt.Errorf(`app: could not initialize Handler for %q: neither "APIHost" or "ServerBaseURL" defined`, name)
		}
		apiHost = cfg.ServerBaseURL + "/"
	}

	proxyURL, err := url.Parse(backendURL)
	if err != nil {
		return nil, fmt.Errorf("could not parse backendURL %q: %v", backendURL, err)
	}

	username, password := auth.RandToken(20), auth.RandToken(20)
	camliAuth := username + ":" + password
	basicAuth := auth.NewBasicAuth(username, password)
	envVars := map[string]string{
		"CAMLI_API_HOST":   apiHost,
		"CAMLI_AUTH":       camliAuth,
		"CAMLI_APP_LISTEN": listen,
	}
	if cfg.AppConfig != nil {
		envVars["CAMLI_APP_CONFIG_URL"] = apiHost + strings.TrimPrefix(cfg.Prefix, "/") + "config.json"
	}

	return &Handler{
		name:       name,
		envVars:    envVars,
		auth:       basicAuth,
		appConfig:  cfg.AppConfig,
		prefix:     strings.TrimSuffix(cfg.Prefix, "/"),
		proxy:      httputil.NewSingleHostReverseProxy(proxyURL),
		backendURL: backendURL,
	}, nil
}
Example #2
0
// NewHandler returns a Handler that proxies requests to an app. Start() on the
// Handler starts the app.
// The apiHost must end in a slash and is the camlistored API server for the app
// process to hit.
// The appHandlerPrefix is the URL path prefix on apiHost where the app is mounted.
// It must end in a slash, and be at minimum "/".
// The conf object has the following members, related to the vars described in
// doc/app-environment.txt:
// "program", string, required. File name of the app's program executable. Either
// an absolute path, or the name of a file located in CAMLI_APP_BINDIR or in PATH.
// "backendURL", string, optional. Automatic if absent. It sets CAMLI_APP_BACKEND_URL.
// "appConfig", object, optional. Additional configuration that the app can request from Camlistore.
func NewHandler(conf jsonconfig.Obj, apiHost, appHandlerPrefix string) (*Handler, error) {
	// TODO: remove the appHandlerPrefix if/when we change where the app config JSON URL is made available.
	name := conf.RequiredString("program")
	backendURL := conf.OptionalString("backendURL", "")
	appConfig := conf.OptionalObject("appConfig")
	// TODO(mpl): add an auth token in the extra config of the dev server config,
	// that the hello app can use to setup a status handler than only responds
	// to requests with that token.
	if err := conf.Validate(); err != nil {
		return nil, err
	}

	if apiHost == "" {
		return nil, fmt.Errorf("app: could not initialize Handler for %q: Camlistore apiHost is unknown", name)
	}
	if appHandlerPrefix == "" {
		return nil, fmt.Errorf("app: could not initialize Handler for %q: empty appHandlerPrefix", name)
	}

	if backendURL == "" {
		var err error
		// If not specified in the conf, we're dynamically picking the port of the CAMLI_APP_BACKEND_URL
		// now (instead of letting the app itself do it), because we need to know it in advance in order
		// to set the app handler's proxy.
		backendURL, err = randPortBackendURL(apiHost, appHandlerPrefix)
		if err != nil {
			return nil, err
		}
	}

	username, password := auth.RandToken(20), auth.RandToken(20)
	camliAuth := username + ":" + password
	basicAuth := auth.NewBasicAuth(username, password)
	envVars := map[string]string{
		"CAMLI_API_HOST":        apiHost,
		"CAMLI_AUTH":            camliAuth,
		"CAMLI_APP_BACKEND_URL": backendURL,
	}
	if appConfig != nil {
		envVars["CAMLI_APP_CONFIG_URL"] = apiHost + strings.TrimPrefix(appHandlerPrefix, "/") + "config.json"
	}

	proxyURL, err := url.Parse(backendURL)
	if err != nil {
		return nil, fmt.Errorf("could not parse backendURL %q: %v", backendURL, err)
	}
	return &Handler{
		name:       name,
		envVars:    envVars,
		auth:       basicAuth,
		appConfig:  appConfig,
		proxy:      httputil.NewSingleHostReverseProxy(proxyURL),
		backendURL: backendURL,
	}, nil
}
Example #3
0
func newCookie() *http.Cookie {
	expiration := cookieExpiration
	return &http.Cookie{
		Name:    "user",
		Value:   auth.RandToken(15),
		Expires: time.Now().Add(expiration),
	}
}
Example #4
0
func newCookie() *http.Cookie {
	expiration := cookieExpiration
	if DevHandler {
		expiration = 2 * time.Minute
	}
	return &http.Cookie{
		Name:    "user",
		Value:   auth.RandToken(15),
		Expires: time.Now().Add(expiration),
	}
}
Example #5
0
// NewDeployHandler initializes a DeployHandler that serves at https://host/prefix/ and returns it.
// A Google account client ID should be set in CAMLI_GCE_CLIENTID with its corresponding client
// secret in CAMLI_GCE_CLIENTSECRET.
func NewDeployHandler(host, prefix string) (http.Handler, error) {
	clientID := os.Getenv("CAMLI_GCE_CLIENTID")
	if clientID == "" {
		return nil, errors.New("Need an oauth2 client ID defined in CAMLI_GCE_CLIENTID")
	}
	clientSecret := os.Getenv("CAMLI_GCE_CLIENTSECRET")
	if clientSecret == "" {
		return nil, errors.New("Need an oauth2 client secret defined in CAMLI_GCE_CLIENTSECRET")
	}
	tpl, err := template.New("root").Parse(noTheme + tplHTML)
	if err != nil {
		return nil, fmt.Errorf("could not parse template: %v", err)
	}
	host = strings.TrimSuffix(host, "/")
	prefix = strings.TrimSuffix(prefix, "/")
	scheme := "https://"
	if DevHandler {
		scheme = "http://"
	}
	xsrfKey := os.Getenv("CAMLI_GCE_XSRFKEY")
	if xsrfKey == "" {
		xsrfKey = auth.RandToken(20)
		log.Printf("xsrf key not provided as env var CAMLI_GCE_XSRFKEY, so generating one instead: %v", xsrfKey)
	}
	instConf, instState, err := dataStores()
	if err != nil {
		return nil, fmt.Errorf("could not initialize conf or state storage: %v", err)
	}
	h := &DeployHandler{
		debug:          DevHandler,
		host:           host,
		xsrfKey:        xsrfKey,
		instConf:       instConf,
		instState:      instState,
		recordStateErr: make(map[string]error),
		scheme:         scheme,
		prefix:         prefix,
		help: map[string]template.HTML{
			"createProject":   template.HTML(googURLPattern.ReplaceAllString(HelpCreateProject, toHyperlink)),
			"enableAPIs":      template.HTML(HelpEnableAPIs),
			"genCert":         template.HTML(helpGenCert),
			"domainName":      template.HTML(helpDomainName),
			"machineTypes":    template.HTML(helpMachineTypes),
			"zones":           template.HTML(helpZones),
			"ssh":             template.HTML(helpSSH),
			"changeCert":      template.HTML(helpChangeCert),
			"changeSSH":       template.HTML(HelpManageSSHKeys),
			"changeHTTPCreds": template.HTML(HelpManageHTTPCreds),
		},
		clientID:     clientID,
		clientSecret: clientSecret,
		tpl:          tpl,
		piggyGIF:     "/static/piggy.gif",
	}
	mux := http.NewServeMux()
	mux.HandleFunc(prefix+"/callback", func(w http.ResponseWriter, r *http.Request) {
		h.serveCallback(w, r)
	})
	mux.HandleFunc(prefix+"/instance", func(w http.ResponseWriter, r *http.Request) {
		h.serveInstanceState(w, r)
	})
	mux.HandleFunc(prefix+"/", func(w http.ResponseWriter, r *http.Request) {
		h.serveRoot(w, r)
	})
	h.mux = mux
	h.Logger = log.New(os.Stderr, "", log.LstdFlags)
	h.zones = backupZones
	// TODO(mpl): use time.AfterFunc and avoid having a goroutine running all the time almost
	// doing nothing.
	refreshZonesFn := func() {
		for {
			if err := h.refreshZones(); err != nil {
				h.Printf("error while refreshing zones: %v", err)
			}
			time.Sleep(24 * time.Hour)
		}
	}
	go refreshZonesFn()
	return h, nil
}