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.
		
		
		
		
		
			
		
			
				
					
					
						
							115 lines
						
					
					
						
							2.7 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							115 lines
						
					
					
						
							2.7 KiB
						
					
					
				
								package weed_server
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"errors"
							 | 
						|
									"fmt"
							 | 
						|
									"mime/multipart"
							 | 
						|
									"net/textproto"
							 | 
						|
									"strconv"
							 | 
						|
									"strings"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// copied from src/pkg/net/http/fs.go
							 | 
						|
								
							 | 
						|
								// httpRange specifies the byte range to be sent to the client.
							 | 
						|
								type httpRange struct {
							 | 
						|
									start, length int64
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r httpRange) contentRange(size int64) string {
							 | 
						|
									return fmt.Sprintf("bytes %d-%d/%d", r.start, r.start+r.length-1, size)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader {
							 | 
						|
									return textproto.MIMEHeader{
							 | 
						|
										"Content-Range": {r.contentRange(size)},
							 | 
						|
										"Content-Type":  {contentType},
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// parseRange parses a Range header string as per RFC 2616.
							 | 
						|
								func parseRange(s string, size int64) ([]httpRange, error) {
							 | 
						|
									if s == "" {
							 | 
						|
										return nil, nil // header not present
							 | 
						|
									}
							 | 
						|
									const b = "bytes="
							 | 
						|
									if !strings.HasPrefix(s, b) {
							 | 
						|
										return nil, errors.New("invalid range")
							 | 
						|
									}
							 | 
						|
									var ranges []httpRange
							 | 
						|
									for _, ra := range strings.Split(s[len(b):], ",") {
							 | 
						|
										ra = strings.TrimSpace(ra)
							 | 
						|
										if ra == "" {
							 | 
						|
											continue
							 | 
						|
										}
							 | 
						|
										i := strings.Index(ra, "-")
							 | 
						|
										if i < 0 {
							 | 
						|
											return nil, errors.New("invalid range")
							 | 
						|
										}
							 | 
						|
										start, end := strings.TrimSpace(ra[:i]), strings.TrimSpace(ra[i+1:])
							 | 
						|
										var r httpRange
							 | 
						|
										if start == "" {
							 | 
						|
											// If no start is specified, end specifies the
							 | 
						|
											// range start relative to the end of the file.
							 | 
						|
											i, err := strconv.ParseInt(end, 10, 64)
							 | 
						|
											if err != nil {
							 | 
						|
												return nil, errors.New("invalid range")
							 | 
						|
											}
							 | 
						|
											if i > size {
							 | 
						|
												i = size
							 | 
						|
											}
							 | 
						|
											r.start = size - i
							 | 
						|
											r.length = size - r.start
							 | 
						|
										} else {
							 | 
						|
											i, err := strconv.ParseInt(start, 10, 64)
							 | 
						|
											if err != nil || i > size || i < 0 {
							 | 
						|
												return nil, errors.New("invalid range")
							 | 
						|
											}
							 | 
						|
											r.start = i
							 | 
						|
											if end == "" {
							 | 
						|
												// If no end is specified, range extends to end of the file.
							 | 
						|
												r.length = size - r.start
							 | 
						|
											} else {
							 | 
						|
												i, err := strconv.ParseInt(end, 10, 64)
							 | 
						|
												if err != nil || r.start > i {
							 | 
						|
													return nil, errors.New("invalid range")
							 | 
						|
												}
							 | 
						|
												if i >= size {
							 | 
						|
													i = size - 1
							 | 
						|
												}
							 | 
						|
												r.length = i - r.start + 1
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										ranges = append(ranges, r)
							 | 
						|
									}
							 | 
						|
									return ranges, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// countingWriter counts how many bytes have been written to it.
							 | 
						|
								type countingWriter int64
							 | 
						|
								
							 | 
						|
								func (w *countingWriter) Write(p []byte) (n int, err error) {
							 | 
						|
									*w += countingWriter(len(p))
							 | 
						|
									return len(p), nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// rangesMIMESize returns the number of bytes it takes to encode the
							 | 
						|
								// provided ranges as a multipart response.
							 | 
						|
								func rangesMIMESize(ranges []httpRange, contentType string, contentSize int64) (encSize int64) {
							 | 
						|
									var w countingWriter
							 | 
						|
									mw := multipart.NewWriter(&w)
							 | 
						|
									for _, ra := range ranges {
							 | 
						|
										mw.CreatePart(ra.mimeHeader(contentType, contentSize))
							 | 
						|
										encSize += ra.length
							 | 
						|
									}
							 | 
						|
									mw.Close()
							 | 
						|
									encSize += int64(w)
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func sumRangesSize(ranges []httpRange) (size int64) {
							 | 
						|
									for _, ra := range ranges {
							 | 
						|
										size += ra.length
							 | 
						|
									}
							 | 
						|
									return
							 | 
						|
								}
							 |