Browse Source

first version

master
Nicolas Massé 8 years ago
commit
97b5c8d43f
  1. 1
      .gitignore
  2. 5
      Dockerfile
  3. 41
      README.md
  4. 124
      src/itix.fr/forward/main.go

1
.gitignore

@ -0,0 +1 @@
apicast-sidecar-proxy

5
Dockerfile

@ -0,0 +1,5 @@
FROM scratch
COPY apicast-sidecar-proxy /apicast-sidecar-proxy
EXPOSE 9090 9091
ENTRYPOINT [ "/apicast-sidecar-proxy" ]

41
README.md

@ -0,0 +1,41 @@
# A sidecar for Apicast, enabling proxy support
## Why this project ?
[Apicast](https://github.com/3scale/apicast) is an API gateway that fetches his
configuration and asks for authorization on the 3scale services (hosted in their
cloud). The gateway, however, is deployed on-premise in the customer network.
More and more customers enforce the use of an HTTP proxy for outgoing connections.
Sometimes, an authentication is required to connect to the proxy.
Currently, apicast does not fully support HTTP proxies. Hence this project provides
a sidecar container for Apicast that enables HTTP proxies support.
## Deployment
TODO
## Development
### Build
```
GOOS=linux GOARCH=amd64 go build -o apicast-sidecar-proxy src/itix.fr/forward/main.go
```
### Package
```
VERSION=1.0
git tag TODO
docker build -t apicast-sidecar-proxy:$VERSION .
```
### Pushing your image to DockerHub (Optional)
```
docker login https://index.docker.io/v1/
docker images apicast-sidecar-proxy:$VERSION --format '{{ .ID }}'
docker tag $(docker images apicast-sidecar-proxy:$VERSION --format '{{ .ID }}') index.docker.io/<your-username>/apicast-sidecar-proxy:$VERSION
docker push index.docker.io/<your-username>/apicast-sidecar-proxy:$VERSION
```

124
src/itix.fr/forward/main.go

@ -0,0 +1,124 @@
package main
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strconv"
)
// The MyResponseWriter is a wrapper around the standard http.ResponseWriter
// We need it to retrieve the http status code of an http response
type MyResponseWriter struct {
Underlying http.ResponseWriter
Status int
}
func (mrw *MyResponseWriter) Header() http.Header {
return mrw.Underlying.Header()
}
func (mrw *MyResponseWriter) Write(b []byte) (int, error) {
return mrw.Underlying.Write(b)
}
func (mrw *MyResponseWriter) WriteHeader(s int) {
mrw.Status = s
mrw.Underlying.WriteHeader(s)
}
func SetupReverseProxy(local_port int, target string) {
// Parse the target URL and perform some sanity checks
url, err := url.Parse(target)
if err != nil {
panic(err)
}
// Initialize a Reverse Proxy object with a custom director
proxy := httputil.NewSingleHostReverseProxy(url)
underlying_director := proxy.Director
proxy.Director = func(req *http.Request) {
// Let the underlying director do the mandatory job
underlying_director(req)
// Custom Handling
// ---------------
//
// Filter out the "Host" header sent by the client
// otherwise the target server won't be able to find the
// matching virtual host. The correct host header will be
// added automatically by the net/http package.
req.Host = ""
}
handler := http.NewServeMux()
handler.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
// Log the incoming request (including headers)
fmt.Printf("%v %v HTTP/1.1\n", req.Method, req.URL)
req.Header.Write(os.Stdout)
fmt.Println()
// Wrap the standard response writer with our own
// implementation because we need the status code of the
// response and that field is not exported by default
mrw := &MyResponseWriter{Underlying: rw}
// Let the reverse proxy handle the request
proxy.ServeHTTP(mrw, req)
// Log the response
fmt.Printf("%v %v\n", mrw.Status, http.StatusText(mrw.Status))
mrw.Header().Write(os.Stdout)
fmt.Println()
})
fmt.Printf("Listening on port %v for incoming requests...\n", local_port)
err = http.ListenAndServe(fmt.Sprintf(":%v", local_port), handler)
if (err != nil) {
fmt.Println("ERROR: %s", err)
}
}
func main() {
portal_endpoint := os.Getenv("THREESCALE_PORTAL_ENDPOINT")
backend_endpoint := os.Getenv("BACKEND_ENDPOINT_OVERRIDE")
portal_port_opt := os.Getenv("PORTAL_LISTEN_PORT")
if (portal_port_opt == "") {
portal_port_opt = "9090"
fmt.Println("WARNING: No PORTAL_LISTEN_PORT environment variable found, defaulting to '9090'...")
}
backend_port_opt := os.Getenv("BACKEND_LISTEN_PORT")
if (backend_port_opt == "") {
backend_port_opt = "9091"
fmt.Println("WARNING: No BACKEND_LISTEN_PORT environment variable found, defaulting to '9091'...")
}
error := false
if portal_endpoint == "" {
fmt.Println("ERROR: No THREESCALE_PORTAL_ENDPOINT environment variable found !")
error = true
}
if backend_endpoint == "" {
fmt.Println("ERROR: No BACKEND_ENDPOINT_OVERRIDE environment variable found !")
error = true
}
portal_port, err := strconv.Atoi(portal_port_opt)
if (err != nil) {
fmt.Printf("ERROR: Cannot parse the PORTAL_LISTEN_PORT environment variable (%s): %s\n", portal_port_opt, err)
error = true
}
backend_port, err := strconv.Atoi(backend_port_opt)
if (err != nil) {
fmt.Printf("ERROR: Cannot parse the BACKEND_LISTEN_PORT environment variable (%s): %s\n", backend_port_opt, err)
error = true
}
if error {
os.Exit(1)
}
go SetupReverseProxy(backend_port, backend_endpoint)
SetupReverseProxy(portal_port, portal_endpoint)
}
Loading…
Cancel
Save