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
;
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
;
42 * Transport over the non-Git aware HTTP and FTP protocol.
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
) {
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
{
69 String uriString
= uri
.toString();
70 if (!uriString
.endsWith("/"))
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();
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
);
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
) {
102 Collection
<WalkRemoteObjectDatabase
> getAlternates() throws IOException
{
104 return readAlternates(INFO_HTTP_ALTERNATES
);
105 } catch (FileNotFoundException err
) {
110 return readAlternates(INFO_ALTERNATES
);
111 } catch (FileNotFoundException err
) {
119 WalkRemoteObjectDatabase
openAlternate(final String location
)
121 return new HttpObjectDB(new URL(objectsUrl
, location
));
125 Collection
<String
> getPackNames() throws IOException
{
126 final Collection
<String
> packs
= new ArrayList
<String
>();
128 final BufferedReader br
= openReader(INFO_PACKS
);
131 final String s
= br
.readLine();
132 if (s
== null || s
.length() == 0)
134 if (!s
.startsWith("P pack-") || !s
.endsWith(".pack"))
135 throw invalidAdvertisement(s
);
136 packs
.add(s
.substring(2));
142 } catch (FileNotFoundException err
) {
148 FileStream
open(final String path
) throws IOException
{
149 final URL base
= objectsUrl
;
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
);
170 void readAdvertisedRefs(final WalkFetchConnection c
)
171 throws TransportException
{
173 final BufferedReader br
= openReader(INFO_REFS
);
175 readAdvertisedImpl(br
, c
);
179 } catch (IOException err
) {
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
>();
195 String line
= br
.readLine();
199 final int tab
= line
.indexOf('\t');
201 throw invalidAdvertisement(line
);
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
);
212 throw outOfOrderAdvertisement(name
);
214 if (prior
.getPeeledObjectId() != null)
215 throw duplicateAdvertisement(name
+ "^{}");
217 avail
.put(name
, new Ref(name
, prior
.getObjectId(), id
));
219 final Ref prior
= avail
.put(name
, new Ref(name
, id
));
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
);
242 // We do not maintain persistent connections.