I am using socketio "github.com/googollee/go-socket.io" in Go.
For some reason, I have to make a new *socketio.server and register it to "/static/" which is already been registered with the old *socketio.server.
It means that I have to replace the handler ioserver in http.Handle("/socket.io/", ioserver) after the server has been started.
Below is part of my code, when something happens, I will create a new ioserver. I want the new ioserver to be the handler in http.Handle("/socket.io/", ioserver)
ioserver, _ = socketio.NewServer(nil)
http.Handle("/socket.io/", ioserver)
log.Fatal(http.ListenAndServe(":81", nil))
An application cannot swap handlers in the mux, but it can write a handler that swaps between other handlers:
type swapper struct {
mu sync.Mutex
h http.Handler
}
func (s *swapper) setHandler(h http.Handler) {
s.mu.Lock()
s.h = h
s.mu.Unlock()
}
func (w *swapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.mu.Lock()
h := s.h
s.mu.Unlock()
h.ServeHTTP(w, r)
}
Register it like this:
ioserver, _ = socketio.NewServer(nil)
s := &swapper{}
s.setHandler(ioserver)
http.Handle("/socket.io/", s)
log.Fatal(http.ListenAndServe(":81", nil))
At any time, the application can call s.setHandler to swap in a new handler.
Related
As I know, every requests to the server creates new goroutine. For ex (probably incorrect code, but this topic is not about it):
package main
import "net/http"
var exampleMap map[string]string
func handlerPost(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
{
exampleMap["test"] = test // Must I syncrhonise this writing?
}
case "GET":
{
if v, ok := exampleMap["test"] { // And what about reading?
fmt.Println(v)
}
}
}
}
func main() {
http.HandleFunc("/", handlerPost)
http.ListenAndServe(":8080", nil)
}
Does it mean that its unsafe to do it like this and I have to use sync.Map (for example), and what about if instead map here was a database queries? What can I do in this case. Thank you!
exampleMap is shared among goroutines, so you have to synchronize access to it. A mutex would do, a RWMutex would perform better:
var exampleMap map[string]string
var exampleMutex sync.RWMutex
...
exampleMutex.Lock()
exampleMap["test"] = test
exampleMutex.Unlock()
...
exampleMutex.RLock()
v, ok := exampleMap["test"]
exampleMutex.RUnlock()
if ok {
...
}
I am a bit lost with go-routines, http server, handlers and pointers.
I ve created a http server that accepts any request at any path, I am starting it in separate routine so main thread is not blocked
// main thread
// I want to bind to random port and read port later
listener, err := net.Listen("tcp", ":0")
rh := requestHandler{} // look at point 2)
s := &http.Server{
Handler: rh,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
go func() {
err := s.Serve(listener)
...
}()
rh is a Handler interface so I ve created a struct following the interface
type requestHandler struct {
values []string
}
func (rh requestHandler) ServeHTTP(w http.ResponseWriter, givenRequest *http.Request) {
log.Println("DEBUG : handling request", givenRequest.Method, givenRequest.URL, givenRequest.RequestURI)
log.Println("DEBUG : headers", givenRequest.Header)
for _, value := range rh.values { // <----- values are always empty
}
}
lets add some values to handler
// main thread
rh.values = append(rh.values, "one)
obviously this doesn't work as Server is copying Handler and rh variable from main thread has different pointer than s.Handler, but this is OK.
What I dont understand is that also rh in func (rh requestHandler) ServeHTTP(w http.ResponseWriter, givenRequest *http.Request) has different pointer than s.Handler.
how can I pass anything to requestHandler.values from main thread so those values can be read in callback function ? I ve tried using channels but I cannot access pointer/ref to right rh. Is it because each request is also spawn in new routine and handler is also copied ?
sample code
type MyServer struct {
Port int
pid *http.Server
requestHandler requestHandler
}
func (s *MyServer) addValue(value string) {
s.requestHandler.values = append(s.requestHandler.values, value)
}
func InitializeServer() *MyServer {
rh := requestHandler{}
listener := bindToNextFreePort()
s := &http.Server{
Handler: rh,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
startServer(s, listener)
currentPort := getTcpPort(listener)
log.Println("My Server Initialized on ", currentPort)
return &MyServer{
pid: s,
Port: currentPort,
requestHandler: rh,
}
}
func startServer(s *http.Server, listener net.Listener) {
go func() {
log.Println("starting Mock Server")
err := s.Serve(listener)
}()
}
type requestHandler struct {
values []string
}
func (rh requestHandler) ServeHTTP(w http.ResponseWriter, givenRequest *http.Request) {
log.Println("DEBUG : handling request", givenRequest.Method, givenRequest.URL, givenRequest.RequestURI)
log.Println("DEBUG : headers", givenRequest.Header)
for key, value := range rh.values {
}
}
func bindToNextFreePort() net.Listener {
listener, err := net.Listen("tcp", ":0")
if err != nil {
panic(err)
}
return listener
}
It turned out to be quite simple, I've closed my eyes for this assignment here
return &MyServer{
pid: s,
Port: currentPort,
requestHandler: rh,
}
Which led to yet another copy.
I want to populate the logging context by items in the request, for example: r.Header.Get("X-Request-Id"). I assumed I could override the Log type in the handler from middleware. Though it doesn't seem to work and I am not sure why!
package main
import (
"fmt"
"net/http"
"os"
"github.com/apex/log"
"github.com/gorilla/mux"
)
// Assumption: handler is the shared state between the functions
type handler struct{ Log *log.Entry }
// New creates a handler for this application to co-ordinate shared resources
func New() (h handler) { return handler{Log: log.WithFields(log.Fields{"test": "FAIL"})} }
func (h handler) index(w http.ResponseWriter, r *http.Request) {
h.Log.Info("Hello from the logger")
fmt.Fprint(w, "YO")
}
func main() {
h := New()
app := mux.NewRouter()
app.HandleFunc("/", h.index)
app.Use(h.loggingMiddleware)
if err := http.ListenAndServe(":"+os.Getenv("PORT"), app); err != nil {
log.WithError(err).Fatal("error listening")
}
}
func (h handler) loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.Log = log.WithFields(log.Fields{"test": "PASS"})
next.ServeHTTP(w, r)
})
}
Can you see why h.Log = log.WithFields(log.Fields{"test": "PASS"}) doesn't seem to have any effect on h.Log.Info("Hello from the logger") which should be IIUC within the same request?
You need your logger to be request-scoped. You're setting it globally for the entire handler, every time a new connection comes in, which means you're asking for data races, and generally undesirable behavior.
For request-scoped context, the context.Context embedded in the request is perfect. You can access it through the Context() and WithContext methods.
Example:
var loggerKey = "Some unique key"
func (h handler) loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, loggerKey, log.WithFields(log.Fields{"test": "PASS"}))
next.ServeHTTP(w, r.WithContext(ctx)
})
}
Then to access your logger:
func doSomething(r *http.Request) error {
log, ok := r.Context().Value(loggerKey).(*log.Logger) // Or whatever type is appropriate
if !ok {
return errors.New("logger not set on context!")
}
// Do stuff...
}
I have an Handler like this:
type handler struct {
Services *domain.Services
Config *domain.Config
}
And then, a lot of new types (they can be twenty or more), like this:
type Handler1 handler
type Handler2 handler
And each one has a ServeHTTP method. And I use this so they can access the Services and Config variables.
They are being used in routes like this:
r.Handle("/login", &h.Handler1{
Services: s,
Config: c,
})
My question is: should I create all of this structs or just create a function that injects the Services and Config into the request Context and then I access them using r.Context().Value()?
I thought about doing this:
func handler1(w http.ResponseWriter, r *http.Request) {
s, c := r.Context().Value("services"), r.Context().Value("config")
// My code
}
r.HandleFunc("/login", inject(handler1, s, c))
What's the best/recommended?
As an alternative to creating all these handler types, you can have functions which return other functions (http.HandlerFunc). This way, you will create a closure and can access the parameters when the request arrives in the handler. For example:
package main
import (
"fmt"
"net/http"
)
func SomeHandler(conf SomeConfig, service SomeService) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "foobar config: %q", conf)
}
}
func main() {
// TODO: initialise your conf & service
http.HandleFunc("/somepath", SomeHandler(conf, service))
}
You probably could create some kind of Router which will provide ServeHTTP and do mapping between your real handlers and route paths.
Something like this:
package main
import "net/http"
type Router struct {
routes map[string]func(rw http.ResponseWriter, r *http.Request)
}
func NewRouter() *Router {
var r Router
r.routes = make(map[string]func(rw http.ResponseWriter, r *http.Request))
return &r
}
func (router *Router) addRoute(path string, f func(rw http.ResponseWriter, r *http.Request)) {
router.routes[path] = f
}
func (router *Router) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
for route, serveHTTP := range router.routes {
if route == r.URL.Path {
serveHTTP(rw, r)
return
}
}
rw.WriteHeader(http.StatusNotFound)
}
func teapot(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusTeapot)
}
func ok(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusOK)
}
func main() {
r := NewRouter()
r.addRoute("/teapot", teapot)
r.addRoute("/ok", ok)
http.ListenAndServe("localhost:8080", r)
}
How is the following function implemented?
func handle(pattern string, handler interface{}) {
// ... what goes here? ...
http.Handle(pattern, ?)
}
handle("/foo", func(w http.ResponseWriter, r http.Request) { io.WriteString(w, "foo") }
handle("/bar", BarHandler{})
handle() is passed either a function which matches the type of http.HandlerFunc or a type which implements the http.Handler interface.
Instead of resorting to reflection, I would do it this way:
func handle(pattern string, handler interface{}) {
var h http.Handler
switch handler := handler.(type) {
case http.Handler:
h = handler
case func(http.ResponseWriter, *http.Request):
h = http.HandlerFunc(handler)
default:
// error
}
http.Handle(pattern, h)
}
First we need to introduce the term "reflections" in Java/C#'s terminology, RTTI in C++'s terminology. It's quite simple actually. The compiler keeps data to find out what is the type of an instance var i SomeType during runtime. Go supports reflection, and that's how it finds out what's the type of handler during runtime.
The handle function uses the reflection. A crude example
package main
import ("reflect";"http")
type fakeHandler struct{}
func (frw *fakeHandler) ServeHTTP(http.ResponseWriter, *http.Request) {}
func handle(pattern string, handler interface{}) {
handlerInterface := reflect.TypeOf(new(http.Handler)).Elem()
handlerFunction := reflect.TypeOf(new(http.HandlerFunc)).Elem()
t := reflect.TypeOf(handler)
if t.Implements(handlerInterface) {fmt.Println("http.Handler")}
//http.HandlerFunc is a different type than
// func(http.ResponseWriter, *http.Request), but we can do
// var hf HandlerFunc = func(http.ResponseWriter, *http.Request){}
if t.AssignableTo(handlerFunction) {fmt.Println("http.HandleFunc")}
}
func f(http.ResponseWriter, *http.Request) {}
func main() {
handle("",&fakeHandler{})
handle("",f)
}