Use the Eclipse network proxy configuration for HTTP connections
[egit/qmx.git] / org.spearce.jgit / src / org / spearce / jgit / transport / TransportHttp.java
blob7b54300b37d19580fcb513e973c150548efe1a02
1 /*
2 * Copyright (C) 2008 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.transport;
19 import java.io.BufferedReader;
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.ConnectException;
24 import java.net.MalformedURLException;
25 import java.net.Proxy;
26 import java.net.ProxySelector;
27 import java.net.URISyntaxException;
28 import java.net.URL;
29 import java.net.URLConnection;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.TreeMap;
34 import org.spearce.jgit.errors.NotSupportedException;
35 import org.spearce.jgit.errors.PackProtocolException;
36 import org.spearce.jgit.errors.TransportException;
37 import org.spearce.jgit.lib.ObjectId;
38 import org.spearce.jgit.lib.Ref;
39 import org.spearce.jgit.lib.Repository;
41 /**
42 * Transport over the non-Git aware HTTP and FTP protocol.
43 * <p>
44 * The HTTP transport does not require any specialized Git support on the remote
45 * (server side) repository. Object files are retrieved directly through
46 * standard HTTP GET requests, making it easy to serve a Git repository through
47 * a standard web host provider that does not offer specific support for Git.
49 * @see WalkFetchConnection
51 class TransportHttp extends WalkTransport {
52 static boolean canHandle(final URIish uri) {
53 if (!uri.isRemote())
54 return false;
55 final String s = uri.getScheme();
56 return "http".equals(s) || "https".equals(s) || "ftp".equals(s);
59 private final URL baseUrl;
61 private final URL objectsUrl;
63 private final ProxySelector proxySelector;
65 TransportHttp(final Repository local, final URIish uri)
66 throws NotSupportedException {
67 super(local, uri);
68 try {
69 String uriString = uri.toString();
70 if (!uriString.endsWith("/"))
71 uriString += "/";
72 baseUrl = new URL(uriString);
73 objectsUrl = new URL(baseUrl, "objects/");
74 } catch (MalformedURLException e) {
75 throw new NotSupportedException("Invalid URL " + uri, e);
77 proxySelector = ProxySelector.getDefault();
80 @Override
81 public FetchConnection openFetch() throws TransportException {
82 final HttpObjectDB c = new HttpObjectDB(objectsUrl);
83 final WalkFetchConnection r = new WalkFetchConnection(this, c);
84 c.readAdvertisedRefs(r);
85 return r;
88 Proxy proxyFor(final URL u) throws URISyntaxException {
89 return proxySelector.select(u.toURI()).get(0);
92 class HttpObjectDB extends WalkRemoteObjectDatabase {
93 private static final String INFO_REFS = "../info/refs";
95 private final URL objectsUrl;
97 HttpObjectDB(final URL b) {
98 objectsUrl = b;
101 @Override
102 Collection<WalkRemoteObjectDatabase> getAlternates() throws IOException {
103 try {
104 return readAlternates(INFO_HTTP_ALTERNATES);
105 } catch (FileNotFoundException err) {
106 // Fall through.
109 try {
110 return readAlternates(INFO_ALTERNATES);
111 } catch (FileNotFoundException err) {
112 // Fall through.
115 return null;
118 @Override
119 WalkRemoteObjectDatabase openAlternate(final String location)
120 throws IOException {
121 return new HttpObjectDB(new URL(objectsUrl, location));
124 @Override
125 Collection<String> getPackNames() throws IOException {
126 final Collection<String> packs = new ArrayList<String>();
127 try {
128 final BufferedReader br = openReader(INFO_PACKS);
129 try {
130 for (;;) {
131 final String s = br.readLine();
132 if (s == null || s.length() == 0)
133 break;
134 if (!s.startsWith("P pack-") || !s.endsWith(".pack"))
135 throw invalidAdvertisement(s);
136 packs.add(s.substring(2));
138 return packs;
139 } finally {
140 br.close();
142 } catch (FileNotFoundException err) {
143 return packs;
147 @Override
148 FileStream open(final String path) throws IOException {
149 final URL base = objectsUrl;
150 try {
151 final URL u = new URL(base, path);
152 final URLConnection c = u.openConnection(proxyFor(u));
153 final InputStream in = c.getInputStream();
154 final int len = c.getContentLength();
155 return new FileStream(in, len);
156 } catch (ConnectException ce) {
157 // The standard J2SE error message is not very useful.
159 if ("Connection timed out: connect".equals(ce.getMessage()))
160 throw new ConnectException("Connection timed out: " + base);
161 throw new ConnectException(ce.getMessage() + " " + base);
162 } catch (URISyntaxException e) {
163 final ConnectException err;
164 err = new ConnectException("Cannot determine proxy for " + base);
165 err.initCause(e);
166 throw err;
170 void readAdvertisedRefs(final WalkFetchConnection c)
171 throws TransportException {
172 try {
173 final BufferedReader br = openReader(INFO_REFS);
174 try {
175 readAdvertisedImpl(br, c);
176 } finally {
177 br.close();
179 } catch (IOException err) {
180 try {
181 throw new TransportException(new URL(objectsUrl, INFO_REFS)
182 + ": cannot read available refs", err);
183 } catch (MalformedURLException mue) {
184 throw new TransportException(objectsUrl + INFO_REFS
185 + ": cannot read available refs", err);
190 private void readAdvertisedImpl(final BufferedReader br,
191 final WalkFetchConnection connection) throws IOException,
192 PackProtocolException {
193 final TreeMap<String, Ref> avail = new TreeMap<String, Ref>();
194 for (;;) {
195 String line = br.readLine();
196 if (line == null)
197 break;
199 final int tab = line.indexOf('\t');
200 if (tab < 0)
201 throw invalidAdvertisement(line);
203 String name;
204 final ObjectId id;
206 name = line.substring(tab + 1);
207 id = ObjectId.fromString(line.substring(0, tab));
208 if (name.endsWith("^{}")) {
209 name = name.substring(0, name.length() - 3);
210 final Ref prior = avail.get(name);
211 if (prior == null)
212 throw outOfOrderAdvertisement(name);
214 if (prior.getPeeledObjectId() != null)
215 throw duplicateAdvertisement(name + "^{}");
217 avail.put(name, new Ref(name, prior.getObjectId(), id));
218 } else {
219 final Ref prior = avail.put(name, new Ref(name, id));
220 if (prior != null)
221 throw duplicateAdvertisement(name);
224 connection.available(avail);
227 private PackProtocolException outOfOrderAdvertisement(final String n) {
228 return new PackProtocolException("advertisement of " + n
229 + "^{} came before " + n);
232 private PackProtocolException invalidAdvertisement(final String n) {
233 return new PackProtocolException("invalid advertisement of " + n);
236 private PackProtocolException duplicateAdvertisement(final String n) {
237 return new PackProtocolException("duplicate advertisements of " + n);
240 @Override
241 void close() {
242 // We do not maintain persistent connections.