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 org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
47 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
48 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
49 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
50 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
51 import static org.eclipse.jgit.transport.SideBandOutputStream.SMALL_BUF;
52 import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
53 import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
54 import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
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.lib.Constants;
67 import org.eclipse.jgit.transport.PacketLineIn;
68 import org.eclipse.jgit.transport.PacketLineOut;
69 import org.eclipse.jgit.transport.ReceivePack;
70 import org.eclipse.jgit.transport.RequestNotYetReadException;
71 import org.eclipse.jgit.transport.SideBandOutputStream;
72 import org.eclipse.jgit.transport.UploadPack;
73
74
75
76
77 public class GitSmartHttpTools {
78 private static final String INFO_REFS = Constants.INFO_REFS;
79
80
81 public static final String UPLOAD_PACK = "git-upload-pack";
82
83
84 public static final String RECEIVE_PACK = "git-receive-pack";
85
86
87 public static final String UPLOAD_PACK_REQUEST_TYPE =
88 "application/x-git-upload-pack-request";
89
90
91 public static final String UPLOAD_PACK_RESULT_TYPE =
92 "application/x-git-upload-pack-result";
93
94
95 public static final String RECEIVE_PACK_REQUEST_TYPE =
96 "application/x-git-receive-pack-request";
97
98
99 public static final String RECEIVE_PACK_RESULT_TYPE =
100 "application/x-git-receive-pack-result";
101
102
103 public static final List<String> VALID_SERVICES =
104 Collections.unmodifiableList(Arrays.asList(new String[] {
105 UPLOAD_PACK, RECEIVE_PACK }));
106
107 private static final String INFO_REFS_PATH = "/" + INFO_REFS;
108 private static final String UPLOAD_PACK_PATH = "/" + UPLOAD_PACK;
109 private static final String RECEIVE_PACK_PATH = "/" + RECEIVE_PACK;
110
111 private static final List<String> SERVICE_SUFFIXES =
112 Collections.unmodifiableList(Arrays.asList(new String[] {
113 INFO_REFS_PATH, UPLOAD_PACK_PATH, RECEIVE_PACK_PATH }));
114
115
116
117
118
119
120
121
122 public static boolean isGitClient(HttpServletRequest req) {
123 return isInfoRefs(req) || isUploadPack(req) || isReceivePack(req);
124 }
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 public static void sendError(HttpServletRequest req,
144 HttpServletResponse res, int httpStatus) throws IOException {
145 sendError(req, res, httpStatus, null);
146 }
147
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 public static void sendError(HttpServletRequest req,
176 HttpServletResponse res, int httpStatus, String textForGit)
177 throws IOException {
178 if (textForGit == null || textForGit.length() == 0) {
179 switch (httpStatus) {
180 case SC_FORBIDDEN:
181 textForGit = HttpServerText.get().repositoryAccessForbidden;
182 break;
183 case SC_NOT_FOUND:
184 textForGit = HttpServerText.get().repositoryNotFound;
185 break;
186 case SC_INTERNAL_SERVER_ERROR:
187 textForGit = HttpServerText.get().internalServerError;
188 break;
189 default:
190 textForGit = "HTTP " + httpStatus;
191 break;
192 }
193 }
194
195 if (isInfoRefs(req)) {
196 sendInfoRefsError(req, res, textForGit);
197 } else if (isUploadPack(req)) {
198 sendUploadPackError(req, res, textForGit);
199 } else if (isReceivePack(req)) {
200 sendReceivePackError(req, res, textForGit);
201 } else {
202 if (httpStatus < 400)
203 ServletUtils.consumeRequestBody(req);
204 res.sendError(httpStatus);
205 }
206 }
207
208 private static void sendInfoRefsError(HttpServletRequest req,
209 HttpServletResponse res, String textForGit) throws IOException {
210 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
211 PacketLineOut pck = new PacketLineOut(buf);
212 String svc = req.getParameter("service");
213 pck.writeString("# service=" + svc + "\n");
214 pck.end();
215 pck.writeString("ERR " + textForGit);
216 send(req, res, infoRefsResultType(svc), buf.toByteArray());
217 }
218
219 private static void sendUploadPackError(HttpServletRequest req,
220 HttpServletResponse res, String textForGit) throws IOException {
221 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
222 PacketLineOut pckOut = new PacketLineOut(buf);
223
224 boolean sideband;
225 UploadPack up = (UploadPack) req.getAttribute(ATTRIBUTE_HANDLER);
226 if (up != null) {
227 try {
228 sideband = up.isSideBand();
229 } catch (RequestNotYetReadException e) {
230 sideband = isUploadPackSideBand(req);
231 }
232 } else
233 sideband = isUploadPackSideBand(req);
234
235 if (sideband)
236 writeSideBand(buf, textForGit);
237 else
238 writePacket(pckOut, textForGit);
239 send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray());
240 }
241
242 private static boolean isUploadPackSideBand(HttpServletRequest req) {
243 try {
244
245
246
247
248 String line = new PacketLineIn(req.getInputStream()).readString();
249 UploadPack.FirstLine parsed = new UploadPack.FirstLine(line);
250 return (parsed.getOptions().contains(OPTION_SIDE_BAND)
251 || parsed.getOptions().contains(OPTION_SIDE_BAND_64K));
252 } catch (IOException e) {
253
254
255 return false;
256 }
257 }
258
259 private static void sendReceivePackError(HttpServletRequest req,
260 HttpServletResponse res, String textForGit) throws IOException {
261 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
262 PacketLineOut pckOut = new PacketLineOut(buf);
263
264 boolean sideband;
265 ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
266 if (rp != null) {
267 try {
268 sideband = rp.isSideBand();
269 } catch (RequestNotYetReadException e) {
270 sideband = isReceivePackSideBand(req);
271 }
272 } else
273 sideband = isReceivePackSideBand(req);
274
275 if (sideband)
276 writeSideBand(buf, textForGit);
277 else
278 writePacket(pckOut, textForGit);
279 send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray());
280 }
281
282 private static boolean isReceivePackSideBand(HttpServletRequest req) {
283 try {
284
285
286
287
288 String line = new PacketLineIn(req.getInputStream()).readString();
289 ReceivePack.FirstLine parsed = new ReceivePack.FirstLine(line);
290 return parsed.getCapabilities().contains(CAPABILITY_SIDE_BAND_64K);
291 } catch (IOException e) {
292
293
294 return false;
295 }
296 }
297
298 private static void writeSideBand(OutputStream out, String textForGit)
299 throws IOException {
300 @SuppressWarnings("resource" )
301 OutputStream msg = new SideBandOutputStream(CH_ERROR, SMALL_BUF, out);
302 msg.write(Constants.encode("error: " + textForGit));
303 msg.flush();
304 }
305
306 private static void writePacket(PacketLineOut pckOut, String textForGit)
307 throws IOException {
308 pckOut.writeString("error: " + textForGit);
309 }
310
311 private static void send(HttpServletRequest req, HttpServletResponse res,
312 String type, byte[] buf) throws IOException {
313 ServletUtils.consumeRequestBody(req);
314 res.setStatus(HttpServletResponse.SC_OK);
315 res.setContentType(type);
316 res.setContentLength(buf.length);
317 OutputStream os = res.getOutputStream();
318 try {
319 os.write(buf);
320 } finally {
321 os.close();
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 }