Goroutines with http.HandleFunc - go

I want to know if code 1 manage internal goutines and can use all the cores of one CPU when the requets will increase(dozens) or if per each handler I have to put the key word go that indicate that the funcion handler will be manage by one gorotine like is show it in code 2, and so can use all the cores of the server.
code 1
package main
import (
"fmt"
"net/http"
)
func HandlerOne(w http.ResponseWriter, req *http.Request) {
fmt.Println("message one")
}
func HandlerTwo(w http.ResponseWriter, req *http.Request) {
fmt.Println("message two")
}
func main() {
http.HandleFunc("/R1", HandlerOne)
http.HandleFunc("/R2", HandlerTwo)
err := http.ListenAndServe(":9998", nil)
if err != nil {
fmt.Printf("Server failed: ", err.Error())
}
}
code 2
package main
import (
"fmt"
"net/http"
)
func HandlerOne(w http.ResponseWriter, req *http.Request) {
fmt.Println("message one")
}
func HandlerTwo(w http.ResponseWriter, req *http.Request) {
fmt.Println("message two")
}
func main() {
go http.HandleFunc("/R1", HandlerOne)
go http.HandleFunc("/R2", HandlerTwo)
err := http.ListenAndServe(":9998", nil)
if err != nil {
fmt.Printf("Server failed: ", err.Error())
}
}
Note: Both run without problems and you be able to test it with
curl -l http://localhost:9998/R1
or
curl -l http://localhost:9998/R2

Version 2 is wrong. Package http handles all this stuff for you.
Just make sure you invoke your program with an appropriate GOMAXPROCS, e.g. like GOMAXPROCS=4 ./main

Related

HTTP Middleware and Google Cloud Functions

What’s the equivalent to middleware handlers in Google Cloud Functions?
In standard approach, normally I do:
router.Handle("/receive", middlewares.ErrorHandler(MyReceiveHandler))
And then, in the middleware:
type ErrorHandler func(http.ResponseWriter, *http.Request) error
func (fn ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := fn(w, r)
if err == nil {
return
}
log.Printf("An error accured: %v", err)
clientError, ok := err.(errors.BaseError)
if !ok {
w.WriteHeader(500)
return
}
w.WriteHeader(clientError.GetStatusCode())
w.Write([]byte(clientError.Error()))
}
In AWS Lambda, I can achieve the same thing using, for example:
func main() {
lambda.Start(
middlewares.Authentication(Handler),
)
}
But I could not find a way to do this in GCP Functions.
How would it work?
Can you help me?
Let's say you start with the following server code in your development environment:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.Handle("/", MiddlewareFinalMsg(" Goodbye!", http.HandlerFunc(HelloWorld)))
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
func MiddlewareFinalMsg(msg string, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r)
fmt.Fprint(w, msg)
})
}
func HelloWorld(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprint(w, "Hello, World!")
}
As far as I can tell, GCF requires its entry point to be an exported identifier of type func(http.ResponseWriter, *http.Request) (not http.HandlerFunc, not http.Handler); therefore, if you have a http.Handler, you'll need to select its ServeHTTP method explicitly to obtain a function of the expected type. However, that identifier can be a package-level function, a method, or a variable.
Here is how you can adapt the code above for GCF:
package p
import (
"fmt"
"net/http"
)
// use F as your GCF's entry point
var F = MiddlewareFinalMsg(" Goodbye!", http.HandlerFunc(HelloWorld)).ServeHTTP
func MiddlewareFinalMsg(msg string, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r)
fmt.Fprint(w, msg)
})
}
func HelloWorld(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprint(w, "Hello, World!")
}

Multiple Dir serving is not working

Any mistakes in below code? Multiple directory serving is not working from the below code. When I access the localhost:9090/ide, the server will return 404 error.
package main
import (
"log"
"net/http"
)
func serveIDE(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("/home/user/ide")).ServeHTTP(w, r)
}
func serveConsole(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("/home/user/console")).ServeHTTP(w, r)
}
func main() {
http.HandleFunc("/ide", serveIDE)
http.HandleFunc("/console", serveConsole)
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
When I change the code like this,
http.HandleFunc("/", serveIDE)
It will work as I expected.
One of the issues with using http.FileServer is that the request path is used to build the file name, so if you're serving from anywhere but the root you need to strip the route prefix to that handler.
The standard library includes a helpful tool for that http.StripPrefix, but that only works on http.Handlers, not http.HandleFuncs, so to use it you need to adapt your HandleFunc to a Handler.
Here is a working version that should do what you want. Note that wHandler is just an adapter from your HttpFunc methods to Hander interface:
package main
import (
"log"
"net/http"
)
func serveIDE(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("/home/user/ide")).ServeHTTP(w, r)
}
func serveConsole(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir("/home/user/console")).ServeHTTP(w, r)
}
type wHandler struct {
fn http.HandlerFunc
}
func (h *wHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("Handle request: %s %s", r.Method, r.RequestURI)
defer log.Printf("Done with request: %s %s", r.Method, r.RequestURI)
h.fn(w, r)
}
func main() {
http.Handle("/ide", http.StripPrefix("/ide", &wHandler{fn: serveIDE}))
http.Handle("/console", http.StripPrefix("/console", &wHandler{fn: serveConsole}))
err := http.ListenAndServe(":9090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}

global recover handler for golang http panic

I want to create global err handler to send it by email.
package main
import (
"github.com/gorilla/mux"
"log"
"net/http"
)
func main() {
rtr := mux.NewRouter()
rtr.HandleFunc("/", withPanic).Methods("GET")
http.Handle("/", rtr)
log.Println("Listening...")
http.ListenAndServe(":3001", http.DefaultServeMux)
}
func withPanic(w http.ResponseWriter, r *http.Request) {
panic("somewhere here will be panic, but I don't know where exactly")
}
How to make it global. It would be easy if I know where error will occur
if err != nil {
sendMeMail(err)
}
But what to do in cases when I don't know exactly where an error will occur? So I should add a global recoverish handler. But how to do it exactly I don't know.
Update
I added defer recover to beginning of main but it never executes on requesting http://localhost:3001. So panic is not emailed.
package main
import (
"errors"
"fmt"
"github.com/gorilla/mux"
"log"
"net/http"
)
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
// find out exactly what the error was and set err
var err error
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("Unknown panic")
}
if err != nil {
// sendMeMail(err)
fmt.Println("sendMeMail")
}
}
}()
rtr := mux.NewRouter()
rtr.HandleFunc("/", withPanic).Methods("GET")
http.Handle("/", rtr)
log.Println("Listening...")
http.ListenAndServe(":3001", http.DefaultServeMux)
}
func withPanic(w http.ResponseWriter, r *http.Request) {
panic("somewhere here will be panic, but I don't know where exactly")
}
You can wrap your handlers in a recovery middleware
package main
import (
"errors"
"github.com/gorilla/mux"
"log"
"net/http"
)
func main() {
m := mux.NewRouter()
m.Handle("/", RecoverWrap(http.HandlerFunc(handler))).Methods("GET")
http.Handle("/", m)
log.Println("Listening...")
http.ListenAndServe(":3001", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
panic(errors.New("panicing from error"))
}
func RecoverWrap(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
r := recover()
if r != nil {
var err error
switch t := r.(type) {
case string:
err = errors.New(t)
case error:
err = t
default:
err = errors.New("Unknown error")
}
sendMeMail(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}()
h.ServeHTTP(w, r)
})
}
func sendMeMail(err error) {
// send mail
}
You can take a a look at codahale recovery handler or negroni middleware for more details.
I believe that is what the gorilla recovery handler is for

gorilla mux router handlers

I can not get the gorilla mux to work..
When requesting http://www.localhost:9000 this is returned by the web server 404 page not found
But this works http://localhost:9000/ and prints Hello world
package main
import (
"net/http"
"fmt"
"log"
"github.com/gorilla/mux"
)
func Handler(w http.ResponseWriter, r *http.Request){
fmt.Fprint(w, "Hello world")
}
func main(){
r := mux.NewRouter()
r.Host("www.localhost")
r.HandleFunc("/", Handler)
err := http.ListenAndServe(":9000", r)
if err != nil {
log.Fatal("ListenAndServe error: ", err)
}
}
You want to be able to support both localhost and www.localhost
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello world")
}
func main() {
r := mux.NewRouter()
r.Host("www.localhost").Path("/").HandlerFunc(Handler)
r.HandleFunc("/", Handler)
err := http.ListenAndServe(":9000", r)
if err != nil {
log.Fatal("ListenAndServe error: ", err)
}
}
If you read the documentation carefully, you'll notice that r.Host() is just another pattern matching function. It doesn't set any global rule for that router.
if you want to make that rule to be inherited you'll need to use a subrouter:
subrouter := r.Host("www.localhost").Subrouter()
then you use "subrouter" in place of "r"

Creating http handler

I have main.go and I want changing to better structure(similarly main2.go ) due to if the proyect would grow up, would not be easy interpret for new developers the code.
My idea is create a folder called handle and put in it the file handle.go with all the handles methods, the trouble that I find is that I do not know how to set up http.HandleFunc("/R1", HandlerOne) and http.HandleFunc("/R2", HandlerTwo) in handler.go and call it from main2.go.
main.go
package main
import (
"fmt"
"net/http"
)
func HandlerOne(w http.ResponseWriter, req *http.Request) {
fmt.Println("message one")
}
func HandlerTwo(w http.ResponseWriter, req *http.Request) {
fmt.Println("message two")
}
func main() {
http.HandleFunc("/R1", HandlerOne)
http.HandleFunc("/R2", HandlerTwo)
err := http.ListenAndServe(":9998", nil)
if err != nil {
fmt.Printf("Server failed: ", err.Error())
}
}
main2.go
package main
import (
"fmt"
"net/http"
)
func main() {
// Call handle.go
err := http.ListenAndServe(":9998", nil)
if err != nil {
fmt.Printf("Server failed: ", err.Error())
}
}
handle.go
package handle
import (
"fmt"
"net/http"
)
func HandlerOne(w http.ResponseWriter, req *http.Request) {
fmt.Println("message one")
}
func HandlerTwo(w http.ResponseWriter, req *http.Request) {
fmt.Println("message two")
}
Note:
Thanks #kluyg for your answer. I going to add that i want that seems I do not explain very well:
That I want is create a function in
handle.go where can I can put all the mapping of the handles. i.e some like that
func SetUpMapping(...){
http.HandleFunc("/R1", HandlerOne)
http.HandleFunc("/R2", HandlerTwo)
..... //Others mapping
http.HandleFunc("/RN", HandlerN)
}
where ... should be one refence to http, but this is one package, and in the main something like code 3
code 3
main2.go
package main
import (
"fmt"
"net/http"
"handlers/handle"
)
func main() {
// Call handle.go
handle.SetUpMapping(...) // i dont know what parameter put here
err := http.ListenAndServe(":9998", nil)
if err != nil {
fmt.Printf("Server failed: ", err.Error())
}
}
I took your code and put main2.go into $GOPATH/handlers folder and handle.go in $GOPATH/handlers/handle folder. Then I changed main2.go to be
package main
import (
"fmt"
"net/http"
"handlers/handle"
)
func main() {
// Call handle.go
http.HandleFunc("/R1", handle.HandlerOne)
http.HandleFunc("/R2", handle.HandlerTwo)
err := http.ListenAndServe(":9998", nil)
if err != nil {
fmt.Printf("Server failed: ", err.Error())
}
}
It compiles and works fine. I believe this is what you asked for.
UPDATE
OK, you can add this to your handle.go
func SetUpMapping() {
http.HandleFunc("/R1", HandlerOne)
http.HandleFunc("/R2", HandlerTwo)
..... //Others mapping
http.HandleFunc("/RN", HandlerN)
}
then in main2.go
package main
import (
"fmt"
"net/http"
"handlers/handle"
)
func main() {
// Call handle.go
handle.SetUpMapping() // you don't need any parameters here
err := http.ListenAndServe(":9998", nil)
if err != nil {
fmt.Printf("Server failed: ", err.Error())
}
}

Resources