/vendor/github.com/vulcand/vulcand/plugin/rewrite/rewrite.go

https://github.com/EmileVauge/traefik · Go · 206 lines · 163 code · 37 blank · 6 comment · 18 complexity · 89c95c09042bfd89f31c705e256d7b85 MD5 · raw file

  1. package rewrite
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "net/url"
  8. "regexp"
  9. "strconv"
  10. "strings"
  11. log "github.com/Sirupsen/logrus"
  12. "github.com/codegangsta/cli"
  13. "github.com/vulcand/oxy/utils"
  14. "github.com/vulcand/vulcand/plugin"
  15. )
  16. const Type = "rewrite"
  17. type Rewrite struct {
  18. Regexp string
  19. Replacement string
  20. RewriteBody bool
  21. Redirect bool
  22. }
  23. func NewRewrite(regex, replacement string, rewriteBody, redirect bool) (*Rewrite, error) {
  24. return &Rewrite{regex, replacement, rewriteBody, redirect}, nil
  25. }
  26. func (rw *Rewrite) NewHandler(next http.Handler) (http.Handler, error) {
  27. return newRewriteHandler(next, rw)
  28. }
  29. func (rw *Rewrite) String() string {
  30. return fmt.Sprintf("regexp=%v, replacement=%v, rewriteBody=%v, redirect=%v",
  31. rw.Regexp, rw.Replacement, rw.RewriteBody, rw.Redirect)
  32. }
  33. type rewriteHandler struct {
  34. next http.Handler
  35. errHandler utils.ErrorHandler
  36. regexp *regexp.Regexp
  37. replacement string
  38. rewriteBody bool
  39. redirect bool
  40. }
  41. func newRewriteHandler(next http.Handler, spec *Rewrite) (*rewriteHandler, error) {
  42. re, err := regexp.Compile(spec.Regexp)
  43. if err != nil {
  44. return nil, err
  45. }
  46. return &rewriteHandler{
  47. regexp: re,
  48. replacement: spec.Replacement,
  49. rewriteBody: spec.RewriteBody,
  50. redirect: spec.Redirect,
  51. next: next,
  52. errHandler: utils.DefaultHandler,
  53. }, nil
  54. }
  55. func (rw *rewriteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  56. oldURL := rawURL(req)
  57. // only continue if the Regexp param matches the URL
  58. if !rw.regexp.MatchString(oldURL) {
  59. rw.next.ServeHTTP(w, req)
  60. return
  61. }
  62. // apply a rewrite regexp to the URL
  63. newURL := rw.regexp.ReplaceAllString(oldURL, rw.replacement)
  64. // replace any variables that may be in there
  65. rewrittenURL := &bytes.Buffer{}
  66. if err := ApplyString(newURL, rewrittenURL, req); err != nil {
  67. rw.errHandler.ServeHTTP(w, req, err)
  68. return
  69. }
  70. // parse the rewritten URL and replace request URL with it
  71. parsedURL, err := url.Parse(rewrittenURL.String())
  72. if err != nil {
  73. rw.errHandler.ServeHTTP(w, req, err)
  74. return
  75. }
  76. if rw.redirect && newURL != oldURL {
  77. (&redirectHandler{u: parsedURL}).ServeHTTP(w, req)
  78. return
  79. }
  80. req.URL = parsedURL
  81. // make sure the request URI corresponds the rewritten URL
  82. req.RequestURI = req.URL.RequestURI()
  83. if !rw.rewriteBody {
  84. rw.next.ServeHTTP(w, req)
  85. return
  86. }
  87. bw := &bufferWriter{header: make(http.Header), buffer: &bytes.Buffer{}}
  88. newBody := &bytes.Buffer{}
  89. rw.next.ServeHTTP(bw, req)
  90. if err := Apply(bw.buffer, newBody, req); err != nil {
  91. log.Errorf("Failed to rewrite response body: %v", err)
  92. return
  93. }
  94. utils.CopyHeaders(w.Header(), bw.Header())
  95. w.Header().Set("Content-Length", strconv.Itoa(newBody.Len()))
  96. w.WriteHeader(bw.code)
  97. io.Copy(w, newBody)
  98. }
  99. func FromOther(rw Rewrite) (plugin.Middleware, error) {
  100. return NewRewrite(rw.Regexp, rw.Replacement, rw.RewriteBody, rw.Redirect)
  101. }
  102. func FromCli(c *cli.Context) (plugin.Middleware, error) {
  103. return NewRewrite(c.String("regexp"), c.String("replacement"), c.Bool("rewriteBody"), c.Bool("redirect"))
  104. }
  105. func GetSpec() *plugin.MiddlewareSpec {
  106. return &plugin.MiddlewareSpec{
  107. Type: Type,
  108. FromOther: FromOther,
  109. FromCli: FromCli,
  110. CliFlags: CliFlags(),
  111. }
  112. }
  113. func CliFlags() []cli.Flag {
  114. return []cli.Flag{
  115. cli.StringFlag{
  116. Name: "regexp",
  117. Usage: "regex to match against http request path",
  118. },
  119. cli.StringFlag{
  120. Name: "replacement",
  121. Usage: "replacement text into which regex expansions are inserted",
  122. },
  123. cli.BoolFlag{
  124. Name: "rewriteBody",
  125. Usage: "if provided, response body is treated as as template and all variables in it are replaced",
  126. },
  127. cli.BoolFlag{
  128. Name: "redirect",
  129. Usage: "if provided, request is redirected to the rewritten URL",
  130. },
  131. }
  132. }
  133. func rawURL(request *http.Request) string {
  134. scheme := "http"
  135. if request.TLS != nil || isXForwardedHTTPS(request) {
  136. scheme = "https"
  137. }
  138. return strings.Join([]string{scheme, "://", request.Host, request.RequestURI}, "")
  139. }
  140. func isXForwardedHTTPS(request *http.Request) bool {
  141. xForwardedProto := request.Header.Get("X-Forwarded-Proto")
  142. return len(xForwardedProto) > 0 && xForwardedProto == "https"
  143. }
  144. type redirectHandler struct {
  145. u *url.URL
  146. }
  147. func (f *redirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  148. w.Header().Set("Location", f.u.String())
  149. w.WriteHeader(http.StatusFound)
  150. w.Write([]byte(http.StatusText(http.StatusFound)))
  151. }
  152. type bufferWriter struct {
  153. header http.Header
  154. code int
  155. buffer *bytes.Buffer
  156. }
  157. func (b *bufferWriter) Close() error {
  158. return nil
  159. }
  160. func (b *bufferWriter) Header() http.Header {
  161. return b.header
  162. }
  163. func (b *bufferWriter) Write(buf []byte) (int, error) {
  164. return b.buffer.Write(buf)
  165. }
  166. // WriteHeader sets rw.Code.
  167. func (b *bufferWriter) WriteHeader(code int) {
  168. b.code = code
  169. }