1
2
3
4
5
6
7
8
9
10
11
12
13 package org.eclipse.jgit.transport;
14
15 import static java.nio.charset.StandardCharsets.UTF_8;
16
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.UncheckedIOException;
20 import java.text.MessageFormat;
21 import java.util.Iterator;
22
23 import org.eclipse.jgit.errors.PackProtocolException;
24 import org.eclipse.jgit.internal.JGitText;
25 import org.eclipse.jgit.lib.MutableObjectId;
26 import org.eclipse.jgit.util.IO;
27 import org.eclipse.jgit.util.RawParseUtils;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31
32
33
34
35
36
37
38
39
40
41 public class PacketLineIn {
42 private static final Logger log = LoggerFactory.getLogger(PacketLineIn.class);
43
44
45
46
47
48
49
50
51
52 @Deprecated
53 public static final String END = new String();
54
55
56
57
58
59
60
61
62 @Deprecated
63 public static final String DELIM = new String();
64
65 enum AckNackResult {
66
67 NAK,
68
69 ACK,
70
71 ACK_CONTINUE,
72
73 ACK_COMMON,
74
75 ACK_READY;
76 }
77
78 private final byte[] lineBuffer = new byte[SideBandOutputStream.SMALL_BUF];
79 private final InputStream in;
80 private long limit;
81
82
83
84
85
86
87
88 public PacketLineIn(InputStream in) {
89 this(in, 0);
90 }
91
92
93
94
95
96
97
98
99
100
101 public PacketLineIn(InputStream in, long limit) {
102 this.in = in;
103 this.limit = limit;
104 }
105
106 AckNackResult readACK(MutableObjectId returnedId) throws IOException {
107 final String line = readString();
108 if (line.length() == 0)
109 throw new PackProtocolException(JGitText.get().expectedACKNAKFoundEOF);
110 if ("NAK".equals(line))
111 return AckNackResult.NAK;
112 if (line.startsWith("ACK ")) {
113 returnedId.fromString(line.substring(4, 44));
114 if (line.length() == 44)
115 return AckNackResult.ACK;
116
117 final String arg = line.substring(44);
118 switch (arg) {
119 case " continue":
120 return AckNackResult.ACK_CONTINUE;
121 case " common":
122 return AckNackResult.ACK_COMMON;
123 case " ready":
124 return AckNackResult.ACK_READY;
125 default:
126 break;
127 }
128 }
129 if (line.startsWith("ERR "))
130 throw new PackProtocolException(line.substring(4));
131 throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedACKNAKGot, line));
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 public String readString() throws IOException {
148 int len = readLength();
149 if (len == 0) {
150 log.debug("git< 0000");
151 return END;
152 }
153 if (len == 1) {
154 log.debug("git< 0001");
155 return DELIM;
156 }
157
158 len -= 4;
159 if (len == 0) {
160 log.debug("git< ");
161 return "";
162 }
163
164 byte[] raw;
165 if (len <= lineBuffer.length)
166 raw = lineBuffer;
167 else
168 raw = new byte[len];
169
170 IO.readFully(in, raw, 0, len);
171 if (raw[len - 1] == '\n')
172 len--;
173
174 String s = RawParseUtils.decode(UTF_8, raw, 0, len);
175 log.debug("git< " + s);
176 return s;
177 }
178
179
180
181
182
183
184
185
186
187
188
189 public PacketLineInIterator readStrings() throws IOException {
190 return new PacketLineInIterator(this);
191 }
192
193
194
195
196
197
198
199
200
201
202
203 public String readStringRaw() throws IOException {
204 int len = readLength();
205 if (len == 0) {
206 log.debug("git< 0000");
207 return END;
208 }
209
210 len -= 4;
211
212 byte[] raw;
213 if (len <= lineBuffer.length)
214 raw = lineBuffer;
215 else
216 raw = new byte[len];
217
218 IO.readFully(in, raw, 0, len);
219
220 String s = RawParseUtils.decode(UTF_8, raw, 0, len);
221 log.debug("git< " + s);
222 return s;
223 }
224
225
226
227
228
229
230
231
232
233 @SuppressWarnings({ "ReferenceEquality", "StringEquality" })
234 public static boolean isDelimiter(String s) {
235 return s == DELIM;
236 }
237
238
239
240
241
242
243
244
245 static String delimiter() {
246 return DELIM;
247 }
248
249
250
251
252
253
254
255
256 static String end() {
257 return END;
258 }
259
260
261
262
263
264
265
266
267
268 @SuppressWarnings({ "ReferenceEquality", "StringEquality" })
269 public static boolean isEnd(String s) {
270 return s == END;
271 }
272
273 void discardUntilEnd() throws IOException {
274 for (;;) {
275 int n = readLength();
276 if (n == 0) {
277 break;
278 }
279 IO.skipFully(in, n - 4);
280 }
281 }
282
283 int readLength() throws IOException {
284 IO.readFully(in, lineBuffer, 0, 4);
285 int len;
286 try {
287 len = RawParseUtils.parseHexInt16(lineBuffer, 0);
288 } catch (ArrayIndexOutOfBoundsException err) {
289 throw invalidHeader(err);
290 }
291
292 if (len == 0) {
293 return 0;
294 } else if (len == 1) {
295 return 1;
296 } else if (len < 4) {
297 throw invalidHeader();
298 }
299
300 if (limit != 0) {
301 int n = len - 4;
302 if (limit < n) {
303 limit = -1;
304 try {
305 IO.skipFully(in, n);
306 } catch (IOException e) {
307
308 }
309 throw new InputOverLimitIOException();
310 }
311
312 limit = n < limit ? limit - n : -1;
313 }
314 return len;
315 }
316
317 private IOException invalidHeader() {
318 return new IOException(MessageFormat.format(JGitText.get().invalidPacketLineHeader,
319 "" + (char) lineBuffer[0] + (char) lineBuffer[1]
320 + (char) lineBuffer[2] + (char) lineBuffer[3]));
321 }
322
323 private IOException invalidHeader(Throwable cause) {
324 IOException ioe = invalidHeader();
325 ioe.initCause(cause);
326 return ioe;
327 }
328
329
330
331
332
333
334 public static class InputOverLimitIOException extends IOException {
335 private static final long serialVersionUID = 1L;
336 }
337
338
339
340
341
342
343
344
345
346
347 public static class PacketLineInIterator implements Iterable<String> {
348 private PacketLineIn in;
349
350 private String current;
351
352 PacketLineInIterator(PacketLineIn in) throws IOException {
353 this.in = in;
354 current = in.readString();
355 }
356
357 @Override
358 public Iterator<String> iterator() {
359 return new Iterator<String>() {
360 @Override
361 public boolean hasNext() {
362 return !PacketLineIn.isEnd(current);
363 }
364
365 @Override
366 public String next() {
367 String next = current;
368 try {
369 current = in.readString();
370 } catch (IOException e) {
371 throw new UncheckedIOException(e);
372 }
373 return next;
374 }
375 };
376 }
377
378 }
379 }