1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.http.server;
12
13 import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
14 import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
15 import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
16 import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
17 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
18 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
19 import static org.eclipse.jgit.transport.SideBandOutputStream.SMALL_BUF;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
27
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30
31 import org.eclipse.jgit.internal.transport.parser.FirstCommand;
32 import org.eclipse.jgit.lib.Constants;
33 import org.eclipse.jgit.transport.PacketLineIn;
34 import org.eclipse.jgit.transport.PacketLineOut;
35 import org.eclipse.jgit.transport.ReceivePack;
36 import org.eclipse.jgit.transport.RequestNotYetReadException;
37 import org.eclipse.jgit.transport.SideBandOutputStream;
38
39
40
41
42 public class GitSmartHttpTools {
43 private static final String INFO_REFS = Constants.INFO_REFS;
44
45
46 public static final String UPLOAD_PACK = "git-upload-pack";
47
48
49 public static final String RECEIVE_PACK = "git-receive-pack";
50
51
52 public static final String UPLOAD_PACK_REQUEST_TYPE =
53 "application/x-git-upload-pack-request";
54
55
56 public static final String UPLOAD_PACK_RESULT_TYPE =
57 "application/x-git-upload-pack-result";
58
59
60 public static final String RECEIVE_PACK_REQUEST_TYPE =
61 "application/x-git-receive-pack-request";
62
63
64 public static final String RECEIVE_PACK_RESULT_TYPE =
65 "application/x-git-receive-pack-result";
66
67
68 public static final List<String> VALID_SERVICES =
69 Collections.unmodifiableList(Arrays.asList(new String[] {
70 UPLOAD_PACK, RECEIVE_PACK }));
71
72 private static final String INFO_REFS_PATH = "/" + INFO_REFS;
73 private static final String UPLOAD_PACK_PATH = "/" + UPLOAD_PACK;
74 private static final String RECEIVE_PACK_PATH = "/" + RECEIVE_PACK;
75
76 private static final List<String> SERVICE_SUFFIXES =
77 Collections.unmodifiableList(Arrays.asList(new String[] {
78 INFO_REFS_PATH, UPLOAD_PACK_PATH, RECEIVE_PACK_PATH }));
79
80
81
82
83
84
85
86
87 public static boolean isGitClient(HttpServletRequest req) {
88 return isInfoRefs(req) || isUploadPack(req) || isReceivePack(req);
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 public static void sendError(HttpServletRequest req,
109 HttpServletResponse res, int httpStatus) throws IOException {
110 sendError(req, res, httpStatus, null);
111 }
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 public static void sendError(HttpServletRequest req,
141 HttpServletResponse res, int httpStatus, String textForGit)
142 throws IOException {
143 if (textForGit == null || textForGit.length() == 0) {
144 switch (httpStatus) {
145 case SC_FORBIDDEN:
146 textForGit = HttpServerText.get().repositoryAccessForbidden;
147 break;
148 case SC_NOT_FOUND:
149 textForGit = HttpServerText.get().repositoryNotFound;
150 break;
151 case SC_INTERNAL_SERVER_ERROR:
152 textForGit = HttpServerText.get().internalServerError;
153 break;
154 default:
155 textForGit = "HTTP " + httpStatus;
156 break;
157 }
158 }
159
160 if (isInfoRefs(req)) {
161 sendInfoRefsError(req, res, textForGit, httpStatus);
162 } else if (isUploadPack(req)) {
163 sendUploadPackError(req, res, textForGit, httpStatus);
164 } else if (isReceivePack(req)) {
165 sendReceivePackError(req, res, textForGit, httpStatus);
166 } else {
167 if (httpStatus < 400)
168 ServletUtils.consumeRequestBody(req);
169 res.sendError(httpStatus, textForGit);
170 }
171 }
172
173 private static void sendInfoRefsError(HttpServletRequest req,
174 HttpServletResponse res, String textForGit, int httpStatus)
175 throws IOException {
176 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
177 PacketLineOut pck = new PacketLineOut(buf);
178 String svc = req.getParameter("service");
179 pck.writeString("# service=" + svc + "\n");
180 pck.end();
181 pck.writeString("ERR " + textForGit);
182 send(req, res, infoRefsResultType(svc), buf.toByteArray(), httpStatus);
183 }
184
185 private static void sendUploadPackError(HttpServletRequest req,
186 HttpServletResponse res, String textForGit, int httpStatus)
187 throws IOException {
188
189
190
191 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
192 PacketLineOut pckOut = new PacketLineOut(buf);
193 writePacket(pckOut, textForGit);
194 send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray(), httpStatus);
195 }
196
197 private static void sendReceivePackError(HttpServletRequest req,
198 HttpServletResponse res, String textForGit, int httpStatus)
199 throws IOException {
200 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
201 PacketLineOut pckOut = new PacketLineOut(buf);
202
203 boolean sideband;
204 ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
205 if (rp != null) {
206 try {
207 sideband = rp.isSideBand();
208 } catch (RequestNotYetReadException e) {
209 sideband = isReceivePackSideBand(req);
210 }
211 } else
212 sideband = isReceivePackSideBand(req);
213
214 if (sideband)
215 writeSideBand(buf, textForGit);
216 else
217 writePacket(pckOut, textForGit);
218 send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray(), httpStatus);
219 }
220
221 private static boolean isReceivePackSideBand(HttpServletRequest req) {
222 try {
223
224
225
226
227 String line = new PacketLineIn(req.getInputStream()).readString();
228 FirstCommand parsed = FirstCommand.fromLine(line);
229 return parsed.getCapabilities().contains(CAPABILITY_SIDE_BAND_64K);
230 } catch (IOException e) {
231
232
233 return false;
234 }
235 }
236
237 private static void writeSideBand(OutputStream out, String textForGit)
238 throws IOException {
239 try (OutputStream msg = new SideBandOutputStream(CH_ERROR, SMALL_BUF,
240 out)) {
241 msg.write(Constants.encode("error: " + textForGit));
242 msg.flush();
243 }
244 }
245
246 private static void writePacket(PacketLineOut pckOut, String textForGit)
247 throws IOException {
248 pckOut.writeString("ERR " + textForGit);
249 }
250
251 private static void send(HttpServletRequest req, HttpServletResponse res,
252 String type, byte[] buf, int httpStatus) throws IOException {
253 ServletUtils.consumeRequestBody(req);
254 res.setStatus(httpStatus);
255 res.setContentType(type);
256 res.setContentLength(buf.length);
257 try (OutputStream os = res.getOutputStream()) {
258 os.write(buf);
259 }
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275 public static String getResponseContentType(HttpServletRequest req) {
276 if (isInfoRefs(req))
277 return infoRefsResultType(req.getParameter("service"));
278 else if (isUploadPack(req))
279 return UPLOAD_PACK_RESULT_TYPE;
280 else if (isReceivePack(req))
281 return RECEIVE_PACK_RESULT_TYPE;
282 else
283 throw new IllegalArgumentException();
284 }
285
286 static String infoRefsResultType(String svc) {
287 return "application/x-" + svc + "-advertisement";
288 }
289
290
291
292
293
294
295
296
297
298
299
300
301 public static String stripServiceSuffix(String path) {
302 for (String suffix : SERVICE_SUFFIXES) {
303 if (path.endsWith(suffix))
304 return path.substring(0, path.length() - suffix.length());
305 }
306 return path;
307 }
308
309
310
311
312
313
314
315
316 public static boolean isInfoRefs(HttpServletRequest req) {
317 return req.getRequestURI().endsWith(INFO_REFS_PATH)
318 && VALID_SERVICES.contains(req.getParameter("service"));
319 }
320
321
322
323
324
325
326
327
328 public static boolean isUploadPack(String pathOrUri) {
329 return pathOrUri != null && pathOrUri.endsWith(UPLOAD_PACK_PATH);
330 }
331
332
333
334
335
336
337
338
339 public static boolean isUploadPack(HttpServletRequest req) {
340 return isUploadPack(req.getRequestURI())
341 && UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType());
342 }
343
344
345
346
347
348
349
350
351 public static boolean isReceivePack(HttpServletRequest req) {
352 String uri = req.getRequestURI();
353 return uri != null && uri.endsWith(RECEIVE_PACK_PATH)
354 && RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType());
355 }
356
357 private GitSmartHttpTools() {
358 }
359 }