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