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 @Override
124 public int read() throws IOException {
125 needDataPacket();
126 if (eof)
127 return -1;
128 available--;
129 return rawIn.read();
130 }
131
132 @Override
133 public int read(final byte[] b, int off, int len) throws IOException {
134 int r = 0;
135 while (len > 0) {
136 needDataPacket();
137 if (eof)
138 break;
139 final int n = rawIn.read(b, off, Math.min(len, available));
140 if (n < 0)
141 break;
142 r += n;
143 off += n;
144 len -= n;
145 available -= n;
146 }
147 return eof && r == 0 ? -1 : r;
148 }
149
150 private void needDataPacket() throws IOException {
151 if (eof || (channel == CH_DATA && available > 0))
152 return;
153 for (;;) {
154 available = pckIn.readLength();
155 if (available == 0) {
156 eof = true;
157 return;
158 }
159
160 channel = rawIn.read() & 0xff;
161 available -= HDR_SIZE;
162 if (available == 0)
163 continue;
164
165 switch (channel) {
166 case CH_DATA:
167 return;
168 case CH_PROGRESS:
169 progress(readString(available));
170 continue;
171 case CH_ERROR:
172 eof = true;
173 throw new TransportException(remote(readString(available)));
174 default:
175 throw new PackProtocolException(
176 MessageFormat.format(JGitText.get().invalidChannel,
177 Integer.valueOf(channel)));
178 }
179 }
180 }
181
182 private void progress(String pkt) throws IOException {
183 pkt = progressBuffer + pkt;
184 for (;;) {
185 final int lf = pkt.indexOf('\n');
186 final int cr = pkt.indexOf('\r');
187 final int s;
188 if (0 <= lf && 0 <= cr)
189 s = Math.min(lf, cr);
190 else if (0 <= lf)
191 s = lf;
192 else if (0 <= cr)
193 s = cr;
194 else
195 break;
196
197 doProgressLine(pkt.substring(0, s + 1));
198 pkt = pkt.substring(s + 1);
199 }
200 progressBuffer = pkt;
201 }
202
203 private void doProgressLine(final String msg) throws IOException {
204 Matcher matcher;
205
206 matcher = P_BOUNDED.matcher(msg);
207 if (matcher.matches()) {
208 final String taskname = matcher.group(1);
209 if (!currentTask.equals(taskname)) {
210 currentTask = taskname;
211 lastCnt = 0;
212 beginTask(Integer.parseInt(matcher.group(3)));
213 }
214 final int cnt = Integer.parseInt(matcher.group(2));
215 monitor.update(cnt - lastCnt);
216 lastCnt = cnt;
217 return;
218 }
219
220 matcher = P_UNBOUNDED.matcher(msg);
221 if (matcher.matches()) {
222 final String taskname = matcher.group(1);
223 if (!currentTask.equals(taskname)) {
224 currentTask = taskname;
225 lastCnt = 0;
226 beginTask(ProgressMonitor.UNKNOWN);
227 }
228 final int cnt = Integer.parseInt(matcher.group(2));
229 monitor.update(cnt - lastCnt);
230 lastCnt = cnt;
231 return;
232 }
233
234 messages.write(msg);
235 if (out != null)
236 out.write(msg.getBytes());
237 }
238
239 private void beginTask(final int totalWorkUnits) {
240 monitor.beginTask(remote(currentTask), totalWorkUnits);
241 }
242
243 private static String remote(String msg) {
244 String prefix = JGitText.get().prefixRemote;
245 StringBuilder r = new StringBuilder(prefix.length() + msg.length() + 1);
246 r.append(prefix);
247 if (prefix.length() > 0 && prefix.charAt(prefix.length() - 1) != ' ') {
248 r.append(' ');
249 }
250 r.append(msg);
251 return r.toString();
252 }
253
254 private String readString(final int len) throws IOException {
255 final byte[] raw = new byte[len];
256 IO.readFully(rawIn, raw, 0, len);
257 return RawParseUtils.decode(Constants.CHARSET, raw, 0, len);
258 }
259 }