I have recently deployed a simple HTTP proxy written in Go to Heroku, and I seem to have problems connecting to the proxy port.
This is the code which starts the listener:
package main
import (
"io"
"log"
"net"
"net/http"
"strings"
"os"
"fmt"
)
// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
"Connection",
"Keep-Alive",
"Proxy-Authenticate",
"Proxy-Authorization",
"Te", // canonicalized version of "TE"
"Trailers",
"Transfer-Encoding",
"Upgrade",
}
func copyHeader(dst, src http.Header) {
for k, vv := range src {
for _, v := range vv {
dst.Add(k, v)
}
}
}
func delHopHeaders(header http.Header) {
for _, h := range hopHeaders {
header.Del(h)
}
}
func appendHostToXForwardHeader(header http.Header, host string) {
// If we aren't the first proxy retain prior
// X-Forwarded-For information as a comma+space
// separated list and fold multiple headers into one.
if prior, ok := header["X-Forwarded-For"]; ok {
host = strings.Join(prior, ", ") + ", " + host
}
header.Set("X-Forwarded-For", host)
}
type proxy struct {
}
func (p *proxy) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
log.Println(req.RemoteAddr, " ", req.Method, " ", req.URL)
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
msg := "unsupported protocal scheme "+req.URL.Scheme
http.Error(wr, msg, http.StatusBadRequest)
log.Println(msg)
return
}
client := &http.Client{}
//http: Request.RequestURI can't be set in client requests.
//http://golang.org/src/pkg/net/http/client.go
req.RequestURI = ""
delHopHeaders(req.Header)
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
appendHostToXForwardHeader(req.Header, clientIP)
}
resp, err := client.Do(req)
if err != nil {
http.Error(wr, "Server Error", http.StatusInternalServerError)
log.Fatal("ServeHTTP:", err)
}
defer resp.Body.Close()
log.Println(req.RemoteAddr, " ", resp.Status)
delHopHeaders(resp.Header)
copyHeader(wr.Header(), resp.Header)
wr.WriteHeader(resp.StatusCode)
io.Copy(wr, resp.Body)
}
func determineListenAddress() (string, error) {
port := os.Getenv("PORT")
if port == "" {
return "", fmt.Errorf("$PORT not set")
}
return ":" + port, nil
}
func main() {
addr, err := determineListenAddress()
if err != nil {
log.Fatal(err)
}
handler := &proxy{}
log.Println("Starting proxy server on", addr)
if err := http.ListenAndServe(addr, handler); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
The application starts up correctly:
2019-03-07T14:52:56.712482+00:00 heroku[web.1]: Starting process with command `go_proxy`
2019-03-07T14:52:58.410575+00:00 heroku[web.1]: State changed from starting to up
2019-03-07T14:52:58.288473+00:00 app[web.1]: 2019/03/07 14:52:58 Starting proxy server on :51264
but it seems to be only listening on port 80. When trying to telnet to my app hostname and port seen in the logs, the connection times out. When I try to reach it on port 80, it only displays some empty HTML page.
I've gone through many examples of simple go webapps but none of those seems to have similar issue.
Any help is much appreciated.