2016-07-12 10 views
0

Nehmen wir an, ich habe eine Middleware in Go, wo ich alle vorhandenen Server Header mit meinem eigenen Wert überschreiben will.Kontrolle HTTP-Header von äußeren Go Middleware

// Server attaches a Server header to the response. 
func Server(h http.Handler, serverName string) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     w.Header().Set("Server", serverName) 
     h.ServeHTTP(w, r) 
    }) 
} 

Dann füge ich es auf eine Antwort Kette wie diese

http.Handle("/", authHandler) 
http.ListenAndServe(":8000", Server(http.DefaultServeMux, "myapp")) 

Leider, wenn die authHandler oder durch DefaultServeMux behandelt etwas nennt w.Header().Add("Server", "foo") (wie, sagen wir, httputil.ReverseProxy.ServeHTTP), I mit zwei Server am Ende Header in der Antwort.

$ http localhost:5962/v1/hello_world 
HTTP/1.1 200 OK 
Content-Length: 11 
Content-Type: text/plain 
Date: Tue, 12 Jul 2016 04:54:04 GMT 
Server: inner-middleware 
Server: myapp 

Was ich wirklich möchte etwas wie folgt aus:

// Server attaches a Server header to the response. 
func Server(h http.Handler, serverName string) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     h.ServeHTTP(w, r) 
     w.Header().Set("Server", serverName) 
    }) 
} 

Dies ist jedoch disallowed by the semantics of ServeHTTP:

ServeHTTP sollte Antwort-Header und Daten an die ResponseWriter und dann wieder schreiben. Rückgabe von Signalen, dass die Anfrage beendet ist; Es ist nicht zulässig, den AntwortWriter zu verwenden oder von der Request.Body nach oder gleichzeitig mit dem Abschluss des ServeHTTP-Aufrufs zu lesen.

Ich habe einige ziemlich hässlich Hacks um dies zu sehen, zum Beispiel den Code in httputil.ReverseProxy.ServeHTTP, die Kopien von Kopf- und umschreibt den Antwortcode oder mit httptest.ResponseRecorder, die den ganzen Körper in ein Byte-Puffer liest.

Ich könnte auch die traditionelle Middleware-Reihenfolge umkehren und meine Server-Middleware zuletzt, irgendwie, oder machen es die innerste Middleware.

Gibt es eine einfache Methode, die ich vermisse?

Antwort

1

Sie können einen benutzerdefinierten Typ definieren, die eine ResponseWriter wickelt und fügt den Server-Header, kurz bevor alle Header, auf Kosten einer zusätzlichen Schicht Indirektionsebene geschrieben . Hier ist ein Beispiel:

type serverWriter struct { 
    w   http.ResponseWriter 
    name  string 
    wroteHeader bool 
} 

func (s serverWriter) WriteHeader(code int) { 
    if s.wroteHeader == false { 
     s.w.Header().Set("Server", s.name) 
     s.wroteHeader = true 
    } 
    s.w.WriteHeader(code) 
} 

func (s serverWriter) Write(b []byte) (int, error) { 
    return s.w.Write(b) 
} 

func (s serverWriter) Header() http.Header { 
    return s.w.Header() 
} 

// Server attaches a Server header to the response. 
func Server(h http.Handler, serverName string) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     sw := serverWriter{ 
      w:   w, 
      name:  serverName, 
      wroteHeader: false, 
     } 
     h.ServeHTTP(sw, r) 
    }) 
} 
+0

Dies ist ein perfektes Beispiel für Overengineering. Wirklich alles, was Sie tun müssen, ist die Stelle zu ändern, an der der Header geschrieben wird, und sie vor dem Aufruf von WriteHeader() zu schreiben. – dlsniper

+0

Ich bin wirklich verwirrt von Ihrem Kommentar. Es ist unmöglich, eine interne Middleware von einer äußeren Middleware zu überschreiben. In diesem Fall rufe ich httputil.ReverseProxy.ServeHTTP zum Proxy an, den ich nicht kontrolliere. ReverseProxy.ServeHTTP ruft WriteHeader auf, was auch immer der Proxy zurückgibt; Ich kann das nicht kontrollieren. –

+0

@Dlsniper müssen Sie die Frage mein Freund erneut lesen – Kugel

1

Nach der Verwendung von WriteHeader (Status) ist es nicht möglich, Header zu ändern (hinzufügen/entfernen/ändern).

Damit dies funktioniert, Änderung:

// Server attaches a Server header to the response. 
func Server(h http.Handler, serverName string) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
    w.Header().Set("Server", serverName) 
    h.ServeHTTP(w, r) 
    }) 
} 
+0

Entschuldigung - ich sehe nicht, wie unterscheidet sich das von meinem ersten Code-Snippet? –

+0

Die Position von write header vs h.ServeHTTP, diff den Code. – dlsniper

+0

Ah - Ich habe zwei Code-Schnipsel in meiner Frage, die erste entspricht, was Sie geschrieben haben, die zweite ist ein Beispiel für das, was ich tun möchte, und ich stelle fest, dass es falsch ist. –