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 package org.eclipse.jgit.http.server;
45
46 import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
47 import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
48 import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
49 import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
50 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
51 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
52 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
53 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
54 import static org.eclipse.jgit.transport.SideBandOutputStream.SMALL_BUF;
55
56 import java.io.ByteArrayOutputStream;
57 import java.io.IOException;
58 import java.io.OutputStream;
59 import java.util.Arrays;
60 import java.util.Collections;
61 import java.util.List;
62
63 import javax.servlet.http.HttpServletRequest;
64 import javax.servlet.http.HttpServletResponse;
65
66 import org.eclipse.jgit.internal.transport.parser.FirstCommand;
67 import org.eclipse.jgit.internal.transport.parser.FirstWant;
68 import org.eclipse.jgit.lib.Constants;
69 import org.eclipse.jgit.transport.PacketLineIn;
70 import org.eclipse.jgit.transport.PacketLineOut;
71 import org.eclipse.jgit.transport.ReceivePack;
72 import org.eclipse.jgit.transport.RequestNotYetReadException;
73 import org.eclipse.jgit.transport.SideBandOutputStream;
74 import org.eclipse.jgit.transport.UploadPack;
75
76
77
78
79 public class GitSmartHttpTools {
80 private static final String INFO_REFS = Constants.INFO_REFS;
81
82
83 public static final String UPLOAD_PACK = "git-upload-pack";
84
85
86 public static final String RECEIVE_PACK = "git-receive-pack";
87
88
89 public static final String UPLOAD_PACK_REQUEST_TYPE =
90 "application/x-git-upload-pack-request";
91
92
93 public static final String UPLOAD_PACK_RESULT_TYPE =
94 "application/x-git-upload-pack-result";
95
96
97 public static final String RECEIVE_PACK_REQUEST_TYPE =
98 "application/x-git-receive-pack-request";
99
100
101 public static final String RECEIVE_PACK_RESULT_TYPE =
102 "application/x-git-receive-pack-result";
103
104
105 public static final List<String> VALID_SERVICES =
106 Collections.unmodifiableList(Arrays.asList(new String[] {
107 UPLOAD_PACK, RECEIVE_PACK }));
108
109 private static final String INFO_REFS_PATH = "/" + INFO_REFS;
110 private static final String UPLOAD_PACK_PATH = "/" + UPLOAD_PACK;
111 private static final String RECEIVE_PACK_PATH = "/" + RECEIVE_PACK;
112
113 private static final List<String> SERVICE_SUFFIXES =
114 Collections.unmodifiableList(Arrays.asList(new String[] {
115 INFO_REFS_PATH, UPLOAD_PACK_PATH, RECEIVE_PACK_PATH }));
116
117
118
119
120
121
122
123
124 public static boolean isGitClient(HttpServletRequest req) {
125 return isInfoRefs(req) || isUploadPack(req) || isReceivePack(req);
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public static void sendError(HttpServletRequest req,
146 HttpServletResponse res, int httpStatus) throws IOException {
147 sendError(req, res, httpStatus, null);
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177 public static void sendError(HttpServletRequest req,
178 HttpServletResponse res, int httpStatus, String textForGit)
179 throws IOException {
180 if (textForGit == null || textForGit.length() == 0) {
181 switch (httpStatus) {
182 case SC_FORBIDDEN:
183 textForGit = HttpServerText.get().repositoryAccessForbidden;
184 break;
185 case SC_NOT_FOUND:
186 textForGit = HttpServerText.get().repositoryNotFound;
187 break;
188 case SC_INTERNAL_SERVER_ERROR:
189 textForGit = HttpServerText.get().internalServerError;
190 break;
191 default:
192 textForGit = "HTTP " + httpStatus;
193 break;
194 }
195 }
196
197 if (isInfoRefs(req)) {
198 sendInfoRefsError(req, res, textForGit);
199 } else if (isUploadPack(req)) {
200 sendUploadPackError(req, res, textForGit);
201 } else if (isReceivePack(req)) {
202 sendReceivePackError(req, res, textForGit);
203 } else {
204 if (httpStatus < 400)
205 ServletUtils.consumeRequestBody(req);
206 res.sendError(httpStatus, textForGit);
207 }
208 }
209
210 private static void sendInfoRefsError(HttpServletRequest req,
211 HttpServletResponse res, String textForGit) throws IOException {
212 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
213 PacketLineOut pck = new PacketLineOut(buf);
214 String svc = req.getParameter("service");
215 pck.writeString("# service=" + svc + "\n");
216 pck.end();
217 pck.writeString("ERR " + textForGit);
218 send(req, res, infoRefsResultType(svc), buf.toByteArray());
219 }
220
221 private static void sendUploadPackError(HttpServletRequest req,
222 HttpServletResponse res, String textForGit) throws IOException {
223 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
224 PacketLineOut pckOut = new PacketLineOut(buf);
225
226 boolean sideband;
227 UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
228 if (up != null) {
229 try {
230 sideband = up.isSideBand();
231 } catch (RequestNotYetReadException e) {
232 sideband = isUploadPackSideBand(req);
233 }
234 } else
235 sideband = isUploadPackSideBand(req);
236
237 if (sideband)
238 writeSideBand(buf, textForGit);
239 else
240 writePacket(pckOut, textForGit);
241 send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray());
242 }
243
244 private static boolean isUploadPackSideBand(HttpServletRequest req) {
245 try {
246
247
248
249
250 String line = new PacketLineIn(req.getInputStream()).readString();
251 FirstWant parsed = FirstWant.fromLine(line);
252 return (parsed.getCapabilities().contains(OPTION_SIDE_BAND)
253 || parsed.getCapabilities().contains(OPTION_SIDE_BAND_64K));
254 } catch (IOException e) {
255
256
257 return false;
258 }
259 }
260
261 private static void sendReceivePackError(HttpServletRequest req,
262 HttpServletResponse res, String textForGit) throws IOException {
263 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
264 PacketLineOut pckOut = new PacketLineOut(buf);
265
266 boolean sideband;
267 ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
268 if (rp != null) {
269 try {
270 sideband = rp.isSideBand();
271 } catch (RequestNotYetReadException e) {
272 sideband = isReceivePackSideBand(req);
273 }
274 } else
275 sideband = isReceivePackSideBand(req);
276
277 if (sideband)
278 writeSideBand(buf, textForGit);
279 else
280 writePacket(pckOut, textForGit);
281 send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray());
282 }
283
284 private static boolean isReceivePackSideBand(HttpServletRequest req) {
285 try {
286
287
288
289
290 String line = new PacketLineIn(req.getInputStream()).readString();
291 FirstCommand parsed = FirstCommand.fromLine(line);
292 return parsed.getCapabilities().contains(CAPABILITY_SIDE_BAND_64K);
293 } catch (IOException e) {
294
295
296 return false;
297 }
298 }
299
300 private static void writeSideBand(OutputStream out, String textForGit)
301 throws IOException {
302 try (OutputStream msg = new SideBandOutputStream(CH_ERROR, SMALL_BUF,
303 out)) {
304 msg.write(Constants.encode("error: " + textForGit));
305 msg.flush();
306 }
307 }
308
309 private static void writePacket(PacketLineOut pckOut, String textForGit)
310 throws IOException {
311 pckOut.writeString("error: " + textForGit);
312 }
313
314 private static void send(HttpServletRequest req, HttpServletResponse res,
315 String type, byte[] buf) throws IOException {
316 ServletUtils.consumeRequestBody(req);
317 res.setStatus(HttpServletResponse.SC_OK);
318 res.setContentType(type);
319 res.setContentLength(buf.length);
320 try (OutputStream os = res.getOutputStream()) {
321 os.write(buf);
322 }
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338 public static String getResponseContentType(HttpServletRequest req) {
339 if (isInfoRefs(req))
340 return infoRefsResultType(req.getParameter("service"));
341 else if (isUploadPack(req))
342 return UPLOAD_PACK_RESULT_TYPE;
343 else if (isReceivePack(req))
344 return RECEIVE_PACK_RESULT_TYPE;
345 else
346 throw new IllegalArgumentException();
347 }
348
349 static String infoRefsResultType(String svc) {
350 return "application/x-" + svc + "-advertisement";
351 }
352
353
354
355
356
357
358
359
360
361
362
363
364 public static String stripServiceSuffix(String path) {
365 for (String suffix : SERVICE_SUFFIXES) {
366 if (path.endsWith(suffix))
367 return path.substring(0, path.length() - suffix.length());
368 }
369 return path;
370 }
371
372
373
374
375
376
377
378
379 public static boolean isInfoRefs(HttpServletRequest req) {
380 return req.getRequestURI().endsWith(INFO_REFS_PATH)
381 && VALID_SERVICES.contains(req.getParameter("service"));
382 }
383
384
385
386
387
388
389
390
391 public static boolean isUploadPack(String pathOrUri) {
392 return pathOrUri != null && pathOrUri.endsWith(UPLOAD_PACK_PATH);
393 }
394
395
396
397
398
399
400
401
402 public static boolean isUploadPack(HttpServletRequest req) {
403 return isUploadPack(req.getRequestURI())
404 && UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType());
405 }
406
407
408
409
410
411
412
413
414 public static boolean isReceivePack(HttpServletRequest req) {
415 String uri = req.getRequestURI();
416 return uri != null && uri.endsWith(RECEIVE_PACK_PATH)
417 && RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType());
418 }
419
420 private GitSmartHttpTools() {
421 }
422 }