mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-11 19:04:35 +00:00
add proxy code
This commit is contained in:
148
app/proxy/proxy.go
Normal file
148
app/proxy/proxy.go
Normal 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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user