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