add proxy code

This commit is contained in:
Asim Aslam
2021-12-15 11:21:48 +00:00
parent ff51489278
commit ea691f1cf9

148
app/proxy/proxy.go Normal file
View File

@@ -0,0 +1,148 @@
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)
}