diff --git a/Makefile b/Makefile index 393af33..e91ad3c 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ xcompile: GOPATH=${GOPATH} GOOS=windows GOARCH=amd64 go build -o releases/windows/amd64/restjester ${GOPACKAGE} # OSX 64 - mkdir -p releases/OSX/amd64 + mkdir -p releases/darwin/amd64 GOPATH=${GOPATH} GOOS=darwin GOARCH=amd64 go build -o releases/darwin/amd64/restjester ${GOPACKAGE} # linux 64 diff --git a/README.md b/README.md index 49bca97..3f22cf9 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,17 @@ Download the server compiled for your architecture -[OSX] (https://github.com/kswope/restjester/blob/sync/releases/darwin/amd64/restjester?raw=true) +[OSX](https://github.com/kswope/restjester/blob/sync/releases/darwin/amd64/restjester?raw=true) | -[Linux 386] (https://github.com/kswope/restjester/blob/sync/releases/linux/386/restjester?raw=true) +[Linux 386](https://github.com/kswope/restjester/blob/sync/releases/linux/386/restjester?raw=true) | -[Linux x86-64] (https://github.com/kswope/restjester/blob/sync/releases/linux/amd64/restjester?raw=true) +[Linux x86-64](https://github.com/kswope/restjester/blob/sync/releases/linux/amd64/restjester?raw=true) | -[Linux ARM] (https://github.com/kswope/restjester/blob/sync/releases/linux/arm/restjester?raw=true) +[Linux ARM](https://github.com/kswope/restjester/blob/sync/releases/linux/arm/restjester?raw=true) | -[Linux ARM64] (https://github.com/kswope/restjester/blob/sync/releases/linux/arm64/restjester?raw=true) +[Linux ARM64](https://github.com/kswope/restjester/blob/sync/releases/linux/arm64/restjester?raw=true) | -[Windows](https://github.com/kswope/restjester/blob/sync/releases/windows/amd64/restjester?raw=true) +[Windows](https://github.com/kswope/restjester/blob/sync/releases/windows/amd64/restjester.exe?raw=true) #### If you want/need to compile your own binary @@ -50,9 +50,19 @@ As a daemon (requires deamon) shell> deamon --name restjester ./restjester ``` +#### For proxying and cache of API +To forward requests when there is no previously registered endpoint and then cache the response +With output to terminal +``` +shell> ./restjester -f https://apihost:80 +Starting server at port 5351 +``` - +As a daemon (requires deamon) +``` +shell> deamon --name restjester ./restjester -f https://apihost:80 +``` ### Complete Functionality @@ -72,7 +82,6 @@ shell> deamon --name restjester ./restjester - ### Example in ruby using rest-client and rspec ``` diff --git a/releases/darwin/amd64/restjester b/releases/darwin/amd64/restjester index 63e805f..b21e27c 100755 Binary files a/releases/darwin/amd64/restjester and b/releases/darwin/amd64/restjester differ diff --git a/releases/linux/386/restjester b/releases/linux/386/restjester index 86a82d7..2709a6c 100755 Binary files a/releases/linux/386/restjester and b/releases/linux/386/restjester differ diff --git a/releases/linux/amd64/restjester b/releases/linux/amd64/restjester index 5541211..8ae7eed 100755 Binary files a/releases/linux/amd64/restjester and b/releases/linux/amd64/restjester differ diff --git a/releases/linux/arm/restjester b/releases/linux/arm/restjester index f2b0f9f..d488cfc 100755 Binary files a/releases/linux/arm/restjester and b/releases/linux/arm/restjester differ diff --git a/releases/linux/arm64/restjester b/releases/linux/arm64/restjester index 0ec8355..2888408 100755 Binary files a/releases/linux/arm64/restjester and b/releases/linux/arm64/restjester differ diff --git a/releases/windows/amd64/restjester b/releases/windows/amd64/restjester deleted file mode 100755 index ead7d47..0000000 Binary files a/releases/windows/amd64/restjester and /dev/null differ diff --git a/releases/windows/amd64/restjester.exe b/releases/windows/amd64/restjester.exe new file mode 100644 index 0000000..1cbc9c0 Binary files /dev/null and b/releases/windows/amd64/restjester.exe differ diff --git a/server/src/restjester/endpoint.go b/server/src/restjester/endpoint.go index b019287..bd69618 100644 --- a/server/src/restjester/endpoint.go +++ b/server/src/restjester/endpoint.go @@ -1,14 +1,17 @@ package main -// import "fmt" -import "reflect" +import ( + "net/http" + "reflect" +) type endpoint struct { Path string // /a/b/c Query map[string][]string // ?a=b&c=d Method string // POST, GET Status int // - Data string // endpoint data + Data []byte // endpoint data + Header http.Header // header to response } //----------------------------------------------------------------------------- @@ -24,7 +27,8 @@ func createEndpoint( query map[string][]string, method string, status int, - data string, + data []byte, + header http.Header, ) endpoint { return endpoint{ Path: path, @@ -32,6 +36,7 @@ func createEndpoint( Method: method, Status: status, Data: data, + Header: header, } } diff --git a/server/src/restjester/endpoint_test.go b/server/src/restjester/endpoint_test.go index 7f975bd..24595ca 100644 --- a/server/src/restjester/endpoint_test.go +++ b/server/src/restjester/endpoint_test.go @@ -15,19 +15,19 @@ func getSampleEndpoint(index int) endpoint { var query map[string][]string query = map[string][]string{"a": []string{"1"}} - ep = createEndpoint("/a/b/c", query, "POST", 200, "data a") + ep = createEndpoint("/a/b/c", query, "POST", 200, []byte("data a"), defaultHeader) samples = append(samples, ep) query = map[string][]string{"b": []string{"2"}} - ep = createEndpoint("/d/e/f", query, "GET", 200, "data b") + ep = createEndpoint("/d/e/f", query, "GET", 200, []byte("data b"), defaultHeader) samples = append(samples, ep) query = map[string][]string{"c": []string{"3"}} - ep = createEndpoint("/g/h/i", query, "POST", 200, "data c") + ep = createEndpoint("/g/h/i", query, "POST", 200, []byte("data c"), defaultHeader) samples = append(samples, ep) query = map[string][]string{"d": []string{"4"}} - ep = createEndpoint("/j/k/l", query, "POST", 200, "data d") + ep = createEndpoint("/j/k/l", query, "POST", 200, []byte("data d"), defaultHeader) samples = append(samples, ep) return samples[index] @@ -39,14 +39,14 @@ func getSampleEndpoint(index int) endpoint { func TestCreateEndpoint(t *testing.T) { query := map[string][]string{"a": []string{"1"}} - endpoint := createEndpoint("/a/b/c", query, "POST", 200, "data a") + endpoint := createEndpoint("/a/b/c", query, "POST", 200, []byte("data a"), defaultHeader) // equality checks pathCheck := endpoint.Path == "/a/b/c" queryCheck := endpoint.Query["a"][0] == "1" methodCheck := endpoint.Method == "POST" statusCheck := endpoint.Status == 200 - dataCheck := endpoint.Data == "data a" + dataCheck := string(endpoint.Data) == "data a" if !(pathCheck && queryCheck && methodCheck && statusCheck && dataCheck) { t.Errorf("Failed with path:%q query:%s method:%s data:%s", diff --git a/server/src/restjester/extract_from_request.go b/server/src/restjester/extract_from_request.go index ea5141c..ab1a4df 100644 --- a/server/src/restjester/extract_from_request.go +++ b/server/src/restjester/extract_from_request.go @@ -30,11 +30,11 @@ func extractMethodFromRequest(req *http.Request) string { } } -func extractDataFromRequest(req *http.Request) string { +func extractDataFromRequest(req *http.Request) []byte { if len(req.Form["data"]) > 0 { - return req.Form["data"][0] + return []byte(req.Form["data"][0]) } else { - return "" // default + return []byte("") // default } } diff --git a/server/src/restjester/extract_from_request_test.go b/server/src/restjester/extract_from_request_test.go index 20c88a4..f085388 100644 --- a/server/src/restjester/extract_from_request_test.go +++ b/server/src/restjester/extract_from_request_test.go @@ -71,7 +71,7 @@ func TestExtractData(t *testing.T) { expected := "the payload" - if data != expected { + if string(data) != expected { t.Errorf("expected '%s', got '%s' from request %q", expected, data, request) } diff --git a/server/src/restjester/handlers.go b/server/src/restjester/handlers.go index dc15212..70497ad 100644 --- a/server/src/restjester/handlers.go +++ b/server/src/restjester/handlers.go @@ -1,8 +1,11 @@ package main -import "fmt" -import "net/http" -import "encoding/json" +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" +) func handleRootPut(w http.ResponseWriter, req *http.Request) { errMsg := "PUT / not implemented yet\n" @@ -14,6 +17,7 @@ func handleRootPut(w http.ResponseWriter, req *http.Request) { func handleRootGet(w http.ResponseWriter, req *http.Request) { fmt.Println("dumping endpoints") dumped, _ := json.Marshal(endpoints) + w.Header().Set("Content-Type", "application/json") w.Write([]byte(dumped)) } @@ -27,6 +31,7 @@ func handleRootPost(w http.ResponseWriter, req *http.Request) { extractMethodFromRequest(req), extractStatusFromRequest(req), extractDataFromRequest(req), + defaultHeader, ) if endpoint.Path == "" { @@ -53,17 +58,58 @@ func handleRootDelete(w http.ResponseWriter, req *http.Request) { func handlerEndpoint(w http.ResponseWriter, req *http.Request) { // create a 'close enough' endpoint to search with - var endpointApprox = createEndpoint(req.URL.Path, req.URL.Query(), req.Method, 200, "") + var endpointApprox = createEndpoint(req.URL.Path, req.URL.Query(), req.Method, 200, []byte{}, defaultHeader) if endpoint, found := endpointGet(endpoints, endpointApprox); found { - fmt.Printf("endpoint HIT %s\n", formatEndpoint(endpoint)) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(endpoint.Status) - w.Write([]byte(endpoint.Data)) + doResponse(w, endpoint) } else { - fmt.Printf("endpoint MISS, %s\n", formatEndpoint(endpointApprox)) - w.WriteHeader(http.StatusNotFound) - fmt.Fprintln(w, "404 endpoint not found") + + if len(proxyURL) > 0 { + urlEndPoint := fmt.Sprintf("%s%s", proxyURL, req.URL) + fmt.Printf("Proxying url, %s\n", urlEndPoint) + proxyRequest, err := http.NewRequest(endpointApprox.Method, urlEndPoint, req.Body) + if err != nil { + errorHandle(w, err) + return + } + proxyRequest.Header = req.Header + + proxyResponse, err := httpClient.Do(proxyRequest) + if err != nil { + errorHandle(w, err) + return + } + proxyResponseData, err := ioutil.ReadAll(proxyResponse.Body) + if err != nil { + errorHandle(w, err) + return + } + endpointApprox.Data = proxyResponseData + endpointApprox.Header = proxyResponse.Header + fmt.Println(endpointApprox.Header) + endpoints = endpointPut(endpoints, endpointApprox) + + doResponse(w, endpointApprox) + } else { + fmt.Printf("endpoint MISS, %s\n", formatEndpoint(endpointApprox)) + w.WriteHeader(http.StatusNotFound) + fmt.Fprintln(w, "404 endpoint not found") + } } } + +func errorHandle(w http.ResponseWriter, err error) { + fmt.Printf("proxy MISS, %s\n", err) + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintln(w, "500 Internal Server Error") +} + +func doResponse(w http.ResponseWriter, endpoint endpoint) { + fmt.Printf("endpoint HIT %s\n", formatEndpoint(endpoint)) + for k, v := range endpoint.Header { + w.Header().Set(k, v[0]) + } + w.WriteHeader(endpoint.Status) + w.Write(endpoint.Data) +} diff --git a/server/src/restjester/restjester.go b/server/src/restjester/restjester.go index 2eff1ae..7e55974 100644 --- a/server/src/restjester/restjester.go +++ b/server/src/restjester/restjester.go @@ -1,10 +1,17 @@ package main -import "fmt" -import "net/http" +import ( + "crypto/tls" + "flag" + "fmt" + "net/http" +) var endpoints = createEndpoints() var gPort int +var proxyURL string +var defaultHeader = http.Header{"Content-Type": []string{"application/json"}} +var httpClient = http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}} func handler(w http.ResponseWriter, req *http.Request) { @@ -30,7 +37,13 @@ func handler(w http.ResponseWriter, req *http.Request) { func main() { - gPort = 5351 + flagPort := flag.Int("p", 5351, "the server port") + flagURL := flag.String("f", "", "url to forward the request") + flag.Parse() + + gPort = *flagPort + proxyURL = *flagURL + fmt.Printf("Starting server at port %d\n", gPort) http.HandleFunc("/", handler) http.ListenAndServe(fmt.Sprintf(":%d", gPort), nil)