github.com/searKing/golang/go@v1.2.117/net/http/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  	"net/textproto"
    10  	"strconv"
    11  	"strings"
    12  )
    13  
    14  // httpRange specifies the byte range to be sent to the client.
    15  type httpRange struct {
    16  	start, length int64
    17  }
    18  
    19  func (r httpRange) contentRange(size int64) string {
    20  	cr := &httpContentRange{
    21  		firstBytePos:   r.start,
    22  		lastBytePos:    r.start + r.length - 1,
    23  		completeLength: size,
    24  	}
    25  	return cr.String()
    26  }
    27  
    28  func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader {
    29  	return textproto.MIMEHeader{
    30  		"Content-Range": {r.contentRange(size)},
    31  		"Content-Type":  {contentType},
    32  	}
    33  }
    34  
    35  // parseRange parses a Range header string as per RFC 7233.
    36  // errNoOverlap is returned if none of the ranges overlap.
    37  func parseRange(s string, size int64) ([]httpRange, error) {
    38  	if s == "" {
    39  		return nil, nil // header not present
    40  	}
    41  	const b = "bytes="
    42  	if !strings.HasPrefix(s, b) {
    43  		return nil, errors.New("invalid range")
    44  	}
    45  	var ranges []httpRange
    46  	noOverlap := false
    47  	for _, ra := range strings.Split(s[len(b):], ",") {
    48  		ra = strings.TrimSpace(ra)
    49  		if ra == "" {
    50  			continue
    51  		}
    52  		i := strings.Index(ra, "-")
    53  		if i < 0 {
    54  			return nil, errors.New("invalid range")
    55  		}
    56  		start, end := strings.TrimSpace(ra[:i]), strings.TrimSpace(ra[i+1:])
    57  		var r httpRange
    58  		if start == "" {
    59  			// If no start is specified, end specifies the
    60  			// range start relative to the end of the file.
    61  			i, err := strconv.ParseInt(end, 10, 64)
    62  			if err != nil {
    63  				return nil, errors.New("invalid range")
    64  			}
    65  			if i > size {
    66  				i = size
    67  			}
    68  			r.start = size - i
    69  			r.length = size - r.start
    70  		} else {
    71  			i, err := strconv.ParseInt(start, 10, 64)
    72  			if err != nil || i < 0 {
    73  				return nil, errors.New("invalid range")
    74  			}
    75  			if i >= size {
    76  				// If the range begins after the size of the content,
    77  				// then it does not overlap.
    78  				noOverlap = true
    79  				continue
    80  			}
    81  			r.start = i
    82  			if end == "" {
    83  				// If no end is specified, range extends to end of the file.
    84  				r.length = size - r.start
    85  			} else {
    86  				i, err := strconv.ParseInt(end, 10, 64)
    87  				if err != nil || r.start > i {
    88  					return nil, errors.New("invalid range")
    89  				}
    90  				if i >= size {
    91  					i = size - 1
    92  				}
    93  				r.length = i - r.start + 1
    94  			}
    95  		}
    96  		ranges = append(ranges, r)
    97  	}
    98  	if noOverlap && len(ranges) == 0 {
    99  		// The specified ranges did not overlap with the content.
   100  		return nil, errNoOverlap
   101  	}
   102  	return ranges, nil
   103  }