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 package org.eclipse.jgit.transport;
47
48 import static java.nio.charset.StandardCharsets.UTF_8;
49
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.text.MessageFormat;
53
54 import org.eclipse.jgit.errors.PackProtocolException;
55 import org.eclipse.jgit.internal.JGitText;
56 import org.eclipse.jgit.lib.MutableObjectId;
57 import org.eclipse.jgit.util.IO;
58 import org.eclipse.jgit.util.RawParseUtils;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62
63
64
65
66
67
68
69
70
71
72 public class PacketLineIn {
73 private static final Logger log = LoggerFactory.getLogger(PacketLineIn.class);
74
75
76 public static final String END = new StringBuilder(0).toString();
77
78
79
80
81
82
83 public static final String DELIM = new StringBuilder(0).toString();
84
85 static enum AckNackResult {
86
87 NAK,
88
89 ACK,
90
91 ACK_CONTINUE,
92
93 ACK_COMMON,
94
95 ACK_READY;
96 }
97
98 private final byte[] lineBuffer = new byte[SideBandOutputStream.SMALL_BUF];
99 private final InputStream in;
100 private long limit;
101
102
103
104
105
106
107
108 public PacketLineIn(InputStream in) {
109 this(in, 0);
110 }
111
112
113
114
115
116
117
118
119
120
121 public PacketLineIn(InputStream in, long limit) {
122 this.in = in;
123 this.limit = limit;
124 }
125
126 AckNackResult readACK(MutableObjectId returnedId) throws IOException {
127 final String line = readString();
128 if (line.length() == 0)
129 throw new PackProtocolException(JGitText.get().expectedACKNAKFoundEOF);
130 if ("NAK".equals(line))
131 return AckNackResult.NAK;
132 if (line.startsWith("ACK ")) {
133 returnedId.fromString(line.substring(4, 44));
134 if (line.length() == 44)
135 return AckNackResult.ACK;
136
137 final String arg = line.substring(44);
138 if (arg.equals(" continue"))
139 return AckNackResult.ACK_CONTINUE;
140 else if (arg.equals(" common"))
141 return AckNackResult.ACK_COMMON;
142 else if (arg.equals(" ready"))
143 return AckNackResult.ACK_READY;
144 }
145 if (line.startsWith("ERR "))
146 throw new PackProtocolException(line.substring(4));
147 throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedACKNAKGot, line));
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 public String readString() throws IOException {
164 int len = readLength();
165 if (len == 0) {
166 log.debug("git< 0000");
167 return END;
168 }
169 if (len == 1) {
170 log.debug("git< 0001");
171 return DELIM;
172 }
173
174 len -= 4;
175 if (len == 0) {
176 log.debug("git< ");
177 return "";
178 }
179
180 byte[] raw;
181 if (len <= lineBuffer.length)
182 raw = lineBuffer;
183 else
184 raw = new byte[len];
185
186 IO.readFully(in, raw, 0, len);
187 if (raw[len - 1] == '\n')
188 len--;
189
190 String s = RawParseUtils.decode(UTF_8, raw, 0, len);
191 log.debug("git< " + s);
192 return s;
193 }
194
195
196
197
198
199
200
201
202
203
204
205 public String readStringRaw() throws IOException {
206 int len = readLength();
207 if (len == 0) {
208 log.debug("git< 0000");
209 return END;
210 }
211
212 len -= 4;
213
214 byte[] raw;
215 if (len <= lineBuffer.length)
216 raw = lineBuffer;
217 else
218 raw = new byte[len];
219
220 IO.readFully(in, raw, 0, len);
221
222 String s = RawParseUtils.decode(UTF_8, raw, 0, len);
223 log.debug("git< " + s);
224 return s;
225 }
226
227 void discardUntilEnd() throws IOException {
228 for (;;) {
229 int n = readLength();
230 if (n == 0) {
231 break;
232 }
233 IO.skipFully(in, n - 4);
234 }
235 }
236
237 int readLength() throws IOException {
238 IO.readFully(in, lineBuffer, 0, 4);
239 int len;
240 try {
241 len = RawParseUtils.parseHexInt16(lineBuffer, 0);
242 } catch (ArrayIndexOutOfBoundsException err) {
243 throw invalidHeader();
244 }
245
246 if (len == 0) {
247 return 0;
248 } else if (len == 1) {
249 return 1;
250 } else if (len < 4) {
251 throw invalidHeader();
252 }
253
254 if (limit != 0) {
255 int n = len - 4;
256 if (limit < n) {
257 limit = -1;
258 try {
259 IO.skipFully(in, n);
260 } catch (IOException e) {
261
262 }
263 throw new InputOverLimitIOException();
264 }
265
266 limit = n < limit ? limit - n : -1;
267 }
268 return len;
269 }
270
271 private IOException invalidHeader() {
272 return new IOException(MessageFormat.format(JGitText.get().invalidPacketLineHeader,
273 "" + (char) lineBuffer[0] + (char) lineBuffer[1]
274 + (char) lineBuffer[2] + (char) lineBuffer[3]));
275 }
276
277
278
279
280
281
282 public static class InputOverLimitIOException extends IOException {
283 private static final long serialVersionUID = 1L;
284 }
285 }