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.server;
44
45 import static java.nio.charset.StandardCharsets.UTF_8;
46 import static org.apache.http.HttpStatus.SC_FORBIDDEN;
47 import static org.apache.http.HttpStatus.SC_INSUFFICIENT_STORAGE;
48 import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
49 import static org.apache.http.HttpStatus.SC_NOT_FOUND;
50 import static org.apache.http.HttpStatus.SC_OK;
51 import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;
52 import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
53 import static org.apache.http.HttpStatus.SC_UNPROCESSABLE_ENTITY;
54 import static org.eclipse.jgit.lfs.lib.Constants.DOWNLOAD;
55 import static org.eclipse.jgit.lfs.lib.Constants.UPLOAD;
56 import static org.eclipse.jgit.lfs.lib.Constants.VERIFY;
57 import static org.eclipse.jgit.util.HttpSupport.HDR_AUTHORIZATION;
58
59 import java.io.BufferedReader;
60 import java.io.BufferedWriter;
61 import java.io.IOException;
62 import java.io.InputStreamReader;
63 import java.io.OutputStreamWriter;
64 import java.io.Reader;
65 import java.io.Writer;
66 import java.text.MessageFormat;
67 import java.util.List;
68
69 import javax.servlet.ServletException;
70 import javax.servlet.http.HttpServlet;
71 import javax.servlet.http.HttpServletRequest;
72 import javax.servlet.http.HttpServletResponse;
73
74 import org.eclipse.jgit.lfs.errors.LfsBandwidthLimitExceeded;
75 import org.eclipse.jgit.lfs.errors.LfsException;
76 import org.eclipse.jgit.lfs.errors.LfsInsufficientStorage;
77 import org.eclipse.jgit.lfs.errors.LfsRateLimitExceeded;
78 import org.eclipse.jgit.lfs.errors.LfsRepositoryNotFound;
79 import org.eclipse.jgit.lfs.errors.LfsRepositoryReadOnly;
80 import org.eclipse.jgit.lfs.errors.LfsUnauthorized;
81 import org.eclipse.jgit.lfs.errors.LfsUnavailable;
82 import org.eclipse.jgit.lfs.errors.LfsValidationError;
83 import org.eclipse.jgit.lfs.internal.LfsText;
84 import org.slf4j.Logger;
85 import org.slf4j.LoggerFactory;
86
87 import com.google.gson.FieldNamingPolicy;
88 import com.google.gson.Gson;
89 import com.google.gson.GsonBuilder;
90
91
92
93
94
95
96
97
98 public abstract class LfsProtocolServlet extends HttpServlet {
99 private static Logger LOG = LoggerFactory
100 .getLogger(LfsProtocolServlet.class);
101
102 private static final long serialVersionUID = 1L;
103
104 private static final String CONTENTTYPE_VND_GIT_LFS_JSON =
105 "application/vnd.git-lfs+json; charset=utf-8";
106
107 private static final int SC_RATE_LIMIT_EXCEEDED = 429;
108
109 private static final int SC_BANDWIDTH_LIMIT_EXCEEDED = 509;
110
111 private Gson gson = createGson();
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 @Deprecated
151 protected LargeFileRepository getLargeFileRepository(LfsRequest request,
152 String path) throws LfsException {
153 return getLargeFileRepository(request, path, null);
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 protected abstract LargeFileRepository getLargeFileRepository(
194 LfsRequest request, String path, String auth) throws LfsException;
195
196
197
198
199
200
201 protected static class LfsRequest {
202 private String operation;
203
204 private List<LfsObject> objects;
205
206
207
208
209
210
211 public String getOperation() {
212 return operation;
213 }
214
215
216
217
218
219
220 public List<LfsObject> getObjects() {
221 return objects;
222 }
223
224
225
226
227
228 public boolean isUpload() {
229 return operation.equals(UPLOAD);
230 }
231
232
233
234
235
236 public boolean isDownload() {
237 return operation.equals(DOWNLOAD);
238 }
239
240
241
242
243
244 public boolean isVerify() {
245 return operation.equals(VERIFY);
246 }
247 }
248
249 @Override
250 protected void doPost(HttpServletRequest req, HttpServletResponse res)
251 throws ServletException, IOException {
252 Writer w = new BufferedWriter(
253 new OutputStreamWriter(res.getOutputStream(), UTF_8));
254
255 Reader r = new BufferedReader(
256 new InputStreamReader(req.getInputStream(), UTF_8));
257 LfsRequest request = gson.fromJson(r, LfsRequest.class);
258 String path = req.getPathInfo();
259
260 res.setContentType(CONTENTTYPE_VND_GIT_LFS_JSON);
261 LargeFileRepository repo = null;
262 try {
263 repo = getLargeFileRepository(request, path,
264 req.getHeader(HDR_AUTHORIZATION));
265 if (repo == null) {
266 String error = MessageFormat
267 .format(LfsText.get().lfsFailedToGetRepository, path);
268 LOG.error(error);
269 throw new LfsException(error);
270 }
271 res.setStatus(SC_OK);
272 TransferHandler handler = TransferHandler
273 .forOperation(request.operation, repo, request.objects);
274 gson.toJson(handler.process(), w);
275 } catch (LfsValidationError e) {
276 sendError(res, w, SC_UNPROCESSABLE_ENTITY, e.getMessage());
277 } catch (LfsRepositoryNotFound e) {
278 sendError(res, w, SC_NOT_FOUND, e.getMessage());
279 } catch (LfsRepositoryReadOnly e) {
280 sendError(res, w, SC_FORBIDDEN, e.getMessage());
281 } catch (LfsRateLimitExceeded e) {
282 sendError(res, w, SC_RATE_LIMIT_EXCEEDED, e.getMessage());
283 } catch (LfsBandwidthLimitExceeded e) {
284 sendError(res, w, SC_BANDWIDTH_LIMIT_EXCEEDED, e.getMessage());
285 } catch (LfsInsufficientStorage e) {
286 sendError(res, w, SC_INSUFFICIENT_STORAGE, e.getMessage());
287 } catch (LfsUnavailable e) {
288 sendError(res, w, SC_SERVICE_UNAVAILABLE, e.getMessage());
289 } catch (LfsUnauthorized e) {
290 sendError(res, w, SC_UNAUTHORIZED, e.getMessage());
291 } catch (LfsException e) {
292 sendError(res, w, SC_INTERNAL_SERVER_ERROR, e.getMessage());
293 } finally {
294 w.flush();
295 }
296 }
297
298 static class Error {
299 String message;
300
301 Error(String m) {
302 this.message = m;
303 }
304 }
305
306 private void sendError(HttpServletResponse rsp, Writer writer, int status,
307 String message) {
308 rsp.setStatus(status);
309 gson.toJson(new Error(message), writer);
310 }
311
312 private Gson createGson() {
313 return new GsonBuilder()
314 .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
315 .disableHtmlEscaping()
316 .create();
317 }
318 }