diff --git a/blobserver/go/get.go b/blobserver/go/get.go index 444a07ea5..46bd4e112 100644 --- a/blobserver/go/get.go +++ b/blobserver/go/get.go @@ -29,8 +29,36 @@ func handleGet(conn *http.Conn, req *http.Request) { serverError(conn, err) return } + + reqRange := getRequestedRange(req) + if reqRange.SkipBytes != 0 { + _, err = file.Seek(reqRange.SkipBytes, 0) + if err != nil { + serverError(conn, err) + return + } + } + + var input io.Reader = file + if reqRange.LimitBytes != -1 { + input = io.LimitReader(file, reqRange.LimitBytes) + } + + remainBytes := stat.Size - reqRange.SkipBytes + if reqRange.LimitBytes != -1 && + reqRange.LimitBytes < remainBytes { + remainBytes = reqRange.LimitBytes + } + conn.SetHeader("Content-Type", "application/octet-stream") - bytesCopied, err := io.Copy(conn, file) + if !reqRange.IsWholeFile() { + conn.SetHeader("Content-Range", + fmt.Sprintf("bytes %d-%d/%d", reqRange.SkipBytes, + reqRange.SkipBytes + remainBytes, + stat.Size)) + conn.WriteHeader(http.StatusPartialContent) + } + bytesCopied, err := io.Copy(conn, input) // If there's an error at this point, it's too late to tell the client, // as they've already been receiving bytes. But they should be smart enough @@ -44,9 +72,9 @@ func handleGet(conn *http.Conn, req *http.Request) { } return } - if bytesCopied != stat.Size { - fmt.Fprintf(os.Stderr, "Error sending file: %v, copied= %d, not %d%v\n", blobRef, - bytesCopied, stat.Size) + if bytesCopied != remainBytes { + fmt.Fprintf(os.Stderr, "Error sending file: %v, copied=%d, not %d\n", blobRef, + bytesCopied, remainBytes) closer, _, err := conn.Hijack() if err != nil { closer.Close() diff --git a/blobserver/go/range.go b/blobserver/go/range.go new file mode 100644 index 000000000..83f624bfb --- /dev/null +++ b/blobserver/go/range.go @@ -0,0 +1,49 @@ +package main + +import ( + "http" + "regexp" + "strconv" +) + +// Default is {0, -1} to read all of a file. +type requestedRange struct { + SkipBytes int64 + LimitBytes int64 // or -1 to read all +} + +func (rr *requestedRange) IsWholeFile() bool { + return rr.SkipBytes == 0 && rr.LimitBytes == -1; +} + +var wholeRange = &requestedRange{0, -1} + +var rangePattern = regexp.MustCompile(`bytes=([0-9]+)-([0-9]*)`) + +func getRequestedRange(req *http.Request) *requestedRange { + rrange, ok := req.Header["Range"] + if !ok { + return wholeRange + } + return getRequestedRangeFromString(rrange) +} + +func getRequestedRangeFromString(rrange string) *requestedRange { + matches := rangePattern.MatchStrings(rrange) + if len(matches) == 0 { + return wholeRange; + } + skipBytes, _ := strconv.Atoi64(matches[1]) + lastByteInclusive := int64(-1) + if len(matches[2]) > 0 { + lastByteInclusive, _ = strconv.Atoi64(matches[2]) + } + limitBytes := int64(-1) + if lastByteInclusive != -1 { + limitBytes = lastByteInclusive - skipBytes + 1 + if limitBytes < 0 { + limitBytes = 0 + } + } + return &requestedRange{skipBytes, limitBytes} +} diff --git a/blobserver/go/range_test.go b/blobserver/go/range_test.go new file mode 100644 index 000000000..0c9a02721 --- /dev/null +++ b/blobserver/go/range_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func testRange(t *t.Testing) { + +} diff --git a/blobserver/go/upload-file.pl b/blobserver/go/upload-file.pl new file mode 100755 index 000000000..cd0205a74 --- /dev/null +++ b/blobserver/go/upload-file.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# +# Lame upload script for development testing only. Doesn't do the +# preupload step and hard-codes the Go server's upload path (not +# conformant to spec). + +use strict; +my $file = shift or die + "Usage: upload-file.pl: "; +-r $file or die "$file isn't readable."; +-f $file or die "$file isn't a file."; + +die "bogus filename" if $file =~ /[ <>&\!]/; + +my $sha1 = `sha1sum $file`; +chomp $sha1; +$sha1 =~ s/\s.+//; + +system("curl", "-u", "foo:foo", "-F", "sha1-$sha1=\@$file", + "http://127.0.0.1:3179/camli/upload") and die "upload failed."; +print "Uploaded http://127.0.0.1:3179/camli/sha1-$sha1\n"; diff --git a/blobserver/go/upload.sh b/blobserver/go/upload.sh deleted file mode 100755 index 96523a8f3..000000000 --- a/blobserver/go/upload.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -curl -u foo:foo -F sha1-7c63f74bbe0b1de55ec41ad0d9297a3762ecfdbc=@/bin/true http://127.0.0.1:3179/camli/upload -