111fe5a4eSEben Haberpackage main
211fe5a4eSEben Haber
311fe5a4eSEben Haberimport (
4d6e1afeaSEben M Haber	"flag"
511fe5a4eSEben Haber	"fmt"
60c4f9a53SEben Haber	"io/ioutil"
711fe5a4eSEben Haber	"net/http"
811fe5a4eSEben Haber	"net/http/httputil"
911fe5a4eSEben Haber	"net/url"
1011fe5a4eSEben Haber	"os"
1111fe5a4eSEben Haber	"strings"
1211fe5a4eSEben Haber
1311fe5a4eSEben Haber	"github.com/couchbase/go-couchbase"
1411fe5a4eSEben Haber	json "github.com/dustin/gojson"
1511fe5a4eSEben Haber)
1611fe5a4eSEben Haber
1711fe5a4eSEben Haberconst TEST_URL = "http://localhost:8091/"
1811fe5a4eSEben Haber
1996a7649fSEben Habervar DATASTORE = flag.String("datastore", "", "Datastore URL (e.g., http://localhost:8091)")
2011fe5a4eSEben Habervar USER = flag.String("user", "", "Authorized user login for Couchbase")
2111fe5a4eSEben Habervar PASS = flag.String("pass", "", "Authorized user password for Couchbase")
2296a7649fSEben Haber
2396a7649fSEben Habervar QUERYENGINE = flag.String("queryEngine", "", "Query URL (e.g., http://localhost:8093)")
2496a7649fSEben Habervar QUERY_PREFIX = flag.String("queryPrefix", "/query", "prefix for URL of query service, e.g. '/query' or '/analytics'")
2596a7649fSEben Haber
2611fe5a4eSEben Habervar LOCALPORT = flag.String("localPort", ":8095", "Port on which the UI runs. E.g., :8095")
275adca9c7SEben Habervar WEBCONTENT = flag.String("webcontent", "./", "Location of static folder containing web content. E.g., ./static")
2811fe5a4eSEben Habervar VERBOSE = flag.Bool("verbose", false, "Produce verbose output about program operation.")
2911fe5a4eSEben Haber
3011fe5a4eSEben Haberfunc main() {
3111fe5a4eSEben Haber	flag.Parse()
3296a7649fSEben Haber	launchWebService()
3311fe5a4eSEben Haber}
3411fe5a4eSEben Haber
3511fe5a4eSEben Haber//
3611fe5a4eSEben Haber// Given a CB Server URL, ask it what ports KV and Query are running on
3711fe5a4eSEben Haber//
3811fe5a4eSEben Haber
3911fe5a4eSEben Haberfunc getClusterQueryKVsAndMgmt(server, user, pass string) ([]string, []string, []string) {
4011fe5a4eSEben Haber	n1ql := make([]string, 0, 0)
4111fe5a4eSEben Haber	kvs := make([]string, 0, 0)
4211fe5a4eSEben Haber	mgmt := make([]string, 0, 0)
4311fe5a4eSEben Haber
4411fe5a4eSEben Haber	var client couchbase.Client
4511fe5a4eSEben Haber	var err error
4611fe5a4eSEben Haber
4711fe5a4eSEben Haber	if len(user) == 0 {
4811fe5a4eSEben Haber		client, err = couchbase.Connect(server)
4911fe5a4eSEben Haber	} else {
5096a7649fSEben Haber		fmt.Printf("Connecting with user: %v pass: %v\n", user, pass)
5111fe5a4eSEben Haber		client, err = couchbase.ConnectWithAuthCreds(server, user, pass)
5211fe5a4eSEben Haber	}
5311fe5a4eSEben Haber
5411fe5a4eSEben Haber	if err != nil {
5511fe5a4eSEben Haber		fmt.Printf("Error with connection to %v: %v\n", server, err)
5611fe5a4eSEben Haber		return n1ql, kvs, mgmt
5711fe5a4eSEben Haber	}
5811fe5a4eSEben Haber
5911fe5a4eSEben Haber	// ...then get the bucket-independent services...
6011fe5a4eSEben Haber	services, err := client.GetPoolServices("default")
6111fe5a4eSEben Haber	if err != nil {
6211fe5a4eSEben Haber		fmt.Printf("Error with pool services: %v\n", err)
6311fe5a4eSEben Haber		return n1ql, kvs, mgmt
6411fe5a4eSEben Haber	}
6511fe5a4eSEben Haber
6611fe5a4eSEben Haber	// ...which then get us to the node-independent services
6711fe5a4eSEben Haber	for _, nodeService := range services.NodesExt {
6811fe5a4eSEben Haber
6911fe5a4eSEben Haber		// the host name is either specified, or the boolean flag "ThisNode"
7011fe5a4eSEben Haber		// indicates that it is the same as the node we connected to
7111fe5a4eSEben Haber		host_name := nodeService.Hostname
7211fe5a4eSEben Haber		if nodeService.ThisNode {
7311fe5a4eSEben Haber			host_name = client.BaseURL.String()
7411fe5a4eSEben Haber		}
7511fe5a4eSEben Haber		// remove any port number from the host name
7611fe5a4eSEben Haber		portIdx := strings.LastIndex(host_name, ":")
7711fe5a4eSEben Haber		if portIdx > 0 {
7811fe5a4eSEben Haber			host_name = host_name[0:portIdx]
7911fe5a4eSEben Haber		}
8011fe5a4eSEben Haber
8111fe5a4eSEben Haber		// remove any http:// or https://
8211fe5a4eSEben Haber		if strings.HasPrefix(host_name, "http://") {
8311fe5a4eSEben Haber			host_name = host_name[7:]
8411fe5a4eSEben Haber		}
8511fe5a4eSEben Haber		if strings.HasPrefix(host_name, "https://") {
8611fe5a4eSEben Haber			host_name = host_name[8:]
8711fe5a4eSEben Haber		}
8811fe5a4eSEben Haber
8911fe5a4eSEben Haber		log(fmt.Sprintf("\nHost name is: %v", host_name))
9011fe5a4eSEben Haber
9111fe5a4eSEben Haber		// now iterate through theh services looking for "kv" and "n1ql"
9211fe5a4eSEben Haber		for k, v := range nodeService.Services {
9311fe5a4eSEben Haber			log(fmt.Sprintf("  Got service %v as %v", k, v))
9411fe5a4eSEben Haber
9511fe5a4eSEben Haber			if strings.EqualFold(k, "n1ql") {
9611fe5a4eSEben Haber				n1ql = append(n1ql, fmt.Sprintf("%s:%d", host_name, v))
9711fe5a4eSEben Haber			}
9811fe5a4eSEben Haber			if strings.EqualFold(k, "mgmt") {
9911fe5a4eSEben Haber				mgmt = append(mgmt, fmt.Sprintf("%s:%d", host_name, v))
10011fe5a4eSEben Haber			}
1010b8f1844SEben Haber			if strings.EqualFold(k, "kv") {
10211fe5a4eSEben Haber				kvs = append(kvs, fmt.Sprintf("%s:%d", host_name, v))
10311fe5a4eSEben Haber			}
10411fe5a4eSEben Haber		}
10511fe5a4eSEben Haber	}
10611fe5a4eSEben Haber
10711fe5a4eSEben Haber	return n1ql, kvs, mgmt
10811fe5a4eSEben Haber}
10911fe5a4eSEben Haber
11011fe5a4eSEben Haber//
11111fe5a4eSEben Haber// our web service has two jobs: it needs to serve up static web content for the
11211fe5a4eSEben Haber// query UI, using a normal file server.
11311fe5a4eSEben Haber//
11411fe5a4eSEben Haber// it *also* needs to proxy the queries from the UI, intercepting 'describe'
11511fe5a4eSEben Haber// statements and running them here.
11611fe5a4eSEben Haber//
11711fe5a4eSEben Haber
11811fe5a4eSEben Habervar serverHost string
11911fe5a4eSEben Habervar queryHost string
12011fe5a4eSEben Habervar serverUrl, serverUser, serverPass string
1210c4f9a53SEben Haber
1220c4f9a53SEben Haber//var kvs []string
12311fe5a4eSEben Haber
12496a7649fSEben Haberfunc launchWebService() {
12596a7649fSEben Haber	fmt.Printf("Launching query web service.\n")
126d6e1afeaSEben M Haber
1270c4f9a53SEben Haber	//
1280c4f9a53SEben Haber	// error checking
1290c4f9a53SEben Haber	//
130d6e1afeaSEben M Haber
1310c4f9a53SEben Haber	if len(*WEBCONTENT) == 0 {
132d6e1afeaSEben M Haber		fmt.Printf("Unable to start proxy, -webcontent was not specified.")
133d6e1afeaSEben M Haber		return
1340c4f9a53SEben Haber	}
135d6e1afeaSEben M Haber
1360c4f9a53SEben Haber	// if we were not given a URL for the QUERYENGINE,
13796a7649fSEben Haber	// the datastore allows us to get /pools, find out where every service is
13896a7649fSEben Haber	if len(*DATASTORE) > 0 {
13996a7649fSEben Haber		fmt.Printf("    Using CB Server at: %s\n", *DATASTORE)
14011fe5a4eSEben Haber
141d6e1afeaSEben M Haber		serverUrl = strings.TrimRight(*DATASTORE, "/")
14296a7649fSEben Haber		serverUser = *USER
14396a7649fSEben Haber		serverPass = *PASS
14496a7649fSEben Haber
14596a7649fSEben Haber		if strings.HasPrefix(strings.ToLower(*DATASTORE), "http://") {
14696a7649fSEben Haber			serverHost = serverUrl[7:]
14796a7649fSEben Haber		} else {
14896a7649fSEben Haber			serverHost = serverUrl
14996a7649fSEben Haber		}
15011fe5a4eSEben Haber
15196a7649fSEben Haber		//
1520c4f9a53SEben Haber		// if we were not given a query engine, use /pools to get a query and
1530c4f9a53SEben Haber		// kv service that we'll need
15496a7649fSEben Haber		//
15511fe5a4eSEben Haber
1560c4f9a53SEben Haber		if QUERYENGINE == nil || len(*QUERYENGINE) == 0 {
1570c4f9a53SEben Haber			var n1ql []string
1580c4f9a53SEben Haber			var mgmt []string
1590c4f9a53SEben Haber			n1ql, _, mgmt = getClusterQueryKVsAndMgmt(*DATASTORE, *USER, *PASS)
1600c4f9a53SEben Haber
1610c4f9a53SEben Haber			if len(n1ql) == 0 {
1620c4f9a53SEben Haber				fmt.Printf("Unable to find a N1QL query service on: %s\n", *DATASTORE)
1630c4f9a53SEben Haber				return
1640c4f9a53SEben Haber			} else {
1650c4f9a53SEben Haber				fmt.Printf("    Using N1QL query service on: %s\n", n1ql[0])
1660c4f9a53SEben Haber				//queryURL = fmt.Sprintf("http://%s/query/service", n1ql[0])
1670c4f9a53SEben Haber				queryHost = n1ql[0]
1680c4f9a53SEben Haber			}
16911fe5a4eSEben Haber
1700c4f9a53SEben Haber			if len(mgmt) == 0 {
1710c4f9a53SEben Haber				fmt.Printf("Unable to find the mgmt query service on: %s\n", *DATASTORE)
1720c4f9a53SEben Haber				return
1730c4f9a53SEben Haber			} else {
1740c4f9a53SEben Haber				fmt.Printf("    Using mgmt query service on: %s\n", serverHost)
1750c4f9a53SEben Haber			}
17696a7649fSEben Haber		}
17796a7649fSEben Haber
1780c4f9a53SEben Haber	}
17996a7649fSEben Haber
1800c4f9a53SEben Haber	//
1810c4f9a53SEben Haber	// if they specified a QUERYENGINE, remember what they specified
1820c4f9a53SEben Haber
1830c4f9a53SEben Haber	if QUERYENGINE != nil && len(*QUERYENGINE) > 0 { // if no DATASTORE, there must be a QUERYENGINE
184046869d9SEben Haber
185046869d9SEben Haber		queryHost = *QUERYENGINE
186046869d9SEben Haber
187046869d9SEben Haber		if strings.HasPrefix(queryHost, "http://") {
18896a7649fSEben Haber			queryHost = queryHost[7:]
18996a7649fSEben Haber		}
190046869d9SEben Haber
191046869d9SEben Haber		fmt.Printf("    Using query service on: %s\n", queryHost)
192046869d9SEben Haber
1930c4f9a53SEben Haber	}
1940c4f9a53SEben Haber
1950c4f9a53SEben Haber	if len(serverHost) == 0 && len(queryHost) == 0 { // let the user know that they need either DATASTORE or QUERYENGINE
1960c4f9a53SEben Haber		fmt.Printf("Error: you must specify -datastore=<couchbase URL> or -queryEngine=<query engine URL>\n")
197046869d9SEben Haber		os.Exit(1)
19811fe5a4eSEben Haber	}
19911fe5a4eSEben Haber
20011fe5a4eSEben Haber	//
20196a7649fSEben Haber	// make a set of proxies for query engine, management server, and image content
20211fe5a4eSEben Haber	//
20311fe5a4eSEben Haber
20411fe5a4eSEben Haber	staticPath := strings.Join([]string{*WEBCONTENT, "query-ui"}, "")
20511fe5a4eSEben Haber
20696a7649fSEben Haber	fmt.Printf("    Using -webcontent=%s, web content path at: %s\n", *WEBCONTENT, staticPath)
20711fe5a4eSEben Haber
20811fe5a4eSEben Haber	_, err := os.Stat(staticPath)
20911fe5a4eSEben Haber	if err != nil {
21011fe5a4eSEben Haber		if os.IsNotExist(err) {
21111fe5a4eSEben Haber			fmt.Printf(" Can't find static path: %v\n", err)
21211fe5a4eSEben Haber		} else {
21311fe5a4eSEben Haber			fmt.Printf(" Error with static path: %v\n", err)
21411fe5a4eSEben Haber		}
21511fe5a4eSEben Haber		os.Exit(1)
21611fe5a4eSEben Haber	}
21711fe5a4eSEben Haber
21896a7649fSEben Haber	// create proxies to query and mgmt ports
21996a7649fSEben Haber	queryProxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: queryHost})
22096a7649fSEben Haber	queryProxy.Director = toQuery
22111fe5a4eSEben Haber
2224868f70dSEben Haber	imageProxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http"})
2234868f70dSEben Haber	imageProxy.Director = toImage
2244868f70dSEben Haber
225046869d9SEben Haber	if len(serverHost) > 0 {
226046869d9SEben Haber		mgmtProxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: serverHost})
227046869d9SEben Haber		mgmtProxy.Director = toMgmt
228d6e1afeaSEben M Haber		mgmtProxy.ModifyResponse = fromMgmt
229046869d9SEben Haber		http.Handle("/pools", mgmtProxy)
230d6e1afeaSEben M Haber		http.Handle("/pools/default", mgmtProxy)
231d6e1afeaSEben M Haber		http.Handle("/pools/default/buckets", mgmtProxy)
232d6e1afeaSEben M Haber		http.Handle("/pools/default/checkPermissions", mgmtProxy)
233d6e1afeaSEben M Haber		//		http.Handle("/uilogin", mgmtProxy)
234d6e1afeaSEben M Haber		//		http.Handle("/uilogout", mgmtProxy)
235d6e1afeaSEben M Haber		//		http.Handle("/whoami", mgmtProxy)
236046869d9SEben Haber	}
23711fe5a4eSEben Haber
23811fe5a4eSEben Haber	// handle queries at the service prefix
2390d609c4dSEben Haber	http.Handle("../_p/query/", queryProxy)
2404868f70dSEben Haber	http.Handle("/_p/query/", queryProxy)
2414868f70dSEben Haber	http.Handle("/analytics/service/", queryProxy)
2424868f70dSEben Haber
2430d609c4dSEben Haber	http.Handle("../_p/ui/query/", imageProxy)
2444868f70dSEben Haber	http.Handle("/_p/ui/query/", imageProxy)
24511fe5a4eSEben Haber
24611fe5a4eSEben Haber	// Handle static endpoint for serving the web content
24711fe5a4eSEben Haber	http.Handle("/", http.FileServer(http.Dir(staticPath)))
24811fe5a4eSEben Haber
24911fe5a4eSEben Haber	fmt.Printf("Launching UI server, to use, point browser at http://localhost%s\n", *LOCALPORT)
25011fe5a4eSEben Haber
25111fe5a4eSEben Haber	listenAndServe(*LOCALPORT)
25211fe5a4eSEben Haber}
25311fe5a4eSEben Haber
254046869d9SEben Habertype Credential struct {
255046869d9SEben Haber	User string `json:"user"`
256046869d9SEben Haber	Pass string `json:"pass"`
257046869d9SEben Haber}
2580c4f9a53SEben Haber
259046869d9SEben Habertype QueryParams struct {
2600c4f9a53SEben Haber	Statement       string       `json:"statement"`
2610c4f9a53SEben Haber	ClientContextId string       `json:"client_context_id"`
2620c4f9a53SEben Haber	Creds           []Credential `json:"creds"`
2630c4f9a53SEben Haber}
264046869d9SEben Haber
26511fe5a4eSEben Haberfunc toQuery(r *http.Request) {
26696a7649fSEben Haber	r.Host = queryHost
26796a7649fSEben Haber	r.URL.Host = r.Host
26896a7649fSEben Haber	r.URL.Scheme = "http"
2690c4f9a53SEben Haber
270046869d9SEben Haber	// rewrite the URL to the approriate proxied value
2714868f70dSEben Haber	//fmt.Printf("Got query path: %s %s\n  form: %v\n", r.Host,r.URL.Path, r.Form)
27296a7649fSEben Haber	if strings.HasPrefix(strings.ToLower(r.URL.Path), "/_p/query/query") {
27396a7649fSEben Haber		r.URL.Path = *QUERY_PREFIX + r.URL.Path[15:]
2744868f70dSEben Haber		//fmt.Printf("  new host %v path: %s\n", r.Host,r.URL.Path)
2754868f70dSEben Haber	}
2760d609c4dSEben Haber	if strings.HasPrefix(strings.ToLower(r.URL.Path), "../_p/query/query") {
2770d609c4dSEben Haber		r.URL.Path = *QUERY_PREFIX + r.URL.Path[17:]
2780d609c4dSEben Haber		//fmt.Printf("  new host %v path: %s\n", r.Host,r.URL.Path)
2790d609c4dSEben Haber	}
2800c4f9a53SEben Haber
2810c4f9a53SEben Haber	//fmt.Printf(" Got query request encoding: %v\n", r.Header.Get("Content-Type"))
2820c4f9a53SEben Haber
283046869d9SEben Haber	// if we were given a USER/PASS, we need to insert them into the creds array
284046869d9SEben Haber	// to give queries those privileges
2850c4f9a53SEben Haber	if USER != nil && len(*USER) > 0 && PASS != nil && len(*PASS) > 0 {
2860c4f9a53SEben Haber		r.SetBasicAuth(*USER, *PASS)
2870c4f9a53SEben Haber		// create a credentials array with USER and PASS
2880c4f9a53SEben Haber		var credentials []Credential = make([]Credential, 0, 0)
2890c4f9a53SEben Haber		newCred := new(Credential)
2900c4f9a53SEben Haber		newCred.User = *USER
2910c4f9a53SEben Haber		newCred.Pass = *PASS
2920c4f9a53SEben Haber		credentials = append(credentials, *newCred)
2930c4f9a53SEben Haber
2940c4f9a53SEben Haber		//
2950c4f9a53SEben Haber		// do we have JSON params or URL encoded params?
2960c4f9a53SEben Haber		//
2970c4f9a53SEben Haber
2980c4f9a53SEben Haber		if strings.EqualFold(r.Header.Get("Content-Type"), "application/json") {
299d6e1afeaSEben M Haber			queryParams := new(QueryParams)
3000c4f9a53SEben Haber			err := json.NewDecoder(r.Body).Decode(&queryParams)
3010c4f9a53SEben Haber			if err != nil {
3020c4f9a53SEben Haber				log(fmt.Sprintf("Error decoding JSON body: %v\n", err))
3030c4f9a53SEben Haber				return
3040c4f9a53SEben Haber			} else {
3050c4f9a53SEben Haber				//log(fmt.Sprintf("Got statement: %s\n", queryParams.Statement))
3060c4f9a53SEben Haber				//log(fmt.Sprintf("Got client context: %s\n", queryParams.ClientContextId))
3070c4f9a53SEben Haber				//log(fmt.Sprintf("Got creds: %v\n", queryParams.Creds))
3080c4f9a53SEben Haber			}
3090c4f9a53SEben Haber
310d6e1afeaSEben M Haber			queryParams.Creds = append(queryParams.Creds, credentials...)
3110c4f9a53SEben Haber
3120c4f9a53SEben Haber			newParamBytes, cerr := json.Marshal(queryParams)
3130c4f9a53SEben Haber			//log(fmt.Sprintf("Got new params: %s\n", string(newParamBytes)))
3140c4f9a53SEben Haber			if cerr != nil {
3150c4f9a53SEben Haber				log(fmt.Sprintf("Error marshalling new credentials %v\n", cerr))
3160c4f9a53SEben Haber			} else {
3170c4f9a53SEben Haber				newBody := string(newParamBytes)
3180c4f9a53SEben Haber				r.Body = ioutil.NopCloser(strings.NewReader(newBody))
3190c4f9a53SEben Haber				r.ContentLength = int64(len(newBody))
3200c4f9a53SEben Haber			}
3210c4f9a53SEben Haber
3220c4f9a53SEben Haber			//log(fmt.Sprintf("Added creds, new bytes: %v\n", string(newParamBytes)))
3230c4f9a53SEben Haber
3240c4f9a53SEben Haber			// URL encoded
3250c4f9a53SEben Haber		} else if strings.EqualFold(r.Header.Get("Content-Type"), "application/x-www-form-urlencoded") {
3260c4f9a53SEben Haber			body, err := ioutil.ReadAll(r.Body)
3270c4f9a53SEben Haber			if err != nil {
3280c4f9a53SEben Haber				log(fmt.Sprintf("Error parsing URL-encoded body: %v\n", err))
3290c4f9a53SEben Haber				return
3300c4f9a53SEben Haber			}
3310c4f9a53SEben Haber			values, err := url.ParseQuery(string(body))
3320c4f9a53SEben Haber			if err != nil {
3330c4f9a53SEben Haber				log(fmt.Sprintf("Error parsing URL-encoded body values: %v\n", err))
3340c4f9a53SEben Haber				return
3350c4f9a53SEben Haber			}
3360c4f9a53SEben Haber			stmt := values.Get("statement")
3370c4f9a53SEben Haber			cci := values.Get("client_context_id")
3380c4f9a53SEben Haber			creds := values.Get("creds")
3390c4f9a53SEben Haber
340d6e1afeaSEben M Haber			// creds is JSON, array of Credential
341d6e1afeaSEben M Haber			var cred_array []Credential
3420c4f9a53SEben Haber			//log(fmt.Sprintf("Got creds bytes: %v\n", creds))
343d6e1afeaSEben M Haber			if len(creds) > 0 {
344d6e1afeaSEben M Haber				err = json.Unmarshal([]byte(creds), &cred_array)
345d6e1afeaSEben M Haber				if err != nil {
346d6e1afeaSEben M Haber					log(fmt.Sprintf("Error unmarshalling creds: %v\n", err))
347d6e1afeaSEben M Haber					return
348d6e1afeaSEben M Haber				}
349d6e1afeaSEben M Haber				cred_array = append(cred_array, credentials...)
350d6e1afeaSEben M Haber				creds_bytes, err := json.Marshal(cred_array)
351d6e1afeaSEben M Haber				if err != nil {
352d6e1afeaSEben M Haber					log(fmt.Sprintf("Error re-marshalling creds: %v\n", err))
353d6e1afeaSEben M Haber					return
354d6e1afeaSEben M Haber				}
355d6e1afeaSEben M Haber				creds = string(creds_bytes)
356d6e1afeaSEben M Haber				//log(fmt.Sprintf("Added u-creds, new bytes: %v\n", string(creds_bytes)))
3570c4f9a53SEben Haber			}
3580c4f9a53SEben Haber
3590c4f9a53SEben Haber			data := url.Values{}
3600c4f9a53SEben Haber			data.Set("statement", stmt)
3610c4f9a53SEben Haber			data.Add("client_context_id", cci)
3620c4f9a53SEben Haber			data.Add("creds", creds)
3630c4f9a53SEben Haber			newBody := data.Encode()
3640c4f9a53SEben Haber			r.Body = ioutil.NopCloser(strings.NewReader(newBody))
3650c4f9a53SEben Haber			r.ContentLength = int64(len(newBody))
366d6e1afeaSEben M Haber		}
3670c4f9a53SEben Haber	}
3680c4f9a53SEben Haber
3690c4f9a53SEben Haber	//log(fmt.Sprintf("Got new request form: %v\n", r.PostForm))
3700c4f9a53SEben Haber	//log(fmt.Sprintf("Got new request: %v\n", r))
3714868f70dSEben Haber}
3724868f70dSEben Haber
3734868f70dSEben Haberfunc toImage(r *http.Request) {
374046869d9SEben Haber	//r.Host = serverHost
3754868f70dSEben Haber	r.URL.Host = r.Host
3764868f70dSEben Haber	r.URL.Scheme = "http"
3774868f70dSEben Haber	//fmt.Printf("Got image path: %s %s\n  form: %v\n", r.Host,r.URL.Path, r.Form)
3784868f70dSEben Haber	if strings.HasPrefix(strings.ToLower(r.URL.Path), "/_p/ui/query") {
3794868f70dSEben Haber		r.URL.Path = r.URL.Path[12:]
3804868f70dSEben Haber		//fmt.Printf("  new host %v path: %s\n", r.Host,r.URL.Path)
38196a7649fSEben Haber	}
3820d609c4dSEben Haber	if strings.HasPrefix(strings.ToLower(r.URL.Path), "../_p/ui/query") {
3830d609c4dSEben Haber		r.URL.Path = r.URL.Path[14:]
3840d609c4dSEben Haber		//fmt.Printf("  new host %v path: %s\n", r.Host,r.URL.Path)
3850d609c4dSEben Haber	}
38611fe5a4eSEben Haber}
38711fe5a4eSEben Haber
38811fe5a4eSEben Haberfunc toMgmt(r *http.Request) {
389d6e1afeaSEben M Haber	//fmt.Printf(" pre toMgmt, host %v path %v\n  method: %v MethodGet %v\n\n", r.Host, r.URL.Path, r.Method, http.MethodGet)
390d6e1afeaSEben M Haber
391d6e1afeaSEben M Haber	// for safely, we should only handle GET requests to pools and pools/default, and POST to checkPermissions
392d6e1afeaSEben M Haber	if r.Method == http.MethodGet || (r.Method == http.MethodPost && r.URL.Path == "/pools/default/checkPermissions") {
393d6e1afeaSEben M Haber		r.Host = serverHost
394d6e1afeaSEben M Haber		r.URL.Host = r.Host
395d6e1afeaSEben M Haber		r.URL.Scheme = "http"
396d6e1afeaSEben M Haber
397d6e1afeaSEben M Haber		// if we have a login/password, use it for authorization
398d6e1afeaSEben M Haber		if USER != nil && len(*USER) > 0 && PASS != nil && len(*PASS) > 0 {
399d6e1afeaSEben M Haber			//fmt.Printf(" Setting user to %s and pass to %s\n",*USER,*PASS)
400d6e1afeaSEben M Haber			r.SetBasicAuth(*USER, *PASS)
401d6e1afeaSEben M Haber		}
402d6e1afeaSEben M Haber		//fmt.Printf("   post toMgmt, host %v path %v\n\n", r.Host, r.URL.Path)
403d6e1afeaSEben M Haber	} //else {
404d6e1afeaSEben M Haber	//    fmt.Printf("   REJECTED!")
405d6e1afeaSEben M Haber	//}
4060c4f9a53SEben Haber}
4070c4f9a53SEben Haber
4080c4f9a53SEben Haberfunc fromMgmt(resp *http.Response) error {
409d6e1afeaSEben M Haber	//fmt.Printf("Got response: %v\n", resp.Header)
4100c4f9a53SEben Haber	delete(resp.Header, "Www-Authenticate")
4110c4f9a53SEben Haber	//fmt.Printf("After got response: %v\n",resp.Header)
4120c4f9a53SEben Haber	return nil
41311fe5a4eSEben Haber}
41411fe5a4eSEben Haber
41511fe5a4eSEben Haber//
41611fe5a4eSEben Haber// This function is used to launch the http server inside a goroutine, accepting a
41711fe5a4eSEben Haber// channel that will return any error status.
41811fe5a4eSEben Haber//
41911fe5a4eSEben Haber
42011fe5a4eSEben Haberfunc listenAndServe(localport string) {
42111fe5a4eSEben Haber	err := http.ListenAndServe(localport, nil)
42211fe5a4eSEben Haber	if err != nil {
42311fe5a4eSEben Haber		fmt.Printf("\n\nError launching web server on port %s: %v\n", localport, err)
42411fe5a4eSEben Haber		os.Exit(1)
42511fe5a4eSEben Haber	}
42611fe5a4eSEben Haber
42711fe5a4eSEben Haber}
42811fe5a4eSEben Haber
42911fe5a4eSEben Haber//
43011fe5a4eSEben Haber// convenience method for returning errors
43111fe5a4eSEben Haber//
43211fe5a4eSEben Haber
43311fe5a4eSEben Haberfunc writeHttpError(message string, resp http.ResponseWriter) {
43411fe5a4eSEben Haber	response := map[string]interface{}{}
43511fe5a4eSEben Haber	response["status"] = "fail"
43611fe5a4eSEben Haber	response["errors"] = message
43711fe5a4eSEben Haber
43811fe5a4eSEben Haber	log(message)
43911fe5a4eSEben Haber	resp.WriteHeader(500)
44011fe5a4eSEben Haber	bytes, err := json.Marshal(response)
44111fe5a4eSEben Haber	if err != nil {
44211fe5a4eSEben Haber		resp.Write([]byte(fmt.Sprintf("internal error marshalling JSON: %v\n", err)))
44311fe5a4eSEben Haber		return
44411fe5a4eSEben Haber	}
44511fe5a4eSEben Haber	resp.Write(bytes)
44611fe5a4eSEben Haber}
44711fe5a4eSEben Haber
44811fe5a4eSEben Haber//
44911fe5a4eSEben Haber// in verbose mode, output messages on the command line
45011fe5a4eSEben Haber//
45111fe5a4eSEben Haber
45211fe5a4eSEben Haberfunc log(message string) {
45311fe5a4eSEben Haber	if *VERBOSE {
45411fe5a4eSEben Haber		fmt.Println(message)
45511fe5a4eSEben Haber	}
45611fe5a4eSEben Haber}
457