Extract readPackedRefs from TransportSftp for reuse
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / transport / TransportHttp.java
blob2f28f2c1168a559200e5a6726403633728d42b08
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.transport;
40 import java.io.BufferedReader;
41 import java.io.FileNotFoundException;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.net.ConnectException;
45 import java.net.MalformedURLException;
46 import java.net.Proxy;
47 import java.net.ProxySelector;
48 import java.net.URISyntaxException;
49 import java.net.URL;
50 import java.net.URLConnection;
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.Map;
54 import java.util.TreeMap;
56 import org.spearce.jgit.errors.NotSupportedException;
57 import org.spearce.jgit.errors.PackProtocolException;
58 import org.spearce.jgit.errors.TransportException;
59 import org.spearce.jgit.lib.ObjectId;
60 import org.spearce.jgit.lib.Ref;
61 import org.spearce.jgit.lib.Repository;
63 /**
64 * Transport over the non-Git aware HTTP and FTP protocol.
65 * <p>
66 * The HTTP transport does not require any specialized Git support on the remote
67 * (server side) repository. Object files are retrieved directly through
68 * standard HTTP GET requests, making it easy to serve a Git repository through
69 * a standard web host provider that does not offer specific support for Git.
71 * @see WalkFetchConnection
73 class TransportHttp extends WalkTransport {
74 static boolean canHandle(final URIish uri) {
75 if (!uri.isRemote())
76 return false;
77 final String s = uri.getScheme();
78 return "http".equals(s) || "https".equals(s) || "ftp".equals(s);
81 private final URL baseUrl;
83 private final URL objectsUrl;
85 private final ProxySelector proxySelector;
87 TransportHttp(final Repository local, final URIish uri)
88 throws NotSupportedException {
89 super(local, uri);
90 try {
91 String uriString = uri.toString();
92 if (!uriString.endsWith("/"))
93 uriString += "/";
94 baseUrl = new URL(uriString);
95 objectsUrl = new URL(baseUrl, "objects/");
96 } catch (MalformedURLException e) {
97 throw new NotSupportedException("Invalid URL " + uri, e);
99 proxySelector = ProxySelector.getDefault();
102 @Override
103 public FetchConnection openFetch() throws TransportException {
104 final HttpObjectDB c = new HttpObjectDB(objectsUrl);
105 final WalkFetchConnection r = new WalkFetchConnection(this, c);
106 r.available(c.readAdvertisedRefs());
107 return r;
110 Proxy proxyFor(final URL u) throws URISyntaxException {
111 return proxySelector.select(u.toURI()).get(0);
114 class HttpObjectDB extends WalkRemoteObjectDatabase {
115 private final URL objectsUrl;
117 HttpObjectDB(final URL b) {
118 objectsUrl = b;
121 @Override
122 URIish getURI() {
123 return new URIish(objectsUrl);
126 @Override
127 Collection<WalkRemoteObjectDatabase> getAlternates() throws IOException {
128 try {
129 return readAlternates(INFO_HTTP_ALTERNATES);
130 } catch (FileNotFoundException err) {
131 // Fall through.
134 try {
135 return readAlternates(INFO_ALTERNATES);
136 } catch (FileNotFoundException err) {
137 // Fall through.
140 return null;
143 @Override
144 WalkRemoteObjectDatabase openAlternate(final String location)
145 throws IOException {
146 return new HttpObjectDB(new URL(objectsUrl, location));
149 @Override
150 Collection<String> getPackNames() throws IOException {
151 final Collection<String> packs = new ArrayList<String>();
152 try {
153 final BufferedReader br = openReader(INFO_PACKS);
154 try {
155 for (;;) {
156 final String s = br.readLine();
157 if (s == null || s.length() == 0)
158 break;
159 if (!s.startsWith("P pack-") || !s.endsWith(".pack"))
160 throw invalidAdvertisement(s);
161 packs.add(s.substring(2));
163 return packs;
164 } finally {
165 br.close();
167 } catch (FileNotFoundException err) {
168 return packs;
172 @Override
173 FileStream open(final String path) throws IOException {
174 final URL base = objectsUrl;
175 try {
176 final URL u = new URL(base, path);
177 final URLConnection c = u.openConnection(proxyFor(u));
178 final InputStream in = c.getInputStream();
179 final int len = c.getContentLength();
180 return new FileStream(in, len);
181 } catch (ConnectException ce) {
182 // The standard J2SE error message is not very useful.
184 if ("Connection timed out: connect".equals(ce.getMessage()))
185 throw new ConnectException("Connection timed out: " + base);
186 throw new ConnectException(ce.getMessage() + " " + base);
187 } catch (URISyntaxException e) {
188 final ConnectException err;
189 err = new ConnectException("Cannot determine proxy for " + base);
190 err.initCause(e);
191 throw err;
195 Map<String, Ref> readAdvertisedRefs() throws TransportException {
196 try {
197 final BufferedReader br = openReader(INFO_REFS);
198 try {
199 return readAdvertisedImpl(br);
200 } finally {
201 br.close();
203 } catch (IOException err) {
204 try {
205 throw new TransportException(new URL(objectsUrl, INFO_REFS)
206 + ": cannot read available refs", err);
207 } catch (MalformedURLException mue) {
208 throw new TransportException(objectsUrl + INFO_REFS
209 + ": cannot read available refs", err);
214 private Map<String, Ref> readAdvertisedImpl(final BufferedReader br)
215 throws IOException, PackProtocolException {
216 final TreeMap<String, Ref> avail = new TreeMap<String, Ref>();
217 for (;;) {
218 String line = br.readLine();
219 if (line == null)
220 break;
222 final int tab = line.indexOf('\t');
223 if (tab < 0)
224 throw invalidAdvertisement(line);
226 String name;
227 final ObjectId id;
229 name = line.substring(tab + 1);
230 id = ObjectId.fromString(line.substring(0, tab));
231 if (name.endsWith("^{}")) {
232 name = name.substring(0, name.length() - 3);
233 final Ref prior = avail.get(name);
234 if (prior == null)
235 throw outOfOrderAdvertisement(name);
237 if (prior.getPeeledObjectId() != null)
238 throw duplicateAdvertisement(name + "^{}");
240 avail.put(name, new Ref(Ref.Storage.NETWORK, name, prior
241 .getObjectId(), id));
242 } else {
243 final Ref prior = avail.put(name, new Ref(
244 Ref.Storage.NETWORK, name, id));
245 if (prior != null)
246 throw duplicateAdvertisement(name);
249 return avail;
252 private PackProtocolException outOfOrderAdvertisement(final String n) {
253 return new PackProtocolException("advertisement of " + n
254 + "^{} came before " + n);
257 private PackProtocolException invalidAdvertisement(final String n) {
258 return new PackProtocolException("invalid advertisement of " + n);
261 private PackProtocolException duplicateAdvertisement(final String n) {
262 return new PackProtocolException("duplicate advertisements of " + n);
265 @Override
266 void close() {
267 // We do not maintain persistent connections.