4

I'm trying to set Server Header on every Response. I'm trying to achieve this, using a Middleware for Gin. However, this does not set the Header, for some reason. I've tried to debug this so far, and I could not understand why this should not work. Probably I'm missing something here.

Here is the code

package main

import "fmt"
import "github.com/gin-gonic/gin"

const SERVER_INFO = "Some-Play-Server"

type ServerHeader struct {
    gin.ResponseWriter
    ServerInfo string
}

func (w *ServerHeader) Write(data []byte) (int, error) {
    if w.Header().Get("Server") == "" {
        w.Header().Add("Server", w.ServerInfo)
    }

    return w.ResponseWriter.Write(data)
}

func InitServerHeader() gin.HandlerFunc {
    return func(c *gin.Context) {
        writer := &ServerHeader{c.Writer, SERVER_INFO}
        c.Writer = writer
        c.Next()
    }
}

func main() {
    mux := gin.Default()
    mux.Use(InitServerHeader())
    mux.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })

    fmt.Println("Server Listening on 0.0.0.0:8080")
    mux.Run(":8080")
}

And, here is the Test Output

❯ curl -v http://localhost:8080/
* About to connect() to localhost port 8080 (#0)
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Date: Wed, 13 Aug 2014 16:54:21 GMT
< Content-Length: 2
< 
OK

4 Answers 4

5

here Gin's creator: DO NOT override the c.Writer, that's too much overhead and uneeded complexity. The writer should be overrided if you want to support transparent gzip compression or caching (see gzip middleware). If you want to add a header just do this:

func main() {
    mux := gin.Default()
    mux.Use(serverHeader)
    mux.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })    
    mux.Run(":8080")
}

func serverHeader(c *gin.Context) {
    // shortcut for c.Writer.Header().Set("Server", "Some-Play-Server")
    c.Header("Server", "Some-Play-Server")
}

Done! since it is attached as a global middleware, all the responses will include the Server header, even when serving static files!

Sign up to request clarification or add additional context in comments.

Comments

3

You did not use the right method for that ..

package main

import "fmt"
import "github.com/gin-gonic/gin"

const SERVER_INFO = "Some-Play-Server"

type ServerHeader struct {
    gin.ResponseWriter
    ServerInfo string
}

func (w *ServerHeader) WriteHeader(code int) {
    if w.Header().Get("Server") == "" {
        w.Header().Add("Server", w.ServerInfo)
    }

    w.ResponseWriter.WriteHeader(code)
}

func InitServerHeader() gin.HandlerFunc {
    return func(c *gin.Context) {
        writer := &ServerHeader{c.Writer, SERVER_INFO}
        c.Writer = writer
        c.Next()
    }
}

func main() {
    mux := gin.Default()
    mux.Use(InitServerHeader())
    mux.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })

    fmt.Println("Server Listening on 0.0.0.0:8080")
    mux.Run(":8080")
}

Here is the Output

$ curl -v 'http://localhost:8080/'
* About to connect() to localhost port 8080 (#0)
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Server: Some-Play-Server
< Date: Thu, 14 Aug 2014 00:41:27 GMT
< Content-Length: 2
< 
OK

Comments

3

you do not have to do that. You just have to do this:

func main() {
    mux := gin.Default()
    mux.Use(func(c *gin.Context) {
        c.Writer.Header().Set("Server", "Some-Play-Server")
    })

    mux.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })    
    mux.Run(":8080")
}

Also, check out the last version (develop branch), we improved it a lot. Now gin automatically defers the call to WriteHeader() so you do not have to worry.

1 Comment

in fact, Gin includes a shortcut method: c.Header("Server", "Some-Play-Server")
2

I'm not familiar with gin, however using the built in http server it's rather trivial to do that:

const SERVER_INFO = "Some-Play-Server"
var extra = map[string]string{
    "Server": SERVER_INFO,
}

func HeaderSetter(fn func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
    return func(rw http.ResponseWriter, req *http.Request) {
        for k, v := range extra {
            rw.Header().Set(k, v)
        }
        fn(rw, req)
    }
}

func main() {
    fn := func(rw http.ResponseWriter, req *http.Request) {
        io.WriteString(rw, "Hello: "+req.URL.String()+"\n")
    }
    http.HandleFunc("/", HeaderSetter(fn))
    log.Panic(http.ListenAndServe(":9020", nil))
}

A different approach was mentioned by @elithrar in the comments is to return http.Handler instead:

func HeaderSetter(fn http.Handler) http.Handler {
    return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
        for k, v := range extra {
            rw.Header().Set(k, v)
        }
        fn(rw, req)
    })
}

playground

2 Comments

You can make this more general purpose by having it accept http.Handler and return http.Handler - which allows the middleware to work on any custom handler types you might have: play.golang.org/p/TMaIrIPzID
@elithrar I added it to the answer with full attribution to you of course, good catch.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.