You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

200 lines
4.8 KiB

  1. // Copyright 2015 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package martianhttp provides HTTP handlers for managing the state of a martian.Proxy.
  15. package martianhttp
  16. import (
  17. "bytes"
  18. "encoding/json"
  19. "io/ioutil"
  20. "net/http"
  21. "sync"
  22. "github.com/google/martian"
  23. "github.com/google/martian/log"
  24. "github.com/google/martian/parse"
  25. "github.com/google/martian/verify"
  26. )
  27. var noop = martian.Noop("martianhttp.Modifier")
  28. // Modifier is a locking modifier that is configured via http.Handler.
  29. type Modifier struct {
  30. mu sync.RWMutex
  31. config []byte
  32. reqmod martian.RequestModifier
  33. resmod martian.ResponseModifier
  34. }
  35. // NewModifier returns a new martianhttp.Modifier.
  36. func NewModifier() *Modifier {
  37. return &Modifier{
  38. reqmod: noop,
  39. resmod: noop,
  40. }
  41. }
  42. // SetRequestModifier sets the request modifier.
  43. func (m *Modifier) SetRequestModifier(reqmod martian.RequestModifier) {
  44. m.mu.Lock()
  45. defer m.mu.Unlock()
  46. m.setRequestModifier(reqmod)
  47. }
  48. func (m *Modifier) setRequestModifier(reqmod martian.RequestModifier) {
  49. if reqmod == nil {
  50. reqmod = noop
  51. }
  52. m.reqmod = reqmod
  53. }
  54. // SetResponseModifier sets the response modifier.
  55. func (m *Modifier) SetResponseModifier(resmod martian.ResponseModifier) {
  56. m.mu.Lock()
  57. defer m.mu.Unlock()
  58. m.setResponseModifier(resmod)
  59. }
  60. func (m *Modifier) setResponseModifier(resmod martian.ResponseModifier) {
  61. if resmod == nil {
  62. resmod = noop
  63. }
  64. m.resmod = resmod
  65. }
  66. // ModifyRequest runs reqmod.
  67. func (m *Modifier) ModifyRequest(req *http.Request) error {
  68. m.mu.RLock()
  69. defer m.mu.RUnlock()
  70. return m.reqmod.ModifyRequest(req)
  71. }
  72. // ModifyResponse runs resmod.
  73. func (m *Modifier) ModifyResponse(res *http.Response) error {
  74. m.mu.RLock()
  75. defer m.mu.RUnlock()
  76. return m.resmod.ModifyResponse(res)
  77. }
  78. // VerifyRequests verifies reqmod, iff reqmod is a RequestVerifier.
  79. func (m *Modifier) VerifyRequests() error {
  80. m.mu.RLock()
  81. defer m.mu.RUnlock()
  82. if reqv, ok := m.reqmod.(verify.RequestVerifier); ok {
  83. return reqv.VerifyRequests()
  84. }
  85. return nil
  86. }
  87. // VerifyResponses verifies resmod, iff resmod is a ResponseVerifier.
  88. func (m *Modifier) VerifyResponses() error {
  89. m.mu.RLock()
  90. defer m.mu.RUnlock()
  91. if resv, ok := m.resmod.(verify.ResponseVerifier); ok {
  92. return resv.VerifyResponses()
  93. }
  94. return nil
  95. }
  96. // ResetRequestVerifications resets verifications on reqmod, iff reqmod is a
  97. // RequestVerifier.
  98. func (m *Modifier) ResetRequestVerifications() {
  99. m.mu.Lock()
  100. defer m.mu.Unlock()
  101. if reqv, ok := m.reqmod.(verify.RequestVerifier); ok {
  102. reqv.ResetRequestVerifications()
  103. }
  104. }
  105. // ResetResponseVerifications resets verifications on resmod, iff resmod is a
  106. // ResponseVerifier.
  107. func (m *Modifier) ResetResponseVerifications() {
  108. m.mu.Lock()
  109. defer m.mu.Unlock()
  110. if resv, ok := m.resmod.(verify.ResponseVerifier); ok {
  111. resv.ResetResponseVerifications()
  112. }
  113. }
  114. // ServeHTTP sets or retrieves the JSON-encoded modifier configuration
  115. // depending on request method. POST requests are expected to provide a JSON
  116. // modifier message in the body which will be used to update the contained
  117. // request and response modifiers. GET requests will return the JSON
  118. // (pretty-printed) for the most recent configuration.
  119. func (m *Modifier) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
  120. switch req.Method {
  121. case "POST":
  122. m.servePOST(rw, req)
  123. return
  124. case "GET":
  125. m.serveGET(rw, req)
  126. return
  127. default:
  128. rw.Header().Set("Allow", "GET, POST")
  129. rw.WriteHeader(405)
  130. }
  131. }
  132. func (m *Modifier) servePOST(rw http.ResponseWriter, req *http.Request) {
  133. body, err := ioutil.ReadAll(req.Body)
  134. if err != nil {
  135. http.Error(rw, err.Error(), 500)
  136. log.Errorf("martianhttp: error reading request body: %v", err)
  137. return
  138. }
  139. req.Body.Close()
  140. r, err := parse.FromJSON(body)
  141. if err != nil {
  142. http.Error(rw, err.Error(), 400)
  143. log.Errorf("martianhttp: error parsing JSON: %v", err)
  144. return
  145. }
  146. buf := new(bytes.Buffer)
  147. if err := json.Indent(buf, body, "", " "); err != nil {
  148. http.Error(rw, err.Error(), 400)
  149. log.Errorf("martianhttp: error formatting JSON: %v", err)
  150. return
  151. }
  152. m.mu.Lock()
  153. defer m.mu.Unlock()
  154. m.config = buf.Bytes()
  155. m.setRequestModifier(r.RequestModifier())
  156. m.setResponseModifier(r.ResponseModifier())
  157. }
  158. func (m *Modifier) serveGET(rw http.ResponseWriter, req *http.Request) {
  159. m.mu.RLock()
  160. defer m.mu.RUnlock()
  161. rw.Header().Set("Content-Type", "application/json")
  162. rw.Write(m.config)
  163. }