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.
 
 
 

231 lines
6.2 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 trafficshape
  15. import (
  16. "bytes"
  17. "encoding/json"
  18. "io"
  19. "io/ioutil"
  20. "net/http"
  21. "time"
  22. "github.com/google/martian/log"
  23. )
  24. // Handler configures a trafficshape.Listener.
  25. type Handler struct {
  26. l *Listener
  27. }
  28. // Throttle represents a byte interval with a specific bandwidth.
  29. type Throttle struct {
  30. Bytes string `json:"bytes"`
  31. Bandwidth int64 `json:"bandwidth"`
  32. ByteStart int64
  33. ByteEnd int64
  34. }
  35. // Action represents an arbitrary event that needs to be executed while writing back to the client.
  36. type Action interface {
  37. // Byte offset to perform Action at.
  38. getByte() int64
  39. // Number of times to perform the action. -1 for infinite times.
  40. getCount() int64
  41. // Update the count when performing an action.
  42. decrementCount()
  43. }
  44. // Halt is the event that represents a period of time to sleep while writing.
  45. // It implements the Action interface.
  46. type Halt struct {
  47. Byte int64 `json:"byte"`
  48. Duration int64 `json:"duration"`
  49. Count int64 `json:"count"`
  50. }
  51. func (h *Halt) getByte() int64 {
  52. return h.Byte
  53. }
  54. func (h *Halt) getCount() int64 {
  55. return h.Count
  56. }
  57. func (h *Halt) decrementCount() {
  58. if h.Count > 0 {
  59. h.Count--
  60. }
  61. }
  62. // CloseConnection is an event that represents the closing of a connection with a client.
  63. // It implements the Action interface.
  64. type CloseConnection struct {
  65. Byte int64 `json:"byte"`
  66. Count int64 `json:"count"`
  67. }
  68. func (cc *CloseConnection) getByte() int64 {
  69. return cc.Byte
  70. }
  71. func (cc *CloseConnection) getCount() int64 {
  72. return cc.Count
  73. }
  74. func (cc *CloseConnection) decrementCount() {
  75. if cc.Count > 0 {
  76. cc.Count--
  77. }
  78. }
  79. // Shape encloses the traffic shape of a particular url regex.
  80. type Shape struct {
  81. URLRegex string `json:"url_regex"`
  82. MaxBandwidth int64 `json:"max_global_bandwidth"`
  83. Throttles []*Throttle `json:"throttles"`
  84. Halts []*Halt `json:"halts"`
  85. CloseConnections []*CloseConnection `json:"close_connections"`
  86. // Actions are populated after processing Throttles, Halts and CloseConnections.
  87. // Actions is sorted in the order of byte offset.
  88. Actions []Action
  89. // WriteBucket is initialized by us using MaxBandwidth.
  90. WriteBucket *Bucket
  91. }
  92. // Bandwidth encloses information about the upstream and downstream bandwidths.
  93. type Bandwidth struct {
  94. Up int64 `json:"up"`
  95. Down int64 `json:"down"`
  96. }
  97. // Default encloses information about the default traffic shaping parameters: bandwidth and latency.
  98. type Default struct {
  99. Bandwidth Bandwidth `json:"bandwidth"`
  100. Latency int64 `json:"latency"`
  101. }
  102. // Trafficshape contains global shape of traffic, i.e information about shape of each url specified and
  103. // the default traffic shaping parameters.
  104. type Trafficshape struct {
  105. Defaults *Default `json:"default"`
  106. Shapes []*Shape `json:"shapes"`
  107. }
  108. // ConfigRequest represents a request to configure the global traffic shape.
  109. type ConfigRequest struct {
  110. Trafficshape *Trafficshape `json:"trafficshape"`
  111. }
  112. // ChangeBandwidth represents the event of changing the current bandwidth. It is used as an
  113. // endpoint of a Throttle. It implements the Action interface.
  114. type ChangeBandwidth struct {
  115. Byte int64
  116. Bandwidth int64
  117. }
  118. func (cb *ChangeBandwidth) getByte() int64 {
  119. return cb.Byte
  120. }
  121. func (cb *ChangeBandwidth) getCount() int64 {
  122. return -1
  123. }
  124. // No op. This is because Throttles have infinite count.
  125. func (cb *ChangeBandwidth) decrementCount() {
  126. }
  127. // NewHandler returns an http.Handler to configure traffic shaping.
  128. func NewHandler(l *Listener) *Handler {
  129. return &Handler{
  130. l: l,
  131. }
  132. }
  133. // ServeHTTP configures latency and bandwidth constraints.
  134. //
  135. // The "latency" query string parameter accepts a duration string in any format
  136. // supported by time.ParseDuration.
  137. // The "up" and "down" query string parameters accept integers as bits per
  138. // second to be used for read and write throughput.
  139. func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
  140. log.Infof("trafficshape: configuration request")
  141. receivedConfig := &ConfigRequest{}
  142. body, err := ioutil.ReadAll(req.Body)
  143. if err != nil {
  144. http.Error(rw, "Error reading request body", 400)
  145. return
  146. }
  147. bodystr := string(body)
  148. req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
  149. if err := json.NewDecoder(req.Body).Decode(&receivedConfig); err != nil {
  150. log.Errorf("Error while parsing the received json: %v", err)
  151. http.Error(rw, err.Error(), 400)
  152. return
  153. }
  154. if receivedConfig.Trafficshape == nil {
  155. http.Error(rw, "Error: trafficshape property not found", 400)
  156. return
  157. }
  158. defaults := receivedConfig.Trafficshape.Defaults
  159. if defaults == nil {
  160. defaults = &Default{}
  161. }
  162. if defaults.Bandwidth.Up < 0 || defaults.Bandwidth.Down < 0 || defaults.Latency < 0 {
  163. http.Error(rw, "Error: Invalid Defaults", 400)
  164. return
  165. }
  166. if defaults.Bandwidth.Up == 0 {
  167. defaults.Bandwidth.Up = DefaultBitrate / 8
  168. }
  169. if defaults.Bandwidth.Down == 0 {
  170. defaults.Bandwidth.Down = DefaultBitrate / 8
  171. }
  172. // Parse and verify the received shapes.
  173. if err := parseShapes(receivedConfig.Trafficshape); err != nil {
  174. http.Error(rw, err.Error(), 400)
  175. return
  176. }
  177. // Update the Listener with the new traffic shape.
  178. h.l.Shapes.Lock()
  179. h.l.Shapes.LastModifiedTime = time.Now()
  180. h.l.ReadBucket.SetCapacity(defaults.Bandwidth.Down)
  181. h.l.WriteBucket.SetCapacity(defaults.Bandwidth.Up)
  182. h.l.SetLatency(time.Duration(defaults.Latency) * time.Millisecond)
  183. h.l.SetDefaults(defaults)
  184. h.l.Shapes.M = make(map[string]*urlShape)
  185. for _, shape := range receivedConfig.Trafficshape.Shapes {
  186. h.l.Shapes.M[shape.URLRegex] = &urlShape{Shape: shape}
  187. }
  188. // Update the time that the map was last modified to the current time.
  189. h.l.Shapes.LastModifiedTime = time.Now()
  190. h.l.Shapes.Unlock()
  191. rw.WriteHeader(http.StatusOK)
  192. io.WriteString(rw, bodystr)
  193. }