handlers.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. // Copyright 2013 The Gorilla Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package handlers
  5. import (
  6. "bufio"
  7. "fmt"
  8. "io"
  9. "net"
  10. "net/http"
  11. "net/url"
  12. "sort"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "unicode/utf8"
  17. )
  18. // MethodHandler is an http.Handler that dispatches to a handler whose key in the
  19. // MethodHandler's map matches the name of the HTTP request's method, eg: GET
  20. //
  21. // If the request's method is OPTIONS and OPTIONS is not a key in the map then
  22. // the handler responds with a status of 200 and sets the Allow header to a
  23. // comma-separated list of available methods.
  24. //
  25. // If the request's method doesn't match any of its keys the handler responds
  26. // with a status of HTTP 405 "Method Not Allowed" and sets the Allow header to a
  27. // comma-separated list of available methods.
  28. type MethodHandler map[string]http.Handler
  29. func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  30. if handler, ok := h[req.Method]; ok {
  31. handler.ServeHTTP(w, req)
  32. } else {
  33. allow := []string{}
  34. for k := range h {
  35. allow = append(allow, k)
  36. }
  37. sort.Strings(allow)
  38. w.Header().Set("Allow", strings.Join(allow, ", "))
  39. if req.Method == "OPTIONS" {
  40. w.WriteHeader(http.StatusOK)
  41. } else {
  42. http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  43. }
  44. }
  45. }
  46. // loggingHandler is the http.Handler implementation for LoggingHandlerTo and its
  47. // friends
  48. type loggingHandler struct {
  49. writer io.Writer
  50. handler http.Handler
  51. }
  52. // combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo
  53. // and its friends
  54. type combinedLoggingHandler struct {
  55. writer io.Writer
  56. handler http.Handler
  57. }
  58. func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  59. t := time.Now()
  60. logger := makeLogger(w)
  61. url := *req.URL
  62. h.handler.ServeHTTP(logger, req)
  63. writeLog(h.writer, req, url, t, logger.Status(), logger.Size())
  64. }
  65. func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  66. t := time.Now()
  67. logger := makeLogger(w)
  68. url := *req.URL
  69. h.handler.ServeHTTP(logger, req)
  70. writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size())
  71. }
  72. func makeLogger(w http.ResponseWriter) loggingResponseWriter {
  73. var logger loggingResponseWriter = &responseLogger{w: w, status: http.StatusOK}
  74. if _, ok := w.(http.Hijacker); ok {
  75. logger = &hijackLogger{responseLogger{w: w, status: http.StatusOK}}
  76. }
  77. h, ok1 := logger.(http.Hijacker)
  78. c, ok2 := w.(http.CloseNotifier)
  79. if ok1 && ok2 {
  80. return hijackCloseNotifier{logger, h, c}
  81. }
  82. if ok2 {
  83. return &closeNotifyWriter{logger, c}
  84. }
  85. return logger
  86. }
  87. type commonLoggingResponseWriter interface {
  88. http.ResponseWriter
  89. http.Flusher
  90. Status() int
  91. Size() int
  92. }
  93. // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP
  94. // status code and body size
  95. type responseLogger struct {
  96. w http.ResponseWriter
  97. status int
  98. size int
  99. }
  100. func (l *responseLogger) Header() http.Header {
  101. return l.w.Header()
  102. }
  103. func (l *responseLogger) Write(b []byte) (int, error) {
  104. size, err := l.w.Write(b)
  105. l.size += size
  106. return size, err
  107. }
  108. func (l *responseLogger) WriteHeader(s int) {
  109. l.w.WriteHeader(s)
  110. l.status = s
  111. }
  112. func (l *responseLogger) Status() int {
  113. return l.status
  114. }
  115. func (l *responseLogger) Size() int {
  116. return l.size
  117. }
  118. func (l *responseLogger) Flush() {
  119. f, ok := l.w.(http.Flusher)
  120. if ok {
  121. f.Flush()
  122. }
  123. }
  124. type hijackLogger struct {
  125. responseLogger
  126. }
  127. func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  128. h := l.responseLogger.w.(http.Hijacker)
  129. conn, rw, err := h.Hijack()
  130. if err == nil && l.responseLogger.status == 0 {
  131. // The status will be StatusSwitchingProtocols if there was no error and
  132. // WriteHeader has not been called yet
  133. l.responseLogger.status = http.StatusSwitchingProtocols
  134. }
  135. return conn, rw, err
  136. }
  137. type closeNotifyWriter struct {
  138. loggingResponseWriter
  139. http.CloseNotifier
  140. }
  141. type hijackCloseNotifier struct {
  142. loggingResponseWriter
  143. http.Hijacker
  144. http.CloseNotifier
  145. }
  146. const lowerhex = "0123456789abcdef"
  147. func appendQuoted(buf []byte, s string) []byte {
  148. var runeTmp [utf8.UTFMax]byte
  149. for width := 0; len(s) > 0; s = s[width:] {
  150. r := rune(s[0])
  151. width = 1
  152. if r >= utf8.RuneSelf {
  153. r, width = utf8.DecodeRuneInString(s)
  154. }
  155. if width == 1 && r == utf8.RuneError {
  156. buf = append(buf, `\x`...)
  157. buf = append(buf, lowerhex[s[0]>>4])
  158. buf = append(buf, lowerhex[s[0]&0xF])
  159. continue
  160. }
  161. if r == rune('"') || r == '\\' { // always backslashed
  162. buf = append(buf, '\\')
  163. buf = append(buf, byte(r))
  164. continue
  165. }
  166. if strconv.IsPrint(r) {
  167. n := utf8.EncodeRune(runeTmp[:], r)
  168. buf = append(buf, runeTmp[:n]...)
  169. continue
  170. }
  171. switch r {
  172. case '\a':
  173. buf = append(buf, `\a`...)
  174. case '\b':
  175. buf = append(buf, `\b`...)
  176. case '\f':
  177. buf = append(buf, `\f`...)
  178. case '\n':
  179. buf = append(buf, `\n`...)
  180. case '\r':
  181. buf = append(buf, `\r`...)
  182. case '\t':
  183. buf = append(buf, `\t`...)
  184. case '\v':
  185. buf = append(buf, `\v`...)
  186. default:
  187. switch {
  188. case r < ' ':
  189. buf = append(buf, `\x`...)
  190. buf = append(buf, lowerhex[s[0]>>4])
  191. buf = append(buf, lowerhex[s[0]&0xF])
  192. case r > utf8.MaxRune:
  193. r = 0xFFFD
  194. fallthrough
  195. case r < 0x10000:
  196. buf = append(buf, `\u`...)
  197. for s := 12; s >= 0; s -= 4 {
  198. buf = append(buf, lowerhex[r>>uint(s)&0xF])
  199. }
  200. default:
  201. buf = append(buf, `\U`...)
  202. for s := 28; s >= 0; s -= 4 {
  203. buf = append(buf, lowerhex[r>>uint(s)&0xF])
  204. }
  205. }
  206. }
  207. }
  208. return buf
  209. }
  210. // buildCommonLogLine builds a log entry for req in Apache Common Log Format.
  211. // ts is the timestamp with which the entry should be logged.
  212. // status and size are used to provide the response HTTP status and size.
  213. func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
  214. username := "-"
  215. if url.User != nil {
  216. if name := url.User.Username(); name != "" {
  217. username = name
  218. }
  219. }
  220. host, _, err := net.SplitHostPort(req.RemoteAddr)
  221. if err != nil {
  222. host = req.RemoteAddr
  223. }
  224. uri := req.RequestURI
  225. // Requests using the CONNECT method over HTTP/2.0 must use
  226. // the authority field (aka r.Host) to identify the target.
  227. // Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT
  228. if req.ProtoMajor == 2 && req.Method == "CONNECT" {
  229. uri = req.Host
  230. }
  231. if uri == "" {
  232. uri = url.RequestURI()
  233. }
  234. buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
  235. buf = append(buf, host...)
  236. buf = append(buf, " - "...)
  237. buf = append(buf, username...)
  238. buf = append(buf, " ["...)
  239. buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
  240. buf = append(buf, `] "`...)
  241. buf = append(buf, req.Method...)
  242. buf = append(buf, " "...)
  243. buf = appendQuoted(buf, uri)
  244. buf = append(buf, " "...)
  245. buf = append(buf, req.Proto...)
  246. buf = append(buf, `" `...)
  247. buf = append(buf, strconv.Itoa(status)...)
  248. buf = append(buf, " "...)
  249. buf = append(buf, strconv.Itoa(size)...)
  250. return buf
  251. }
  252. // writeLog writes a log entry for req to w in Apache Common Log Format.
  253. // ts is the timestamp with which the entry should be logged.
  254. // status and size are used to provide the response HTTP status and size.
  255. func writeLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
  256. buf := buildCommonLogLine(req, url, ts, status, size)
  257. buf = append(buf, '\n')
  258. w.Write(buf)
  259. }
  260. // writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
  261. // ts is the timestamp with which the entry should be logged.
  262. // status and size are used to provide the response HTTP status and size.
  263. func writeCombinedLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
  264. buf := buildCommonLogLine(req, url, ts, status, size)
  265. buf = append(buf, ` "`...)
  266. buf = appendQuoted(buf, req.Referer())
  267. buf = append(buf, `" "`...)
  268. buf = appendQuoted(buf, req.UserAgent())
  269. buf = append(buf, '"', '\n')
  270. w.Write(buf)
  271. }
  272. // CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
  273. // Apache Combined Log Format.
  274. //
  275. // See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
  276. //
  277. // LoggingHandler always sets the ident field of the log to -
  278. func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
  279. return combinedLoggingHandler{out, h}
  280. }
  281. // LoggingHandler return a http.Handler that wraps h and logs requests to out in
  282. // Apache Common Log Format (CLF).
  283. //
  284. // See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
  285. //
  286. // LoggingHandler always sets the ident field of the log to -
  287. //
  288. // Example:
  289. //
  290. // r := mux.NewRouter()
  291. // r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  292. // w.Write([]byte("This is a catch-all route"))
  293. // })
  294. // loggedRouter := handlers.LoggingHandler(os.Stdout, r)
  295. // http.ListenAndServe(":1123", loggedRouter)
  296. //
  297. func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
  298. return loggingHandler{out, h}
  299. }
  300. // isContentType validates the Content-Type header matches the supplied
  301. // contentType. That is, its type and subtype match.
  302. func isContentType(h http.Header, contentType string) bool {
  303. ct := h.Get("Content-Type")
  304. if i := strings.IndexRune(ct, ';'); i != -1 {
  305. ct = ct[0:i]
  306. }
  307. return ct == contentType
  308. }
  309. // ContentTypeHandler wraps and returns a http.Handler, validating the request
  310. // content type is compatible with the contentTypes list. It writes a HTTP 415
  311. // error if that fails.
  312. //
  313. // Only PUT, POST, and PATCH requests are considered.
  314. func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
  315. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  316. if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
  317. h.ServeHTTP(w, r)
  318. return
  319. }
  320. for _, ct := range contentTypes {
  321. if isContentType(r.Header, ct) {
  322. h.ServeHTTP(w, r)
  323. return
  324. }
  325. }
  326. http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
  327. })
  328. }
  329. const (
  330. // HTTPMethodOverrideHeader is a commonly used
  331. // http header to override a request method.
  332. HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
  333. // HTTPMethodOverrideFormKey is a commonly used
  334. // HTML form key to override a request method.
  335. HTTPMethodOverrideFormKey = "_method"
  336. )
  337. // HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for
  338. // the X-HTTP-Method-Override header or the _method form key, and overrides (if
  339. // valid) request.Method with its value.
  340. //
  341. // This is especially useful for HTTP clients that don't support many http verbs.
  342. // It isn't secure to override e.g a GET to a POST, so only POST requests are
  343. // considered. Likewise, the override method can only be a "write" method: PUT,
  344. // PATCH or DELETE.
  345. //
  346. // Form method takes precedence over header method.
  347. func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
  348. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  349. if r.Method == "POST" {
  350. om := r.FormValue(HTTPMethodOverrideFormKey)
  351. if om == "" {
  352. om = r.Header.Get(HTTPMethodOverrideHeader)
  353. }
  354. if om == "PUT" || om == "PATCH" || om == "DELETE" {
  355. r.Method = om
  356. }
  357. }
  358. h.ServeHTTP(w, r)
  359. })
  360. }