github.com/searKing/golang/go@v1.2.117/net/http/content_range.go (about)

     1  // Copyright 2020 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package http
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  	"strconv"
    12  	"strings"
    13  )
    14  
    15  // errNoOverlap is returned by serveContent's parseContentRange if first-byte-pos of
    16  // all of the byte-range-spec values is greater than the content size.
    17  var errNoOverlap = errors.New("invalid range: failed to overlap")
    18  
    19  // httpContentRange specifies the byte range to be sent to the client.
    20  type httpContentRange struct {
    21  	firstBytePos, lastBytePos, completeLength int64
    22  }
    23  
    24  func (r httpContentRange) String() string {
    25  	var b strings.Builder
    26  	b.WriteString("bytes ")
    27  	if r.firstBytePos == -1 && r.lastBytePos == -1 {
    28  		b.WriteString("*")
    29  	} else {
    30  		if r.firstBytePos == -1 {
    31  			b.WriteString("*")
    32  		} else {
    33  			b.WriteString(fmt.Sprintf("%d", r.firstBytePos))
    34  		}
    35  		b.WriteString("-")
    36  		if r.lastBytePos == -1 {
    37  			b.WriteString("*")
    38  		} else {
    39  			b.WriteString(fmt.Sprintf("%d", r.lastBytePos))
    40  		}
    41  	}
    42  	b.WriteString("/")
    43  
    44  	if r.completeLength < 0 {
    45  		b.WriteString("*")
    46  	} else {
    47  		b.WriteString(fmt.Sprintf("%d", r.completeLength))
    48  	}
    49  	return b.String()
    50  }
    51  
    52  func parseContentRanges(s []string) ([]httpContentRange, error) {
    53  	var ranges []*httpContentRange
    54  
    55  	for _, ra := range s {
    56  		ra = strings.TrimSpace(ra)
    57  		if ra == "" {
    58  			continue
    59  		}
    60  		r, err := parseContentRange(ra)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  		ranges = append(ranges, r)
    65  	}
    66  
    67  	var size int64 = -1
    68  	for _, ra := range ranges {
    69  		if size != -1 && ra.completeLength != -1 && size != ra.completeLength {
    70  			return nil, errors.New("invalid range")
    71  		}
    72  		if ra.completeLength >= 0 {
    73  			size = ra.completeLength
    74  		}
    75  	}
    76  
    77  	for _, ra := range ranges {
    78  		ra.completeLength = size
    79  		if ra.firstBytePos < 0 {
    80  			ra.firstBytePos = 0
    81  		}
    82  		if ra.lastBytePos < 0 {
    83  			if size < 0 {
    84  				return nil, errors.New("invalid range")
    85  			}
    86  			ra.lastBytePos = size
    87  		}
    88  	}
    89  
    90  	var totalSize int64
    91  	for _, ra := range ranges {
    92  		totalSize += ra.lastBytePos - ra.firstBytePos + 1
    93  	}
    94  
    95  	if size >= 0 && size != totalSize {
    96  		return nil, errors.New("invalid range")
    97  	}
    98  
    99  	var outRanges []httpContentRange
   100  	for _, ra := range ranges {
   101  		ra.completeLength = totalSize
   102  		outRanges = append(outRanges, *ra)
   103  	}
   104  
   105  	return outRanges, nil
   106  }
   107  
   108  // parseContentRange parses a Range header string as per RFC 7233.
   109  // errNoOverlap is returned if none of the ranges overlap.
   110  func parseContentRange(s string) (contentRange *httpContentRange, err error) {
   111  	// bytes 0-499/1234
   112  	if s == "" {
   113  		return nil, nil // header not present
   114  	}
   115  	const b = "bytes "
   116  	if !strings.HasPrefix(s, b) {
   117  		return nil, errors.New("invalid range")
   118  	}
   119  
   120  	ra := strings.TrimSpace(s[len(b):])
   121  	if ra == "" {
   122  		return nil, nil
   123  	}
   124  
   125  	i := strings.Index(ra, "/")
   126  	if i < 0 {
   127  		return nil, errors.New("invalid range")
   128  	}
   129  	byteRange := strings.TrimSpace(ra[:i])
   130  	completeLength := strings.TrimSpace(ra[i+1:])
   131  
   132  	if byteRange == "*" && completeLength == "*" {
   133  		return nil, errors.New("invalid range")
   134  	}
   135  	if byteRange == "" && completeLength == "" {
   136  		return nil, errors.New("invalid range")
   137  	}
   138  	if byteRange == "*" && completeLength == "" {
   139  		return nil, errors.New("invalid range")
   140  	}
   141  	if byteRange == "" && completeLength == "*" {
   142  		return nil, errors.New("invalid range")
   143  	}
   144  
   145  	var r = httpContentRange{
   146  		firstBytePos:   -1,
   147  		lastBytePos:    -1,
   148  		completeLength: -1,
   149  	}
   150  	if byteRange != "*" {
   151  		i := strings.Index(byteRange, "-")
   152  		if i < 0 {
   153  			return nil, errors.New("invalid range")
   154  		}
   155  		firstBytePos := strings.TrimSpace(byteRange[:i])
   156  		lastBytePos := strings.TrimSpace(byteRange[i+1:])
   157  		if firstBytePos != "" {
   158  			i, err := strconv.ParseInt(firstBytePos, 10, 64)
   159  			if err != nil {
   160  				return nil, errors.New("invalid range")
   161  			}
   162  			r.firstBytePos = i
   163  		}
   164  		if lastBytePos != "" {
   165  			i, err := strconv.ParseInt(lastBytePos, 10, 64)
   166  			if err != nil {
   167  				return nil, errors.New("invalid range")
   168  			}
   169  			r.lastBytePos = i
   170  		}
   171  	}
   172  
   173  	if completeLength != "*" {
   174  		i, err := strconv.ParseInt(completeLength, 10, 64)
   175  		if err != nil {
   176  			return nil, errors.New("invalid range")
   177  		}
   178  		r.completeLength = i
   179  	}
   180  
   181  	if r.firstBytePos < 0 && r.lastBytePos < 0 && r.completeLength < 0 {
   182  		return nil, errors.New("invalid range")
   183  	}
   184  
   185  	if r.firstBytePos >= 0 && r.lastBytePos >= 0 && r.firstBytePos > r.lastBytePos {
   186  		return nil, errors.New("invalid range")
   187  	}
   188  
   189  	if r.firstBytePos >= 0 && r.lastBytePos >= 0 && r.completeLength >= 0 && (r.lastBytePos-r.firstBytePos+1 < r.completeLength) {
   190  		// The specified ranges did not overlap with the content.
   191  		return nil, errNoOverlap
   192  	}
   193  	return &r, nil
   194  }
   195  
   196  // countingWriter counts how many bytes have been written to it.
   197  type countingWriter int64
   198  
   199  func (w *countingWriter) Write(p []byte) (n int, err error) {
   200  	*w += countingWriter(len(p))
   201  	return len(p), nil
   202  }
   203  
   204  func writeContentRanges(w http.ResponseWriter, ranges []httpContentRange) {
   205  	for _, ra := range ranges {
   206  		w.Header().Add("Content-Range", ra.String())
   207  	}
   208  }