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 | |
| }
 |