1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 package org.eclipse.jgit.transport;
48
49 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
50
51 import java.io.EOFException;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.OutputStream;
55 import java.text.MessageFormat;
56 import java.util.HashSet;
57 import java.util.LinkedHashMap;
58 import java.util.Set;
59
60 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
61 import org.eclipse.jgit.errors.PackProtocolException;
62 import org.eclipse.jgit.errors.RemoteRepositoryException;
63 import org.eclipse.jgit.errors.TransportException;
64 import org.eclipse.jgit.internal.JGitText;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.ObjectIdRef;
67 import org.eclipse.jgit.lib.Ref;
68 import org.eclipse.jgit.lib.Repository;
69 import org.eclipse.jgit.util.io.InterruptTimer;
70 import org.eclipse.jgit.util.io.TimeoutInputStream;
71 import org.eclipse.jgit.util.io.TimeoutOutputStream;
72
73
74
75
76
77
78
79
80
81 abstract class BasePackConnection extends BaseConnection {
82
83
84 protected final Repository local;
85
86
87 protected final URIish uri;
88
89
90 protected final Transport transport;
91
92
93 protected TimeoutInputStream timeoutIn;
94
95
96 protected TimeoutOutputStream timeoutOut;
97
98
99 private InterruptTimer myTimer;
100
101
102 protected InputStream in;
103
104
105 protected OutputStream out;
106
107
108 protected PacketLineIn pckIn;
109
110
111 protected PacketLineOut pckOut;
112
113
114 protected boolean outNeedsEnd;
115
116
117 protected boolean statelessRPC;
118
119
120 private final Set<String> remoteCapablities = new HashSet<String>();
121
122
123 protected final Set<ObjectId> additionalHaves = new HashSet<ObjectId>();
124
125 BasePackConnection(final PackTransport packTransport) {
126 transport = (Transport) packTransport;
127 local = transport.local;
128 uri = transport.uri;
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142 protected final void init(InputStream myIn, OutputStream myOut) {
143 final int timeout = transport.getTimeout();
144 if (timeout > 0) {
145 final Thread caller = Thread.currentThread();
146 myTimer = new InterruptTimer(caller.getName() + "-Timer");
147 timeoutIn = new TimeoutInputStream(myIn, myTimer);
148 timeoutOut = new TimeoutOutputStream(myOut, myTimer);
149 timeoutIn.setTimeout(timeout * 1000);
150 timeoutOut.setTimeout(timeout * 1000);
151 myIn = timeoutIn;
152 myOut = timeoutOut;
153 }
154
155 in = myIn;
156 out = myOut;
157
158 pckIn = new PacketLineIn(in);
159 pckOut = new PacketLineOut(out);
160 outNeedsEnd = true;
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 protected void readAdvertisedRefs() throws TransportException {
177 try {
178 readAdvertisedRefsImpl();
179 } catch (TransportException err) {
180 close();
181 throw err;
182 } catch (IOException err) {
183 close();
184 throw new TransportException(err.getMessage(), err);
185 } catch (RuntimeException err) {
186 close();
187 throw new TransportException(err.getMessage(), err);
188 }
189 }
190
191 private void readAdvertisedRefsImpl() throws IOException {
192 final LinkedHashMap<String, Ref> avail = new LinkedHashMap<String, Ref>();
193 for (;;) {
194 String line;
195
196 try {
197 line = pckIn.readString();
198 } catch (EOFException eof) {
199 if (avail.isEmpty())
200 throw noRepository();
201 throw eof;
202 }
203 if (line == PacketLineIn.END)
204 break;
205
206 if (line.startsWith("ERR ")) {
207
208
209 throw new RemoteRepositoryException(uri, line.substring(4));
210 }
211
212 if (avail.isEmpty()) {
213 final int nul = line.indexOf('\0');
214 if (nul >= 0) {
215
216
217 for (String c : line.substring(nul + 1).split(" "))
218 remoteCapablities.add(c);
219 line = line.substring(0, nul);
220 }
221 }
222
223 String name = line.substring(41, line.length());
224 if (avail.isEmpty() && name.equals("capabilities^{}")) {
225
226
227 continue;
228 }
229
230 final ObjectId id = ObjectId.fromString(line.substring(0, 40));
231 if (name.equals(".have")) {
232 additionalHaves.add(id);
233 } else if (name.endsWith("^{}")) {
234 name = name.substring(0, name.length() - 3);
235 final Ref prior = avail.get(name);
236 if (prior == null)
237 throw new PackProtocolException(uri, MessageFormat.format(
238 JGitText.get().advertisementCameBefore, name, name));
239
240 if (prior.getPeeledObjectId() != null)
241 throw duplicateAdvertisement(name + "^{}");
242
243 avail.put(name, new ObjectIdRef.PeeledTag(
244 Ref.Storage.NETWORK, name, prior.getObjectId(), id));
245 } else {
246 final Ref prior = avail.put(name, new ObjectIdRef.PeeledNonTag(
247 Ref.Storage.NETWORK, name, id));
248 if (prior != null)
249 throw duplicateAdvertisement(name);
250 }
251 }
252 available(avail);
253 }
254
255
256
257
258
259
260
261
262
263
264 protected TransportException noRepository() {
265 return new NoRemoteRepositoryException(uri, JGitText.get().notFound);
266 }
267
268 protected boolean isCapableOf(final String option) {
269 return remoteCapablities.contains(option);
270 }
271
272 protected boolean wantCapability(final StringBuilder b, final String option) {
273 if (!isCapableOf(option))
274 return false;
275 b.append(' ');
276 b.append(option);
277 return true;
278 }
279
280 protected void addUserAgentCapability(StringBuilder b) {
281 String a = UserAgent.get();
282 if (a != null && UserAgent.hasAgent(remoteCapablities)) {
283 b.append(' ').append(OPTION_AGENT).append('=').append(a);
284 }
285 }
286
287 @Override
288 public String getPeerUserAgent() {
289 return UserAgent.getAgent(remoteCapablities, super.getPeerUserAgent());
290 }
291
292 private PackProtocolException duplicateAdvertisement(final String name) {
293 return new PackProtocolException(uri, MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name));
294 }
295
296 @Override
297 public void close() {
298 if (out != null) {
299 try {
300 if (outNeedsEnd) {
301 outNeedsEnd = false;
302 pckOut.end();
303 }
304 out.close();
305 } catch (IOException err) {
306
307 } finally {
308 out = null;
309 pckOut = null;
310 }
311 }
312
313 if (in != null) {
314 try {
315 in.close();
316 } catch (IOException err) {
317
318 } finally {
319 in = null;
320 pckIn = null;
321 }
322 }
323
324 if (myTimer != null) {
325 try {
326 myTimer.terminate();
327 } finally {
328 myTimer = null;
329 timeoutIn = null;
330 timeoutOut = null;
331 }
332 }
333 }
334
335
336 protected void endOut() {
337 if (outNeedsEnd && out != null) {
338 try {
339 outNeedsEnd = false;
340 pckOut.end();
341 } catch (IOException e) {
342 try {
343 out.close();
344 } catch (IOException err) {
345
346 } finally {
347 out = null;
348 pckOut = null;
349 }
350 }
351 }
352 }
353 }