How to disable HTTP/2 using Server.TLSNextProto - go

I have a Go server handling https requests:
package main
import (
"fmt"
"net/http"
"log"
)
const (
port = "5966"
cert = "/tmp/cert.pem"
key = "/tmp/key.pem"
)
func main() {
listen_at := ":" + port
fmt.Println("Listening at", listen_at)
go http.HandleFunc("/job_handler/", job_handler)
log.Fatal(http.ListenAndServeTLS(listen_at, cert, key, nil))
}
func job_handler(w http.ResponseWriter, r *http.Request) {
// do somework
}
Turns out in https mode, Go has transparent support for the HTTP/2 protocol. We've some clients that noticeably misbehave in HTTP/2, and hence we need to disable HTTP/2 on the server side.
Unfortunately, I can't use ENV variable GODEBUG=http2server=0 to disable HTTP/2. What's left is Server.TLSNextProto as documented here.
How can I use Server.TLSNextProto on my server code above to disable https/2?

The simplest setup that disables HTTP/2 is
package main
import (
"log"
"net/http"
"crypto/tls"
)
func main() {
m := http.NewServeMux()
srv := &http.Server{
Handler: m,
Addr: "127.0.0.1:8080",
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
}
log.Fatal(srv.ListenAndServe())
}
You can verify the support with
curl -v --http2-prior-knowledge http://localhost:8080

Related

What configuration am I missing for httputil.NewSingleHostReverseProxy?

The code below produces the error further below. When I type "http://www.cnn.com/favicon.ico" straight into any browser it works without issue. I am guessing that I am missing some critical configuration for the reverse proxy. What is the minimum config needed for getting this to work?
package main
import (
"net/http"
"net/http/httputil"
"net/url"
"log"
)
func main(){
url, _ := url.Parse("http://www.cnn.com/favicon.ico")
proxy := httputil.NewSingleHostReverseProxy(url)
http.HandleFunc("/", proxy.ServeHTTP)
log.Fatal(http.ListenAndServe(":9090", nil))
}
Fastly error: unknown domain: localhost. Please check that this domain
has been added to a service.
Details: cache-lax8625-LAX
Happy 4th of July!
I made the following 2 changes to get it working:
Firstly, point the proxy at www.cnn.com instead of www.cnn.com/favicon.ico. Of course, now we must make our request to localhost:9090/favicon.ico.
Next, set the proxied request's Host field to the target host, not the host of the proxy which is localhost.
The code ends up looking like this:
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
type Director func(*http.Request)
func (f Director) Then(g Director) Director {
return func(req *http.Request) {
f(req)
g(req)
}
}
func hostDirector(host string) Director {
return func(req *http.Request) {
req.Host = host
}
}
func main() {
url, _ := url.Parse("http://www.cnn.com")
proxy := httputil.NewSingleHostReverseProxy(url)
d := proxy.Director
// sequence the default director with our host director
proxy.Director = Director(d).Then(hostDirector(url.Hostname()))
http.Handle("/", proxy)
log.Fatal(http.ListenAndServe(":9090", nil))
}

How to pipe/forward seamlessly from port 80 to port XXXX with go?

I have a bunch of websites running on one server (single IP) and they all run on high ports so I don't need root to run them.
When someone visits from one web address, lets say, http://address001.com/, I want to seamlessly pipe the data from port 4444 to the person who made this request, and if someone visits http://address002.com/ I want to pipe the data from port 5555.
How would I do this in Go?
So far I have a handler function that looks like this:
func home(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.Host, "address001") {
// ???
}
}
you can use httputil's ReverseProxy
here's an example code
package main
import (
"log"
"net/http"
"net/http/httputil"
)
func main() {
director := func(req *http.Request) {
switch req.Host {
case "address001.com":
req.URL.Host = "localhost:4444"
req.URL.Scheme = "http"
case "address002.com":
req.URL.Host = "localhost:5555"
req.URL.Scheme = "http"
default:
log.Println("error")
}
}
proxy := &httputil.ReverseProxy{Director: director}
log.Fatalln(http.ListenAndServe(":8080", proxy))
}

reverse proxy does not work

I am using GO's reverse proxy like this, but this does not work well
package main
import (
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
u, _ := url.Parse("http://www.darul.io")
http.ListenAndServe(":9000", httputil.NewSingleHostReverseProxy(u))
}
when I visit the http://localhost:9000, I am seeing not expected page
From this article A Proper API Proxy Written in Go:
httputil.NewSingleHostReverseProxy does not set the host of the request to the host of the destination server. If you’re proxying from foo.com to bar.com, requests will arrive at bar.com with the host of foo.com. Many webservers are configured to not serve pages if a request doesn’t appear from the same host.
You need to define a custom middleware to set the required host parameter:
package main
import (
"net/http"
"net/http/httputil"
"net/url"
)
func sameHost(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Host = r.URL.Host
handler.ServeHTTP(w, r)
})
}
func main() {
// initialize our reverse proxy
u, _ := url.Parse("http://www.darul.io")
reverseProxy := httputil.NewSingleHostReverseProxy(u)
// wrap that proxy with our sameHost function
singleHosted := sameHost(reverseProxy)
http.ListenAndServe(":5000", singleHosted)
}

Point domain to go server with gorilla mux

I have a small server and I want that server to listen to my custom domain sftablet.dev using gorilla/mux package.
Here is the code:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.Host("sftablet.dev")
r.HandleFunc("/", HomeHandler)
r.HandleFunc("/products", ProductsHandler)
http.ListenAndServe(":8080", r)
}
func HomeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hey, this is homepage")
}
func ProductsHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hey, this is products")
}
I also added this in the hosts file:
127.0.0.1 sftablet.dev
But for some reason it doesn't work. It does work if I go to 127.0.0.1:8080, but not when I access http://sftablet.dev/. Also cleared the DNS cache.
http://sftablet.dev/ would by default query the port 80
Your server only listen to port 8080. http://sftablet.dev:8080/ should work.

Profiling Go web application built with Gorilla's mux with net/http/pprof

I have a relatively big web application written in Go that uses Gorilla's mux for routing. I recently realised that my web application is quite slow and I would like to profile the web application.
After reading about it, it seems that net/http/pprof is what I need. But I can't make it run with mux; even in the case of the most trivial web application.
Does anyone knows how to make that work?
Here is an example of a trivial code that does not work (i.e. nothing is served at /debug).
package main
import (
"fmt"
"github.com/gorilla/mux"
"math"
"net/http"
)
import _ "net/http/pprof"
func SayHello(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 1000000; i++ {
math.Pow(36, 89)
}
fmt.Fprint(w, "Hello!")
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/hello", SayHello)
http.ListenAndServe(":6060", r)
}
My preferred method for this is to just let net/http/pprof register itself to http.DefaultServeMux, and then pass all requests starting with /debug/pprof/ along:
package main
import (
"net/http"
_ "net/http/pprof"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
if err := http.ListenAndServe(":6060", router); err != nil {
panic(err)
}
}
I find that this approach is a lot more stable than one that depends on the implementation of a hidden initialization method, and also guarantees that you didn't miss anything.
user983716 - Thanks for your question and solution!
I was not able to use the links from the web index (http://[my-server]/debug/pprof), until I added a few lines to your solution, like so:
...
func AttachProfiler(router *mux.Router) {
router.HandleFunc("/debug/pprof/", pprof.Index)
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
// Manually add support for paths linked to by index page at /debug/pprof/
router.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
router.Handle("/debug/pprof/heap", pprof.Handler("heap"))
router.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
router.Handle("/debug/pprof/block", pprof.Handler("block"))
}
...
If anyone has the same problem, I hope this helps!
Sorry for that question. The answer is in the init() function of pprof. One just need to add 4 functions from pprof to the mux router. Here is the fixed code from above.
package main
import (
"fmt"
"github.com/gorilla/mux"
"math"
"net/http"
)
import "net/http/pprof"
func AttachProfiler(router *mux.Router) {
router.HandleFunc("/debug/pprof/", pprof.Index)
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
}
func SayHello(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 1000000; i++ {
math.Pow(36, 89)
}
fmt.Fprint(w, "Hello!")
}
func main() {
r := mux.NewRouter()
AttachProfiler(r)
r.HandleFunc("/hello", SayHello)
http.ListenAndServe(":6060", r)
}
Previous examples not really work on my side.
To use pprof in an existing golang project with gorrila/mux, try to add :
...previous code
func main() {
r := mux.NewRouter()
r.HandleFunc("/hello", SayHello)
go func() {
log.Fatal(http.ListenAndServe(":6061", http.DefaultServeMux))
}()
http.ListenAndServe(":6060", r)
}
then go to http://localhost:6061/debug/pprof/
I did something else, I added another native http server on a different port and it just works out of the box
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
log.Println(http.ListenAndServe(":6060", nil))
}()
log.Fatalln(http.ListenAndServe(":8080", route.Handlers()))
}
Now the pprof endpoint is at :
http://localhost:6060/debug/pprof/ and the applcation is running on port :8080
Just so:
r := mux.NewRouter()
r.PathPrefix("/debug").Handler(http.DefaultServeMux)
Im using https://github.com/julienschmidt/httprouter but i just got this answer from google search.
That's what i did
router := httprouter.New()
router.Handler("GET", "/debug/pprof/profile", http.DefaultServeMux)
router.Handler("GET", "/debug/pprof/heap", http.DefaultServeMux)
I only need this two routes.
This answer is combine of #damien and #user983716 answers.
The following should work:
import (
"net/http"
_ "net/http/pprof"
)
myrouter.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)

Resources