github.com/TeaOSLab/EdgeNode@v1.3.8/internal/utils/readers/reader_closer_byte_ranges.go (about)

     1  // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
     2  
     3  package readers
     4  
     5  import (
     6  	"bytes"
     7  	"github.com/iwind/TeaGo/types"
     8  	"io"
     9  	"mime/multipart"
    10  	"net/textproto"
    11  	"regexp"
    12  	"strings"
    13  )
    14  
    15  type OnPartReadHandler func(start int64, end int64, total int64, data []byte, header textproto.MIMEHeader)
    16  
    17  var contentRangeRegexp = regexp.MustCompile(`^(\d+)-(\d+)/(\d+|\*)`)
    18  
    19  type ByteRangesReaderCloser struct {
    20  	BaseReader
    21  
    22  	rawReader io.ReadCloser
    23  	boundary  string
    24  
    25  	mReader *multipart.Reader
    26  	part    *multipart.Part
    27  
    28  	buf   *bytes.Buffer
    29  	isEOF bool
    30  
    31  	onPartReadHandler OnPartReadHandler
    32  	rangeStart        int64
    33  	rangeEnd          int64
    34  	total             int64
    35  
    36  	isStarted bool
    37  	nl        string
    38  }
    39  
    40  func NewByteRangesReaderCloser(reader io.ReadCloser, boundary string) *ByteRangesReaderCloser {
    41  	return &ByteRangesReaderCloser{
    42  		rawReader: reader,
    43  		mReader:   multipart.NewReader(reader, boundary),
    44  		boundary:  boundary,
    45  		buf:       &bytes.Buffer{},
    46  		nl:        "\r\n",
    47  	}
    48  }
    49  
    50  func (this *ByteRangesReaderCloser) Read(p []byte) (n int, err error) {
    51  	n, err = this.read(p)
    52  	return
    53  }
    54  
    55  func (this *ByteRangesReaderCloser) Close() error {
    56  	return this.rawReader.Close()
    57  }
    58  
    59  func (this *ByteRangesReaderCloser) OnPartRead(handler OnPartReadHandler) {
    60  	this.onPartReadHandler = handler
    61  }
    62  
    63  func (this *ByteRangesReaderCloser) read(p []byte) (n int, err error) {
    64  	// read from buffer
    65  	n, err = this.buf.Read(p)
    66  	if !this.isEOF {
    67  		err = nil
    68  	}
    69  	if n > 0 {
    70  		return
    71  	}
    72  	if this.isEOF {
    73  		return
    74  	}
    75  
    76  	if this.part == nil {
    77  		part, partErr := this.mReader.NextPart()
    78  		if partErr != nil {
    79  			if partErr == io.EOF {
    80  				this.buf.WriteString(this.nl + "--" + this.boundary + "--" + this.nl)
    81  				this.isEOF = true
    82  				n, _ = this.buf.Read(p)
    83  				return
    84  			}
    85  
    86  			return 0, partErr
    87  		}
    88  
    89  		if !this.isStarted {
    90  			this.isStarted = true
    91  			this.buf.WriteString("--" + this.boundary + this.nl)
    92  		} else {
    93  			this.buf.WriteString(this.nl + "--" + this.boundary + this.nl)
    94  		}
    95  
    96  		// Headers
    97  		var hasRange = false
    98  		for k, v := range part.Header {
    99  			for _, v1 := range v {
   100  				this.buf.WriteString(k + ": " + v1 + this.nl)
   101  
   102  				// parse range
   103  				if k == "Content-Range" {
   104  					var bytesPrefix = "bytes "
   105  					if strings.HasPrefix(v1, bytesPrefix) {
   106  						var r = v1[len(bytesPrefix):]
   107  						var matches = contentRangeRegexp.FindStringSubmatch(r)
   108  						if len(matches) > 2 {
   109  							var start = types.Int64(matches[1])
   110  							var end = types.Int64(matches[2])
   111  							var total int64 = 0
   112  							if matches[3] != "*" {
   113  								total = types.Int64(matches[3])
   114  							}
   115  							if start <= end {
   116  								hasRange = true
   117  								this.rangeStart = start
   118  								this.rangeEnd = end
   119  								this.total = total
   120  							}
   121  						}
   122  					}
   123  				}
   124  			}
   125  		}
   126  
   127  		if !hasRange {
   128  			this.rangeStart = -1
   129  			this.rangeEnd = -1
   130  		}
   131  
   132  		this.buf.WriteString(this.nl)
   133  		this.part = part
   134  
   135  		n, _ = this.buf.Read(p)
   136  		return
   137  	}
   138  
   139  	n, err = this.part.Read(p)
   140  
   141  	if this.onPartReadHandler != nil && n > 0 && this.rangeStart >= 0 && this.rangeEnd >= 0 {
   142  		this.onPartReadHandler(this.rangeStart, this.rangeEnd, this.total, p[:n], this.part.Header)
   143  		this.rangeStart += int64(n)
   144  	}
   145  
   146  	if err == io.EOF {
   147  		this.part = nil
   148  		err = nil
   149  
   150  		// 如果没有读取到内容,则直接跳到下一个Part
   151  		if n == 0 {
   152  			return this.read(p)
   153  		}
   154  	}
   155  
   156  	return
   157  }