Proxy request host is overwritten by the real request - go

I am trying to serve a proxy like this:
package main
import (
"net/http"
)
func main() {
http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
println(r.Host)
}))
}
and calling it with curl
curl -k -x http://localhost:8080 http://golang.org/
I get golang.org printed out. Why I do not get the proxy hostname localhost? is that a bug or limitation with the http proxy?
Update
To clarify, what I am looking for is something like Nginx server address http://nginx.org/en/docs/http/ngx_http_core_module.html#var_server_addr

I should use LocalAddrContextKey but looks like there is a known bug with setting it https://github.com/golang/go/issues/18686
A workaround is to hijack the http.ResponseWriter ex:
package main
import (
"net/http"
)
func main() {
http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hij, ok := w.(http.Hijacker)
if !ok {
panic("http server does not support hijacker")
}
clientConn, _, err := hij.Hijack()
if err != nil {
panic(err.Error())
}
println(clientConn.LocalAddr().String())
}))
}

Related

how to create a reverse proxy in golang

I wants to make a reverse proxy in golang using net package from stl library. Used httputil for creating reverse proxy. But when I make request to the proxy server it return 404 error.
Here is the proxy server code
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
demoUrl , err := url.Parse("http://localhost:1000/run/")
if err!=nil{
log.Fatal(err)
return
}
proxy := httputil.NewSingleHostReverseProxy(demoUrl)
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(rw, r)
})
http.ListenAndServe(":2000", nil)
}
Here is the origin server code :
package main
import (
"net/http"
)
func main(){
http.HandleFunc("/run", func(w http.ResponseWriter, r *http.Request){
w.Write([]byte("I am running"))
})
http.ListenAndServe(":1000", nil)
}
Please tell what I am missing here and how to fix the bug! Please
The router doesn't match.
this will be working as expected.
...
func main(){
http.HandleFunc("/run/", func(w http.ResponseWriter, r *http.Request){
w.Write([]byte("I am running"))
})
http.ListenAndServe(":1000", nil)
}
...

Web address routing works fine with http.ListenAndServe, but fails with cgi.Serve()

I'm working on a website using Go. The server constraints require that I use CGI. When I test the following code locally using http.ListenAndServe() (commented out below), the various handlers are called correctly depending on the address requested. However, if I use cgi.Serve() instead, the default router is executed for all addresses (i.e., the handler for "/" is always executed). I'd appreciate any clues as to how to fix the issue.
EDIT: Here is the simplest test case I can think of to show the problem:
//=============SIMPLIFIED CODE================//
package main
import (
"fmt"
"net/http"
"net/http/cgi"
)
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Default")
}
func otherHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Other")
}
func main() {
http.HandleFunc("/other", otherHandler)
http.HandleFunc("/", defaultHandler)
/*
//Works fine
err := http.ListenAndServe(":8090", nil)
if err != nil {
panic(err)
}
*/
//Always fires defaultHandler no matter the address requested
err := cgi.Serve(nil)
if err != nil {
panic(err)
}
}
//=============CODE FROM ORIGINAL POST===================//
package main
import (
"fmt"
"net/http"
"net/http/cgi"
"net/url"
"os"
"github.com/go-cas/cas"
)
func logoutHandler(w http.ResponseWriter, r *http.Request) {
cas.RedirectToLogout(w, r)
}
func calendarHandler(w http.ResponseWriter, r *http.Request) {
if !cas.IsAuthenticated(r) {
cas.RedirectToLogin(w, r)
}
fmt.Fprintf(w, "Calendar for %s", cas.Username(r))
}
func defaultHandler(w http.ResponseWriter, r *http.Request) {
if !cas.IsAuthenticated(r) {
cas.RedirectToLogin(w, r)
}
fmt.Fprintf(w, "Hi there %s!", cas.Username(r))
}
func main() {
u, _ := url.Parse("https://www.examplecasserver.com")
client := cas.NewClient(&cas.Options{
URL: u,
})
http.Handle("/logout", client.HandleFunc(logoutHandler))
http.Handle("/calendar", client.HandleFunc(calendarHandler))
http.Handle("/", client.HandleFunc(defaultHandler))
/*
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
*/
err := cgi.Serve(nil)
if err != nil {
panic(err)
}
}
The CGI program expects some variables to be set in order to build the request.
Probably there is some issue with the configuration of your web server in which the variables are either not set correctly or not named correctly.
To verify this:
1) Add this before calling cgi.Serve and you'll see how the right handler is called (otherHandler)
os.Setenv("REQUEST_METHOD", "get")
os.Setenv("SERVER_PROTOCOL", "HTTP/1.1")
os.Setenv("SCRIPT_NAME", "/other")
2) Add this at the beginning of the main to check how the variables are being set by the web server:
fmt.Println(os.Environ())
In that output, look for the CGI meta variables defined in the spec:
http://www.ietf.org/rfc/rfc3875
Look for the section "Request Meta-Variables" in that page, you are probably looking for the SCRIPT_NAME or PATH_INFO variables.
EDIT
From the variable values you pasted below, it seems the issue is that the REQUEST_URI contains an additional path component:
REQUEST_URI=/main.cgi/other
So the easiest fix would be for you to map the routes accordingly:
http.HandleFunc("/main.cgi/other", otherHandler)
http.HandleFunc("/", defaultHandler) // or maybe /main.cgi

how to redirect http to https in Gorilla Mux?

I am developing a golang application and I am using Gorilla Mux and i want to redirect HTTP requests to HTTPS
here is what i have so far
package main
import (
"net/http"
"github.com/gorilla/mux"
"github.com/zolamk/deviant/handlers"
"github.com/zolamk/deviant/lib"
)
func main() {
router := mux.NewRouter()
// this is where i am trying to redirect
router.PathPrefix("/").Schemes("HTTP").HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
http.Redirect(res, req, fmt.Sprintf("https://%s", req.URL), http.StatusSeeOther)
})
router.Handle("/", handlers.ContextHandler(handlers.Index)).Methods("GET")
router.Handle("/register/", handlers.ContextHandler(handlers.Register)).Methods("GET")
router.Handle("/register/", handlers.ContextHandler(handlers.RegisterPost)).Methods("POST")
router.Handle("/login/", handlers.ContextHandler(handlers.Login)).Methods("GET")
router.Handle("/login/", handlers.ContextHandler(handlers.LoginPost)).Methods("POST")
router.Handle("/logout/", handlers.ContextHandler(handlers.Logout)).Methods("GET")
if lib.Settings.ServeStatic {
router.PathPrefix("/public/").Handler(http.FileServer(http.Dir("./")))
}
router.NotFoundHandler = handlers.ContextHandler(handlers.NotFound)
log.Printf("Deviant running # http://%s\n", lib.Settings.Address)
loggedRouter := handlers.LoggedRouter(os.Stdout, router)
log.Fatal(http.ListenAndServe(lib.Settings.Address, loggedRouter))
}
so like i said before how do i redirect HTTP traffic to HTTPS without affecting my other routes? Thank You.
Start another HTTP handler on the other port in a separate go routine
go http.ListenAndServe(":80", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently)
}))
What I ended up doing was that, I wrote a middleware that redirects HTTP requests to HTTPS
func RedirectToHTTPSRouter(next http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
proto := req.Header.Get("x-forwarded-proto")
if proto == "http" || proto == "HTTP" {
http.Redirect(res, req, fmt.Sprintf("https://%s%s", req.Host, req.URL), http.StatusPermanentRedirect)
return
}
next.ServeHTTP(res, req)
})
}
func main() {
router := mux.NewRouter()
httpsRouter := RedirectToHTTPSRouter(router)
log.Fatal(http.ListenAndServe(lib.Settings.Address, httpsRouter))
}

How do I rewrite / redirect from http to https in Go?

I have put up TLS and it works. I know how to rewrite from http to https in nginx, but I do not use nginx anymore. I don't know how to do this in Go properly.
func main() {
certificate := "/srv/ssl/ssl-bundle.crt"
privateKey := "/srv/ssl/mykey.key"
http.HandleFunc("/", rootHander)
// log.Fatal(http.ListenAndServe(":80", nil))
log.Fatal(http.ListenAndServeTLS(":443", certificate, privateKey, nil))
}
func rootHander(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("To the moon!"))
}
How would I do this in a good way?
Create a handler which handles redirection to https like:
func redirectToTls(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://IPAddr:443"+r.RequestURI, http.StatusMovedPermanently)
}
Then redirect http traffic:
go func() {
if err := http.ListenAndServe(":80", http.HandlerFunc(redirectToTls)); err != nil {
log.Fatalf("ListenAndServe error: %v", err)
}
}()
The solutions posted above are a little inflexible, especially if the external hostname is different from the local host.
Here is the code I use for HTTP->HTTPS redirects:
package main
import (
"net"
"log"
"net/http"
)
var httpAddr ":8080"
var httpsAddr ":8443"
func main() {
srv := http.Server{
Addr: httpsAddr,
}
_, tlsPort, err := net.SplitHostPort(httpsAddr)
if err != nil {
return err
}
go redirectToHTTPS(tlsPort)
srv.ListenAndServeTLS("cert.pem", "key.pem")
}
func redirectToHTTPS(tlsPort string) {
httpSrv := http.Server{
Addr: httpAddr,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
host, _, _ := net.SplitHostPort(r.Host)
u := r.URL
u.Host = net.JoinHostPort(host, tlsPort)
u.Scheme="https"
log.Println(u.String())
http.Redirect(w,r,u.String(), http.StatusMovedPermanently)
}),
}
log.Println(httpSrv.ListenAndServe())
}
If you are using standard ports (80,443) the splitting of joining of the adresses is not rquired and just setting the scheme on the URL is sufficient.
package main
import (
"fmt"
"net/http"
)
func redirectToHttps(w http.ResponseWriter, r *http.Request) {
// Redirect the incoming HTTP request. Note that "127.0.0.1:443" will only work if you are accessing the server from your local machine.
http.Redirect(w, r, "https://127.0.0.1:443"+r.RequestURI, http.StatusMovedPermanently)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there!")
fmt.Println(r.RequestURI)
}
func main() {
http.HandleFunc("/", handler)
// Start the HTTPS server in a goroutine
go http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
// Start the HTTP server and redirect all incoming connections to HTTPS
http.ListenAndServe(":8080", http.HandlerFunc(redirectToHttps))
}
There is another great example and discussion here if you are using your own mux:
https://gist.github.com/d-schmidt/587ceec34ce1334a5e60

Go simple API Gateway proxy

I've been searching all over the internet how to do this, but I haven't been able to find it. I'm trying to build a simple API gateway using Go and Martini for my system that has a few microservices with REST interfaces running. For example, I have my users service running on 192.168.2.8:8000, and I want to access it through /users
So my API gateway would look something like this:
package main
import (
"github.com/codegangsta/martini"
"net/http"
)
func main(){
app := martini.Classic()
app.Get("/users/:resource", func(req *http.Request, res http.ResponseWriter){
//proxy to http://192.168.2.8:8000/:resource
})
app.Run()
}
edit
I've got something working, but all i see is [vhost v2] release 2.2.5:
package main
import(
"net/url"
"net/http"
"net/http/httputil"
"github.com/codegangsta/martini"
"fmt"
)
func main() {
remote, err := url.Parse("http://127.0.0.1:3000")
if err != nil {
panic(err)
}
proxy := httputil.NewSingleHostReverseProxy(remote)
app := martini.Classic()
app.Get("/users/**", handler(proxy))
app.RunOnAddr(":4000")
}
func handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request, martini.Params) {
return func(w http.ResponseWriter, r *http.Request, params martini.Params) {
fmt.Println(params)
r.URL.Path = "/authorize"
p.ServeHTTP(w, r)
}
}
edit 2
This only seems to be a problem when using it directly through the browser, XMLHttpRequest works just fine
stdlib version
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
target, err := url.Parse("http://192.168.2.8:8000")
if err != nil {
log.Fatal(err)
}
http.Handle("/users/", http.StripPrefix("/users/", httputil.NewSingleHostReverseProxy(target)))
http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("./Documents"))))
log.Fatal(http.ListenAndServe(":8080", nil))
}
Wrap http.StripPrefix with a function that logs before calling it if you need logging.

Resources