/pkg/proxy/ranges/byterange/range.go

https://github.com/tricksterproxy/trickster · Go · 315 lines · 235 code · 39 blank · 41 comment · 78 complexity · dd8311873ada935a2b304c33b7a6fe29 MD5 · raw file

  1. /*
  2. * Copyright 2018 The Trickster Authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. // Package byterange provides acceleration functions for Byte Ranges
  17. // for use with HTTP Range Requests
  18. package byterange
  19. import (
  20. "errors"
  21. "fmt"
  22. "regexp"
  23. "sort"
  24. "strconv"
  25. "strings"
  26. )
  27. //go:generate msgp
  28. // Range represents the start and end for a byte range object
  29. type Range struct {
  30. Start int64 `msg:"start"`
  31. End int64 `msg:"end"`
  32. }
  33. // Ranges represents a slice of type Range
  34. // The objects in the slice may not overlap value coverage,
  35. // meaning values are contained within <= 1 Range in the slice
  36. // Good: [ 1-10, 21-30, 35-40 ]; Bad: [ 1-10, 10-20 ]; Bad: [ 1-10, 5-20 ]
  37. type Ranges []Range
  38. const byteRequestRangePrefix = "bytes="
  39. const byteResponsRangePrefix = "bytes "
  40. var respRE *regexp.Regexp
  41. func init() {
  42. respRE = regexp.MustCompile(`^bytes ([0-9]+)-([0-9]+)\/([0-9]+)$`)
  43. }
  44. func (br Range) String() string {
  45. var start string
  46. var end string
  47. if br.Start >= 0 {
  48. start = strconv.FormatInt(br.Start, 10)
  49. }
  50. if br.End >= 0 {
  51. end = strconv.FormatInt(br.End, 10)
  52. }
  53. return start + "-" + end
  54. }
  55. // ContentRangeHeader returns a 'Content-Range' header representing the extent of the subject range
  56. func (br Range) ContentRangeHeader(contentLength int64) string {
  57. var start string
  58. var end string
  59. cl := "*"
  60. if br.Start >= 0 {
  61. start = strconv.FormatInt(br.Start, 10)
  62. }
  63. if br.End >= 0 {
  64. end = strconv.FormatInt(br.End, 10)
  65. }
  66. if contentLength > 0 {
  67. cl = strconv.FormatInt(contentLength, 10)
  68. }
  69. return byteResponsRangePrefix + start + "-" + end + "/" + cl
  70. }
  71. func (brs Ranges) String() string {
  72. if len(brs) == 0 {
  73. return ""
  74. }
  75. sb := strings.Builder{}
  76. sb.WriteString(byteRequestRangePrefix)
  77. var sep string
  78. for _, r := range brs {
  79. sb.WriteString(fmt.Sprintf("%s%s", sep, r.String()))
  80. sep = ", "
  81. }
  82. return sb.String()
  83. }
  84. // CalculateDelta calculates the delta between two Ranges
  85. func (brs Ranges) CalculateDelta(haves Ranges, fullContentLength int64) Ranges {
  86. checkpoint := int64(-1)
  87. if len(brs) == 0 {
  88. return haves
  89. }
  90. if haves == nil || fullContentLength < 1 || len(haves) == 0 {
  91. return brs
  92. }
  93. if brs.Equal(haves) {
  94. return Ranges{}
  95. }
  96. sort.Sort(brs)
  97. sort.Sort(haves)
  98. need := make(Ranges, 0, len(brs)+len(haves))
  99. deltaRange := func() Range {
  100. return Range{Start: -1, End: -1}
  101. }
  102. nr := deltaRange()
  103. for i, want := range brs {
  104. // adjust any prefix/suffix ranges to known start/ends
  105. if want.Start == -1 || want.End == -1 {
  106. if want.Start == -1 {
  107. want.Start = fullContentLength - want.End
  108. }
  109. want.End = fullContentLength - 1
  110. brs[i] = want
  111. }
  112. if want.End > fullContentLength {
  113. // end is out of bounds, consider a full miss
  114. return brs
  115. }
  116. checked := false
  117. // now compare to any cached ranges to determine any ranges that are not in cache
  118. for _, have := range haves {
  119. if have.End < checkpoint {
  120. continue
  121. }
  122. if have.Start > want.End {
  123. if nr.Start > -1 && nr.End == -1 {
  124. nr.End = want.End
  125. checkpoint = nr.End
  126. need = append(need, nr)
  127. checked = true
  128. nr = deltaRange()
  129. }
  130. break
  131. }
  132. if want.Start > have.End {
  133. if i < len(haves) {
  134. nr.Start = want.Start
  135. }
  136. continue
  137. }
  138. if want.Start >= have.Start && want.Start <= have.End &&
  139. want.End <= have.End && want.End >= have.Start {
  140. checked = true
  141. nr = deltaRange()
  142. continue
  143. }
  144. if nr.Start == -1 {
  145. // want and have share mutual start and/or ends
  146. if want.Start >= have.Start {
  147. // they are identical, break and move on
  148. if want.End <= have.End {
  149. break
  150. }
  151. nr.Start = have.End + 1
  152. continue
  153. }
  154. nr.Start = want.Start
  155. }
  156. if want.End <= have.End {
  157. if nr.Start > -1 && have.Start > 0 {
  158. nr.End = have.Start - 1
  159. need = append(need, nr)
  160. }
  161. checked = true
  162. nr = deltaRange()
  163. continue
  164. }
  165. if want.Start < have.Start && want.End > have.End {
  166. nr.End = have.Start - 1
  167. checkpoint = nr.End
  168. need = append(need, nr)
  169. checked = true
  170. nr = deltaRange()
  171. nr.Start = have.End + 1
  172. }
  173. if want.Start >= have.Start && want.Start <= have.End && want.End > have.End {
  174. nr.Start = have.End + 1
  175. }
  176. }
  177. if !checked {
  178. if nr.Start > -1 {
  179. want.Start = nr.Start
  180. }
  181. need = append(need, want)
  182. nr = deltaRange()
  183. }
  184. }
  185. if nr.Start != -1 && nr.End == -1 {
  186. nr.End = brs[len(brs)-1].End
  187. need = append(need, nr)
  188. }
  189. sort.Sort(need)
  190. return need
  191. }
  192. // ParseContentRangeHeader returns a Ranges list from the provided input,
  193. // which must be a properly-formatted HTTP 'Content-Range' Response Header value
  194. func ParseContentRangeHeader(input string) (Range, int64, error) {
  195. parts := respRE.FindAllStringSubmatch(input, -1)
  196. if len(parts) == 1 && len(parts[0]) == 4 {
  197. r := Range{}
  198. r.Start, _ = strconv.ParseInt(parts[0][1], 10, 64)
  199. r.End, _ = strconv.ParseInt(parts[0][2], 10, 64)
  200. if parts[0][3] == "*" {
  201. return r, -1, nil
  202. }
  203. cl, _ := strconv.ParseInt(parts[0][3], 10, 64)
  204. return r, cl, nil
  205. }
  206. return Range{}, -1, errors.New("invalid input format")
  207. }
  208. // ParseRangeHeader returns a Ranges list from the provided input,
  209. // which must be a properly-formatted HTTP 'Range' Request Header value
  210. func ParseRangeHeader(input string) Ranges {
  211. if input == "" || !strings.HasPrefix(input, byteRequestRangePrefix) ||
  212. input == byteRequestRangePrefix {
  213. return nil
  214. }
  215. input = strings.Replace(input, " ", "", -1)[6:]
  216. parts := strings.Split(input, ",")
  217. ranges := make(Ranges, len(parts))
  218. for i, p := range parts {
  219. j := strings.Index(p, "-")
  220. if j < 0 {
  221. return nil
  222. }
  223. var start = int64(-1)
  224. var end = int64(-1)
  225. var err error
  226. if j > 0 {
  227. start, err = strconv.ParseInt(p[0:j], 10, 64)
  228. if err != nil {
  229. return nil
  230. }
  231. }
  232. if j < len(p)-1 {
  233. end, err = strconv.ParseInt(p[j+1:], 10, 64)
  234. if err != nil {
  235. return nil
  236. }
  237. }
  238. ranges[i].Start = start
  239. ranges[i].End = end
  240. }
  241. sort.Sort(ranges)
  242. return ranges
  243. }
  244. // Equal returns true if the compared byte range slices are equal
  245. // and assumes that the Ranges are sorted
  246. func (brs Ranges) Equal(brs2 Ranges) bool {
  247. if brs2 == nil {
  248. return false
  249. }
  250. if len(brs) != len(brs2) {
  251. return false
  252. }
  253. for i := range brs {
  254. if brs[i] != brs2[i] {
  255. return false
  256. }
  257. }
  258. return true
  259. }
  260. // sort.Interface required functions for Ranges
  261. // Len returns the length of an slice of type Ranges
  262. func (brs Ranges) Len() int {
  263. return len(brs)
  264. }
  265. // Less returns true if element i in the Ranges comes before j
  266. func (brs Ranges) Less(i, j int) bool {
  267. return brs[i].Start < (brs[j].Start)
  268. }
  269. // Swap modifies an Ranges by swapping the values in indexes i and j
  270. func (brs Ranges) Swap(i, j int) {
  271. brs[i], brs[j] = brs[j], brs[i]
  272. }
  273. // Less returns true if element i in the Ranges comes before j
  274. func (br Range) Less(br2 Range) bool {
  275. return br.Start < br2.Start
  276. }