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