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