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.
 
 
 

204 lines
6.3 KiB

  1. package trafficshape
  2. import (
  3. "errors"
  4. "fmt"
  5. "regexp"
  6. "sort"
  7. "strconv"
  8. "strings"
  9. "time"
  10. )
  11. // Converts a sorted slice of Throttles to their ChangeBandwidth actions. In adddition, checks for
  12. // overlapping throttle ranges. Returns a slice of actions and an error specifying if the throttles
  13. // passed the non-overlapping verification.
  14. // Idea: For every throttle, add two ChangeBandwidth actions (one for start and one for end), unless
  15. // the ending byte of one throttle is the same as the starting byte of the next throttle, in which
  16. // case we do not add the end ChangeBandwidth for the first throttle, or if the end of a throttle
  17. // is -1 (representing till the end of file), in which case we do not add the end ChangeBandwidth
  18. // action for the throttle. Note, we only allow the last throttle in the sorted list to have an end
  19. // of -1, since otherwise there would be an overlap.
  20. func getActionsFromThrottles(throttles []*Throttle, defaultBandwidth int64) ([]Action, error) {
  21. lenThr := len(throttles)
  22. var actions []Action
  23. for index, throttle := range throttles {
  24. start := throttle.ByteStart
  25. end := throttle.ByteEnd
  26. if index == lenThr-1 {
  27. if end == -1 {
  28. actions = append(actions,
  29. Action(&ChangeBandwidth{
  30. Byte: start,
  31. Bandwidth: throttle.Bandwidth,
  32. }))
  33. } else {
  34. actions = append(actions,
  35. Action(&ChangeBandwidth{
  36. Byte: start,
  37. Bandwidth: throttle.Bandwidth,
  38. }),
  39. Action(&ChangeBandwidth{
  40. Byte: end,
  41. Bandwidth: defaultBandwidth,
  42. }))
  43. }
  44. break
  45. }
  46. if end > throttles[index+1].ByteStart || end == -1 {
  47. return actions, errors.New("overlapping throttle intervals found")
  48. }
  49. if end == throttles[index+1].ByteStart {
  50. actions = append(actions,
  51. Action(&ChangeBandwidth{
  52. Byte: start,
  53. Bandwidth: throttle.Bandwidth,
  54. }))
  55. } else {
  56. actions = append(actions,
  57. Action(&ChangeBandwidth{
  58. Byte: start,
  59. Bandwidth: throttle.Bandwidth,
  60. }),
  61. Action(&ChangeBandwidth{
  62. Byte: end,
  63. Bandwidth: defaultBandwidth,
  64. }))
  65. }
  66. }
  67. return actions, nil
  68. }
  69. // Parses the Trafficshape object and populates/updates Traffficshape.Shapes,
  70. // while performing verifications. Returns an error in case a verification check fails.
  71. func parseShapes(ts *Trafficshape) error {
  72. var err error
  73. for shapeIndex, shape := range ts.Shapes {
  74. if shape == nil {
  75. return fmt.Errorf("nil shape at index: %d", shapeIndex)
  76. }
  77. if shape.URLRegex == "" {
  78. return fmt.Errorf("no url_regex for shape at index: %d", shapeIndex)
  79. }
  80. if _, err = regexp.Compile(shape.URLRegex); err != nil {
  81. return fmt.Errorf("url_regex for shape at index doesn't compile: %d", shapeIndex)
  82. }
  83. if shape.MaxBandwidth < 0 {
  84. return fmt.Errorf("max_bandwidth cannot be negative for shape at index: %d", shapeIndex)
  85. }
  86. if shape.MaxBandwidth == 0 {
  87. shape.MaxBandwidth = DefaultBitrate / 8
  88. }
  89. shape.WriteBucket = NewBucket(shape.MaxBandwidth, time.Second)
  90. // Verify and process the throttles, filling in their ByteStart and ByteEnd.
  91. for throttleIndex, throttle := range shape.Throttles {
  92. if throttle == nil {
  93. return fmt.Errorf("nil throttle at index %d in shape index %d", throttleIndex, shapeIndex)
  94. }
  95. if throttle.Bandwidth <= 0 {
  96. return fmt.Errorf("invalid bandwidth: %d at throttle index %d in shape index %d",
  97. throttle.Bandwidth, throttleIndex, shapeIndex)
  98. }
  99. sl := strings.Split(throttle.Bytes, "-")
  100. if len(sl) != 2 {
  101. return fmt.Errorf("invalid bytes: %s at throttle index %d in shape index %d",
  102. throttle.Bytes, throttleIndex, shapeIndex)
  103. }
  104. start := sl[0]
  105. end := sl[1]
  106. if start == "" {
  107. throttle.ByteStart = 0
  108. } else {
  109. throttle.ByteStart, err = strconv.ParseInt(start, 10, 64)
  110. if err != nil {
  111. return fmt.Errorf("invalid bytes: %s at throttle index %d in shape index %d",
  112. throttle.Bytes, throttleIndex, shapeIndex)
  113. }
  114. }
  115. if end == "" {
  116. throttle.ByteEnd = -1
  117. } else {
  118. throttle.ByteEnd, err = strconv.ParseInt(end, 10, 64)
  119. if err != nil {
  120. return fmt.Errorf("invalid bytes: %s at throttle index %d in shape index %d",
  121. throttle.Bytes, throttleIndex, shapeIndex)
  122. }
  123. if throttle.ByteEnd < throttle.ByteStart {
  124. return fmt.Errorf("invalid bytes: %s at throttle index %d in shape index %d",
  125. throttle.Bytes, throttleIndex, shapeIndex)
  126. }
  127. }
  128. if throttle.ByteStart == throttle.ByteEnd {
  129. return fmt.Errorf("invalid bytes: %s at throttle index %d in shape index %d",
  130. throttle.Bytes, throttleIndex, shapeIndex)
  131. }
  132. }
  133. // Fill in the actions, while performing verification.
  134. shape.Actions = make([]Action, len(shape.Halts)+len(shape.CloseConnections))
  135. for index, value := range shape.Halts {
  136. if value == nil {
  137. return fmt.Errorf("nil halt at index %d in shape index %d", index, shapeIndex)
  138. }
  139. if value.Duration < 0 || value.Byte < 0 {
  140. return fmt.Errorf("invalid halt at index %d in shape index %d", index, shapeIndex)
  141. }
  142. if value.Count == 0 {
  143. return fmt.Errorf(" 0 count for halt at index %d in shape index %d", index, shapeIndex)
  144. }
  145. shape.Actions[index] = Action(value)
  146. }
  147. offset := len(shape.Halts)
  148. for index, value := range shape.CloseConnections {
  149. if value == nil {
  150. return fmt.Errorf("nil close_connection at index %d in shape index %d",
  151. index, shapeIndex)
  152. }
  153. if value.Byte < 0 {
  154. return fmt.Errorf("invalid close_connection at index %d in shape index %d",
  155. index, shapeIndex)
  156. }
  157. if value.Count == 0 {
  158. return fmt.Errorf("0 count for close_connection at index %d in shape index %d",
  159. index, shapeIndex)
  160. }
  161. shape.Actions[offset+index] = Action(value)
  162. }
  163. sort.SliceStable(shape.Throttles, func(i, j int) bool {
  164. return shape.Throttles[i].ByteStart < shape.Throttles[j].ByteStart
  165. })
  166. defaultBandwidth := DefaultBitrate / 8
  167. if shape.MaxBandwidth > 0 {
  168. defaultBandwidth = shape.MaxBandwidth
  169. }
  170. throttleActions, err := getActionsFromThrottles(shape.Throttles, defaultBandwidth)
  171. if err != nil {
  172. return fmt.Errorf("err: %s in shape index %d", err.Error(), shapeIndex)
  173. }
  174. shape.Actions = append(shape.Actions, throttleActions...)
  175. // Sort the actions according to their byte offset.
  176. sort.SliceStable(shape.Actions, func(i, j int) bool { return shape.Actions[i].getByte() < shape.Actions[j].getByte() })
  177. }
  178. return nil
  179. }