Fallback to HTTP if blob is missing from local repository.

Change-Id: Ieee01a6519f2427fc53d5468fbd1cd7a41fdcd96
diff --git a/cache/gitcache.go b/cache/gitcache.go
index e9fa0bd..fa8c827 100644
--- a/cache/gitcache.go
+++ b/cache/gitcache.go
@@ -18,6 +18,7 @@
 	"bytes"
 	"fmt"
 	"log"
+	"net"
 	"net/url"
 	"os"
 	"os/exec"
@@ -29,7 +30,9 @@
 	git "github.com/libgit2/git2go"
 )
 
-// gitCache manages a set of bare git repositories.
+// gitCache manages a set of bare git repositories.  Repositories are
+// recognized by URLs. Port numbers in git URLs are not part of the
+// cache key.
 type gitCache struct {
 	// directory to hold the repositories.
 	dir string
@@ -112,6 +115,10 @@
 		return "", err
 	}
 
+	if h, _, err := net.SplitHostPort(parsed.Host); err == nil {
+		parsed.Host = h
+	}
+
 	p := path.Clean(parsed.Path)
 	if path.Base(p) == ".git" {
 		p = path.Dir(p)
@@ -158,7 +165,6 @@
 	if err != nil {
 		return nil
 	}
-
 	repo, err := git.OpenRepository(p)
 	if err != nil {
 		return nil
diff --git a/fs/gitilesfs.go b/fs/gitilesfs.go
index 16b1d96..8400832 100644
--- a/fs/gitilesfs.go
+++ b/fs/gitilesfs.go
@@ -239,12 +239,13 @@
 	var content []byte
 	if repo != nil {
 		blob, err := repo.LookupBlob(&id)
-		if err != nil {
-			return err
+		if err == nil {
+			content = blob.Contents()
+			blob.Free()
 		}
-		defer blob.Free()
-		content = blob.Contents()
-	} else {
+	}
+
+	if content == nil {
 		path := r.shaMap[id]
 
 		var err error
diff --git a/fs/gitilesfs_test.go b/fs/gitilesfs_test.go
index a07b96d..797358d 100644
--- a/fs/gitilesfs_test.go
+++ b/fs/gitilesfs_test.go
@@ -22,6 +22,7 @@
 	"net"
 	"net/http"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"reflect"
 	"regexp"
@@ -68,7 +69,8 @@
   <default revision="master"
            remote="aosp"
            sync-j="4" />
-  <project path="build/kati" name="platform/build/kati" groups="pdk,tradefed" revision="ce34badf691d36e8048b63f89d1a86ee5fa4325c">
+  <project path="build/kati" name="platform/build/kati" groups="pdk,tradefed" revision="ce34badf691d36e8048b63f89d1a86ee5fa4325c"
+            clone-url="http://localhost/platform/platform/build/kati" >
     <copyfile dest="build/copydest" src="AUTHORS" />
     <linkfile dest="build/linkdest" src="AUTHORS" />
   </project>
@@ -159,6 +161,7 @@
 }
 
 type testServer struct {
+	addr     string
 	listener net.Listener
 	mu       sync.Mutex
 	requests map[string]int
@@ -189,14 +192,17 @@
 }
 
 func newTestServer() (*testServer, error) {
-	listener, err := net.Listen("tcp", ":0")
+	listener, err := net.Listen("tcp", "localhost:0")
 	if err != nil {
 		return nil, err
 	}
 
+	addr := listener.Addr().(*net.TCPAddr)
+
 	ts := &testServer{
 		listener: listener,
 		requests: map[string]int{},
+		addr:     fmt.Sprintf("localhost:%d", addr.Port),
 	}
 
 	mux := http.NewServeMux()
@@ -211,6 +217,48 @@
 	return ts, err
 }
 
+func TestGitilesFSNotInGit(t *testing.T) {
+	fix, err := newTestFixture()
+	if err != nil {
+		t.Fatal("newTestFixture", err)
+	}
+	defer fix.cleanup()
+
+	// Add a git repo; this doesn't have the requested blob.
+	cmd := exec.Command("/bin/sh", "-c",
+		strings.Join([]string{
+			"mkdir -p localhost/platform/build/kati.git",
+			"cd localhost/platform/build/kati.git",
+			"git init",
+			"touch file",
+			"git add file",
+			"git commit -m msg -a"}, " && "))
+	cmd.Dir = filepath.Join(fix.dir, "cache", "git")
+	if out, err := cmd.CombinedOutput(); err != nil {
+		t.Fatalf("create repo: %v, out: %s", err, string(out))
+	}
+
+	repoService := fix.service.NewRepoService("platform/build/kati")
+	treeResp, err := repoService.GetTree("ce34badf691d36e8048b63f89d1a86ee5fa4325c", "", true)
+	if err != nil {
+		t.Fatal("Tree:", err)
+	}
+
+	options := GitilesOptions{
+		Revision: "ce34badf691d36e8048b63f89d1a86ee5fa4325c",
+		CloneURL: fmt.Sprintf("http://%s/platform/build/kati", fix.testServer.addr),
+	}
+
+	fs := NewGitilesRoot(fix.cache, treeResp, repoService, options)
+	if err := fix.mount(fs); err != nil {
+		t.Fatal("mount", err)
+	}
+
+	if _, err := ioutil.ReadFile(filepath.Join(fix.mntDir, "AUTHORS")); err != nil {
+		t.Fatalf("ReadFile: %v", err)
+	}
+}
+
 func TestGitilesFSSharedNodes(t *testing.T) {
 	fix, err := newTestFixture()
 	if err != nil {
@@ -640,7 +688,7 @@
 	}
 
 	fixture.service, err = gitiles.NewService(gitiles.Options{
-		Address: fmt.Sprintf("http://%s", fixture.testServer.listener.Addr().String()),
+		Address: fmt.Sprintf("http://%s", fixture.testServer.addr),
 	})
 	if err != nil {
 		return nil, err