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<>();
121
122
123 protected final Set<ObjectId> additionalHaves = new HashSet<>();
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 if (myTimer == null) {
147 myTimer = new InterruptTimer(caller.getName() + "-Timer");
148 }
149 timeoutIn = new TimeoutInputStream(myIn, myTimer);
150 timeoutOut = new TimeoutOutputStream(myOut, myTimer);
151 timeoutIn.setTimeout(timeout * 1000);
152 timeoutOut.setTimeout(timeout * 1000);
153 myIn = timeoutIn;
154 myOut = timeoutOut;
155 }
156
157 in = myIn;
158 out = myOut;
159
160 pckIn = new PacketLineIn(in);
161 pckOut = new PacketLineOut(out);
162 outNeedsEnd = true;
163 }
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178 protected void readAdvertisedRefs() throws TransportException {
179 try {
180 readAdvertisedRefsImpl();
181 } catch (TransportException err) {
182 close();
183 throw err;
184 } catch (IOException err) {
185 close();
186 throw new TransportException(err.getMessage(), err);
187 } catch (RuntimeException err) {
188 close();
189 throw new TransportException(err.getMessage(), err);
190 }
191 }
192
193 private void readAdvertisedRefsImpl() throws IOException {
194 final LinkedHashMap<String, Ref> avail = new LinkedHashMap<>();
195 for (;;) {
196 String line;
197
198 try {
199 line = pckIn.readString();
200 } catch (EOFException eof) {
201 if (avail.isEmpty())
202 throw noRepository();
203 throw eof;
204 }
205 if (line == PacketLineIn.END)
206 break;
207
208 if (line.startsWith("ERR ")) {
209
210
211 throw new RemoteRepositoryException(uri, line.substring(4));
212 }
213
214 if (avail.isEmpty()) {
215 final int nul = line.indexOf('\0');
216 if (nul >= 0) {
217
218
219 for (String c : line.substring(nul + 1).split(" "))
220 remoteCapablities.add(c);
221 line = line.substring(0, nul);
222 }
223 }
224
225 String name = line.substring(41, line.length());
226 if (avail.isEmpty() && name.equals("capabilities^{}")) {
227
228
229 continue;
230 }
231
232 final ObjectId id = ObjectId.fromString(line.substring(0, 40));
233 if (name.equals(".have")) {
234 additionalHaves.add(id);
235 } else if (name.endsWith("^{}")) {
236 name = name.substring(0, name.length() - 3);
237 final Ref prior = avail.get(name);
238 if (prior == null)
239 throw new PackProtocolException(uri, MessageFormat.format(
240 JGitText.get().advertisementCameBefore, name, name));
241
242 if (prior.getPeeledObjectId() != null)
243 throw duplicateAdvertisement(name + "^{}");
244
245 avail.put(name, new ObjectIdRef.PeeledTag(
246 Ref.Storage.NETWORK, name, prior.getObjectId(), id));
247 } else {
248 final Ref prior = avail.put(name, new ObjectIdRef.PeeledNonTag(
249 Ref.Storage.NETWORK, name, id));
250 if (prior != null)
251 throw duplicateAdvertisement(name);
252 }
253 }
254 available(avail);
255 }
256
257
258
259
260
261
262
263
264
265
266 protected TransportException noRepository() {
267 return new NoRemoteRepositoryException(uri, JGitText.get().notFound);
268 }
269
270 protected boolean isCapableOf(final String option) {
271 return remoteCapablities.contains(option);
272 }
273
274 protected boolean wantCapability(final StringBuilder b, final String option) {
275 if (!isCapableOf(option))
276 return false;
277 b.append(' ');
278 b.append(option);
279 return true;
280 }
281
282 protected void addUserAgentCapability(StringBuilder b) {
283 String a = UserAgent.get();
284 if (a != null && UserAgent.hasAgent(remoteCapablities)) {
285 b.append(' ').append(OPTION_AGENT).append('=').append(a);
286 }
287 }
288
289 @Override
290 public String getPeerUserAgent() {
291 return UserAgent.getAgent(remoteCapablities, super.getPeerUserAgent());
292 }
293
294 private PackProtocolException duplicateAdvertisement(final String name) {
295 return new PackProtocolException(uri, MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name));
296 }
297
298 @Override
299 public void close() {
300 if (out != null) {
301 try {
302 if (outNeedsEnd) {
303 outNeedsEnd = false;
304 pckOut.end();
305 }
306 out.close();
307 } catch (IOException err) {
308
309 } finally {
310 out = null;
311 pckOut = null;
312 }
313 }
314
315 if (in != null) {
316 try {
317 in.close();
318 } catch (IOException err) {
319
320 } finally {
321 in = null;
322 pckIn = null;
323 }
324 }
325
326 if (myTimer != null) {
327 try {
328 myTimer.terminate();
329 } finally {
330 myTimer = null;
331 timeoutIn = null;
332 timeoutOut = null;
333 }
334 }
335 }
336
337
338 protected void endOut() {
339 if (outNeedsEnd && out != null) {
340 try {
341 outNeedsEnd = false;
342 pckOut.end();
343 } catch (IOException e) {
344 try {
345 out.close();
346 } catch (IOException err) {
347
348 } finally {
349 out = null;
350 pckOut = null;
351 }
352 }
353 }
354 }
355 }