Go: How to combine two (or more) http.ServeMux?
Asked Answered
R

3

8

Given that you have two instances of http.ServeMux, and you wish for them to be served at the same port number, like so:

    muxA, muxB http.ServeMux
    //initialise muxA
    //initialise muxB
    combinedMux := combineMux([muxA, muxB])
    http.ListenAndServe(":8080", combinedMux)

How would one go about writing the combinedMux function, as described above?

... or is there an alternative way to accomplish the same thing?

Randers answered 16/5, 2014 at 5:57 Comment(2)
Can you give a little info on what the two ServeMux do?can they run one after another? Why two muxes instead of 1 with multiple handlers?Dang
@tony: 2 handlers could work too. One is provided by martini and the other by socket.io. A Martini struct implements the interface of http.HandlerFunc, and a SocketIOServer extends *http.ServeMux - essentially what I'm trying to do is serve both from the same server & port.Randers
H
27

Because an http.ServeMux is also an http.Handler you can easily nest one mux inside another, even on the same port and same hostname. Here's one example of doing that:

rootMux := http.NewServeMux()
subMux := http.NewServeMux()

// This will end up handling /top_path/sub_path
subMux.HandleFunc("/sub_path", myHandleFunc)

// Use the StripPrefix here to make sure the URL has been mapped
// to a path the subMux can read
rootMux.Handle("/top_path/", http.StripPrefix("/top_path", subMux))

http.ListenAndServe(":8000", rootMux)

Note that without that http.StripPrefix() call, you would need to handle the whole path in the lower mux.

Historic answered 12/4, 2017 at 21:34 Comment(2)
Can you do the same if root mux is a Gorilla Mux? rootMux := mux.NewRouter()Swept
Great answer. Perfect for mixing microservicesProviso
S
4

The SeverMux type is itself a http.Handler, therefore you can nest them easily. For example:

mux := NewServeMux()
mux.AddHandler("server1.com:8080/", muxA)
mux.AddHandler("server2.com:8080/", muxB)

I'm not quite sure what you mean exactly with "combining" them. If you want to try one handler first and then another one in case of 404, you might do it like this (untested):

mux := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    rec := httptest.NewRecorder()
    rec.Body = &bytes.Buffer{}
    muxA.ServeHTTP(rec, r)
    if rec.Code == 404 {
        muxB.ServeHTTP(w, r)
        return
    }
    for key, val := range rec.HeaderMap {
        w.Header().Set(key, val)
    }
    w.WriteHeader(rec.Code)
    rec.Body.WriteTo(w)
})

This has obviously some disadvantages like storing the whole response in memory. Alternatively, if you don't mind calling your handler twice, you can also set rec.Body = nil, check just rec.Code and call muxA.ServeHTTP(w, r) again in case of success. But it's probably better to restructure your application so that the first approach (nested ServerMux) is sufficient.

Select answered 16/5, 2014 at 14:58 Comment(1)
For the first code block you posted, would that work if each of muxA and muxB were to listen on the same server and port, but the paths that they handle are different?Randers
T
2

Check example below

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    userServer := http.NewServeMux()
    userServer.HandleFunc("/test", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "Hello from user server")
    })

    postServer := http.NewServeMux()
    postServer.HandleFunc("/test", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "Hello from post server")
    })

    server := http.NewServeMux()
    server.Handle("/user/", http.StripPrefix("/user", userServer))
    server.Handle("/post/", http.StripPrefix("/post", postServer))

    log.Fatal(http.ListenAndServe(":8080", server))
}
Tuscarora answered 23/2 at 15:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.