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