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 package org.eclipse.jgit.transport;
46
47 import static org.eclipse.jgit.transport.SideBandOutputStream.HDR_SIZE;
48
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.OutputStream;
52 import java.io.Writer;
53 import java.text.MessageFormat;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
56
57 import org.eclipse.jgit.errors.PackProtocolException;
58 import org.eclipse.jgit.errors.TransportException;
59 import org.eclipse.jgit.internal.JGitText;
60 import org.eclipse.jgit.lib.Constants;
61 import org.eclipse.jgit.lib.ProgressMonitor;
62 import org.eclipse.jgit.util.IO;
63 import org.eclipse.jgit.util.RawParseUtils;
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 class SideBandInputStream extends InputStream {
81 static final int CH_DATA = 1;
82 static final int CH_PROGRESS = 2;
83 static final int CH_ERROR = 3;
84
85 private static Pattern P_UNBOUNDED = Pattern
86 .compile("^([\\w ]+): +(\\d+)(?:, done\\.)? *[\r\n]$");
87
88 private static Pattern P_BOUNDED = Pattern
89 .compile("^([\\w ]+): +\\d+% +\\( *(\\d+)/ *(\\d+)\\)(?:, done\\.)? *[\r\n]$");
90
91 private final InputStream rawIn;
92
93 private final PacketLineIn pckIn;
94
95 private final ProgressMonitor monitor;
96
97 private final Writer messages;
98
99 private final OutputStream out;
100
101 private String progressBuffer = "";
102
103 private String currentTask;
104
105 private int lastCnt;
106
107 private boolean eof;
108
109 private int channel;
110
111 private int available;
112
113 SideBandInputStream(final InputStream in, final ProgressMonitor progress,
114 final Writer messageStream, OutputStream outputStream) {
115 rawIn = in;
116 pckIn = new PacketLineIn(rawIn);
117 monitor = progress;
118 messages = messageStream;
119 currentTask = "";
120 out = outputStream;
121 }
122
123
124 @Override
125 public int read() throws IOException {
126 needDataPacket();
127 if (eof)
128 return -1;
129 available--;
130 return rawIn.read();
131 }
132
133
134 @Override
135 public int read(final byte[] b, int off, int len) throws IOException {
136 int r = 0;
137 while (len > 0) {
138 needDataPacket();
139 if (eof)
140 break;
141 final int n = rawIn.read(b, off, Math.min(len, available));
142 if (n < 0)
143 break;
144 r += n;
145 off += n;
146 len -= n;
147 available -= n;
148 }
149 return eof && r == 0 ? -1 : r;
150 }
151
152 private void needDataPacket() throws IOException {
153 if (eof || (channel == CH_DATA && available > 0))
154 return;
155 for (;;) {
156 available = pckIn.readLength();
157 if (available == 0) {
158 eof = true;
159 return;
160 }
161
162 channel = rawIn.read() & 0xff;
163 available -= HDR_SIZE;
164 if (available == 0)
165 continue;
166
167 switch (channel) {
168 case CH_DATA:
169 return;
170 case CH_PROGRESS:
171 progress(readString(available));
172 continue;
173 case CH_ERROR:
174 eof = true;
175 throw new TransportException(remote(readString(available)));
176 default:
177 throw new PackProtocolException(
178 MessageFormat.format(JGitText.get().invalidChannel,
179 Integer.valueOf(channel)));
180 }
181 }
182 }
183
184 private void progress(String pkt) throws IOException {
185 pkt = progressBuffer + pkt;
186 for (;;) {
187 final int lf = pkt.indexOf('\n');
188 final int cr = pkt.indexOf('\r');
189 final int s;
190 if (0 <= lf && 0 <= cr)
191 s = Math.min(lf, cr);
192 else if (0 <= lf)
193 s = lf;
194 else if (0 <= cr)
195 s = cr;
196 else
197 break;
198
199 doProgressLine(pkt.substring(0, s + 1));
200 pkt = pkt.substring(s + 1);
201 }
202 progressBuffer = pkt;
203 }
204
205 private void doProgressLine(final String msg) throws IOException {
206 Matcher matcher;
207
208 matcher = P_BOUNDED.matcher(msg);
209 if (matcher.matches()) {
210 final String taskname = matcher.group(1);
211 if (!currentTask.equals(taskname)) {
212 currentTask = taskname;
213 lastCnt = 0;
214 beginTask(Integer.parseInt(matcher.group(3)));
215 }
216 final int cnt = Integer.parseInt(matcher.group(2));
217 monitor.update(cnt - lastCnt);
218 lastCnt = cnt;
219 return;
220 }
221
222 matcher = P_UNBOUNDED.matcher(msg);
223 if (matcher.matches()) {
224 final String taskname = matcher.group(1);
225 if (!currentTask.equals(taskname)) {
226 currentTask = taskname;
227 lastCnt = 0;
228 beginTask(ProgressMonitor.UNKNOWN);
229 }
230 final int cnt = Integer.parseInt(matcher.group(2));
231 monitor.update(cnt - lastCnt);
232 lastCnt = cnt;
233 return;
234 }
235
236 messages.write(msg);
237 if (out != null)
238 out.write(msg.getBytes());
239 }
240
241 private void beginTask(final int totalWorkUnits) {
242 monitor.beginTask(remote(currentTask), totalWorkUnits);
243 }
244
245 private static String remote(String msg) {
246 String prefix = JGitText.get().prefixRemote;
247 StringBuilder r = new StringBuilder(prefix.length() + msg.length() + 1);
248 r.append(prefix);
249 if (prefix.length() > 0 && prefix.charAt(prefix.length() - 1) != ' ') {
250 r.append(' ');
251 }
252 r.append(msg);
253 return r.toString();
254 }
255
256 private String readString(final int len) throws IOException {
257 final byte[] raw = new byte[len];
258 IO.readFully(rawIn, raw, 0, len);
259 return RawParseUtils.decode(Constants.CHARSET, raw, 0, len);
260 }
261 }