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 package org.eclipse.jgit.lfs;
44
45 import static java.nio.charset.StandardCharsets.UTF_8;
46
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.InputStreamReader;
50 import java.io.OutputStream;
51 import java.nio.file.Files;
52 import java.nio.file.Path;
53 import java.text.MessageFormat;
54 import java.util.ArrayList;
55 import java.util.Collection;
56 import java.util.HashMap;
57 import java.util.Map;
58
59 import org.eclipse.jgit.attributes.FilterCommand;
60 import org.eclipse.jgit.attributes.FilterCommandFactory;
61 import org.eclipse.jgit.attributes.FilterCommandRegistry;
62 import org.eclipse.jgit.lfs.internal.LfsConnectionFactory;
63 import org.eclipse.jgit.lfs.internal.LfsText;
64 import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
65 import org.eclipse.jgit.lfs.lib.Constants;
66 import org.eclipse.jgit.lib.Repository;
67 import org.eclipse.jgit.transport.http.HttpConnection;
68 import org.eclipse.jgit.util.HttpSupport;
69
70 import com.google.gson.Gson;
71 import com.google.gson.stream.JsonReader;
72
73
74
75
76
77
78
79
80
81
82
83
84 public class SmudgeFilter extends FilterCommand {
85
86
87
88
89 private static final int MAX_COPY_BYTES = 1024 * 1024 * 256;
90
91
92
93
94
95 public final static FilterCommandFactory FACTORY = new FilterCommandFactory() {
96 @Override
97 public FilterCommand create(Repository db, InputStream in,
98 OutputStream out) throws IOException {
99 return new SmudgeFilter(db, in, out);
100 }
101 };
102
103
104
105
106 static void register() {
107 FilterCommandRegistry
108 .register(org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
109 + Constants.ATTR_FILTER_DRIVER_PREFIX
110 + org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_SMUDGE,
111 FACTORY);
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 public SmudgeFilter(Repository db, InputStream in, OutputStream out)
128 throws IOException {
129 super(in, out);
130 try {
131 Lfs lfs = new Lfs(db);
132 LfsPointer res = LfsPointer.parseLfsPointer(in);
133 if (res != null) {
134 AnyLongObjectId oid = res.getOid();
135 Path mediaFile = lfs.getMediaFile(oid);
136 if (!Files.exists(mediaFile)) {
137 downloadLfsResource(lfs, db, res);
138 }
139 this.in = Files.newInputStream(mediaFile);
140 }
141 } finally {
142 in.close();
143 }
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 public static Collection<Path> downloadLfsResource(Lfs lfs, Repository db,
160 LfsPointer... res) throws IOException {
161 Collection<Path> downloadedPaths = new ArrayList<>();
162 Map<String, LfsPointer> oidStr2ptr = new HashMap<>();
163 for (LfsPointer p : res) {
164 oidStr2ptr.put(p.getOid().name(), p);
165 }
166 HttpConnection lfsServerConn = LfsConnectionFactory.getLfsConnection(db,
167 HttpSupport.METHOD_POST, Protocol.OPERATION_DOWNLOAD);
168 Gson gson = Protocol.gson();
169 lfsServerConn.getOutputStream()
170 .write(gson
171 .toJson(LfsConnectionFactory
172 .toRequest(Protocol.OPERATION_DOWNLOAD, res))
173 .getBytes(UTF_8));
174 int responseCode = lfsServerConn.getResponseCode();
175 if (responseCode != HttpConnection.HTTP_OK) {
176 throw new IOException(
177 MessageFormat.format(LfsText.get().serverFailure,
178 lfsServerConn.getURL(),
179 Integer.valueOf(responseCode)));
180 }
181 try (JsonReader reader = new JsonReader(
182 new InputStreamReader(lfsServerConn.getInputStream(),
183 UTF_8))) {
184 Protocol.Response resp = gson.fromJson(reader,
185 Protocol.Response.class);
186 for (Protocol.ObjectInfo o : resp.objects) {
187 if (o.error != null) {
188 throw new IOException(
189 MessageFormat.format(LfsText.get().protocolError,
190 Integer.valueOf(o.error.code),
191 o.error.message));
192 }
193 if (o.actions == null) {
194 continue;
195 }
196 LfsPointer ptr = oidStr2ptr.get(o.oid);
197 if (ptr == null) {
198
199 continue;
200 }
201 if (ptr.getSize() != o.size) {
202 throw new IOException(MessageFormat.format(
203 LfsText.get().inconsistentContentLength,
204 lfsServerConn.getURL(), Long.valueOf(ptr.getSize()),
205 Long.valueOf(o.size)));
206 }
207 Protocol.Action downloadAction = o.actions
208 .get(Protocol.OPERATION_DOWNLOAD);
209 if (downloadAction == null || downloadAction.href == null) {
210 continue;
211 }
212
213 HttpConnection contentServerConn = LfsConnectionFactory
214 .getLfsContentConnection(db, downloadAction,
215 HttpSupport.METHOD_GET);
216
217 responseCode = contentServerConn.getResponseCode();
218 if (responseCode != HttpConnection.HTTP_OK) {
219 throw new IOException(
220 MessageFormat.format(LfsText.get().serverFailure,
221 contentServerConn.getURL(),
222 Integer.valueOf(responseCode)));
223 }
224 Path path = lfs.getMediaFile(ptr.getOid());
225 path.getParent().toFile().mkdirs();
226 try (InputStream contentIn = contentServerConn
227 .getInputStream()) {
228 long bytesCopied = Files.copy(contentIn, path);
229 if (bytesCopied != o.size) {
230 throw new IOException(MessageFormat.format(
231 LfsText.get().wrongAmoutOfDataReceived,
232 contentServerConn.getURL(),
233 Long.valueOf(bytesCopied),
234 Long.valueOf(o.size)));
235 }
236 downloadedPaths.add(path);
237 }
238 }
239 }
240 return downloadedPaths;
241 }
242
243
244 @Override
245 public int run() throws IOException {
246 try {
247 int totalRead = 0;
248 int length = 0;
249 if (in != null) {
250 byte[] buf = new byte[8192];
251 while ((length = in.read(buf)) != -1) {
252 out.write(buf, 0, length);
253 totalRead += length;
254
255
256
257
258
259 if (totalRead >= MAX_COPY_BYTES) {
260
261 return totalRead;
262 }
263 }
264 }
265
266 if (totalRead == 0 && length == -1) {
267
268 in.close();
269 out.close();
270 return length;
271 }
272
273 return totalRead;
274 } catch (IOException e) {
275 in.close();
276 out.close();
277 throw e;
278 }
279 }
280
281 }