mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-11 19:04:35 +00:00
149 lines
2.7 KiB
Go
149 lines
2.7 KiB
Go
package proxy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
// API Key
|
|
APIKey = os.Getenv("MICRO_API_KEY")
|
|
|
|
// API Url
|
|
APIHost = "https://api.m3o.com"
|
|
|
|
// host to proxy for Apps
|
|
AppHost = "m3o.app"
|
|
)
|
|
|
|
var (
|
|
mtx sync.RWMutex
|
|
|
|
// local cache
|
|
appMap = map[string]*backend{}
|
|
)
|
|
|
|
type backend struct {
|
|
url *url.URL
|
|
created time.Time
|
|
}
|
|
|
|
func Handler(w http.ResponseWriter, r *http.Request) {
|
|
// no subdomain
|
|
if r.Host == AppHost {
|
|
return
|
|
}
|
|
|
|
// lookup the app map
|
|
mtx.RLock()
|
|
bk, ok := appMap[r.Host]
|
|
mtx.RUnlock()
|
|
|
|
// check the url map
|
|
if ok && time.Since(bk.created) < time.Minute {
|
|
r.Host = bk.url.Host
|
|
r.Header.Set("Host", r.Host)
|
|
httputil.NewSingleHostReverseProxy(bk.url).ServeHTTP(w, r)
|
|
return
|
|
}
|
|
|
|
subdomain := strings.TrimSuffix(r.Host, "."+AppHost)
|
|
|
|
// only process one part for now
|
|
parts := strings.Split(subdomain, ".")
|
|
if len(parts) > 1 {
|
|
log.Print("[app/proxy] more parts than expected", parts)
|
|
return
|
|
}
|
|
|
|
// currently service id is the subdomain
|
|
id := subdomain
|
|
|
|
log.Printf("[app/proxy] resolving host %s to id %s\n", r.Host, id)
|
|
|
|
apiURL := APIHost + "/app/resolve"
|
|
|
|
// use /v1/
|
|
if len(APIKey) > 0 {
|
|
apiURL = APIHost + "/v1/app/resolve"
|
|
}
|
|
|
|
// make new request
|
|
log.Printf("[app/proxy] Calling: %v", apiURL+"?id="+id)
|
|
req, err := http.NewRequest("GET", apiURL+"?id="+id, nil)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
|
|
if req.Header == nil {
|
|
req.Header = make(http.Header)
|
|
}
|
|
|
|
// set the api key after we're given the header
|
|
if len(APIKey) > 0 {
|
|
req.Header.Set("Authorization", "Bearer "+APIKey)
|
|
}
|
|
|
|
// call the backend for the url
|
|
rsp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
defer rsp.Body.Close()
|
|
|
|
b, err := ioutil.ReadAll(rsp.Body)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
|
|
if rsp.StatusCode != 200 {
|
|
log.Printf("[app/proxy] Error calling api: status: %v %v", rsp.StatusCode, string(b))
|
|
http.Error(w, "unexpected error", 500)
|
|
return
|
|
}
|
|
|
|
result := map[string]interface{}{}
|
|
|
|
if err := json.Unmarshal(b, &result); err != nil {
|
|
log.Print("[app/proxy] failed to unmarshal response")
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
|
|
// get the destination url
|
|
u, _ := result["url"].(string)
|
|
if len(u) == 0 {
|
|
log.Print("[app/proxy] no response url")
|
|
return
|
|
}
|
|
|
|
uri, err := url.Parse(u)
|
|
if err != nil {
|
|
log.Print("[app/proxy] failed to parse url", err.Error())
|
|
return
|
|
}
|
|
|
|
mtx.Lock()
|
|
appMap[r.Host] = &backend{
|
|
url: uri,
|
|
created: time.Now(),
|
|
}
|
|
mtx.Unlock()
|
|
|
|
r.Host = uri.Host
|
|
r.Header.Set("Host", r.Host)
|
|
|
|
httputil.NewSingleHostReverseProxy(uri).ServeHTTP(w, r)
|
|
}
|