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);
162 } else if (isUploadPack(req)) {
163 sendUploadPackError(req, res, textForGit);
164 } else if (isReceivePack(req)) {
165 sendReceivePackError(req, res, textForGit);
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) throws IOException {
175 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
176 PacketLineOut pck = new PacketLineOut(buf);
177 String svc = req.getParameter("service");
178 pck.writeString("# service=" + svc + "\n");
179 pck.end();
180 pck.writeString("ERR " + textForGit);
181 send(req, res, infoRefsResultType(svc), buf.toByteArray());
182 }
183
184 private static void sendUploadPackError(HttpServletRequest req,
185 HttpServletResponse res, String textForGit) throws IOException {
186
187
188
189 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
190 PacketLineOut pckOut = new PacketLineOut(buf);
191 writePacket(pckOut, textForGit);
192 send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray());
193 }
194
195 private static void sendReceivePackError(HttpServletRequest req,
196 HttpServletResponse res, String textForGit) throws IOException {
197 ByteArrayOutputStream buf = new ByteArrayOutputStream(128);
198 PacketLineOut pckOut = new PacketLineOut(buf);
199
200 boolean sideband;
201 ReceivePack rp = (ReceivePack) req.getAttribute(ATTRIBUTE_HANDLER);
202 if (rp != null) {
203 try {
204 sideband = rp.isSideBand();
205 } catch (RequestNotYetReadException e) {
206 sideband = isReceivePackSideBand(req);
207 }
208 } else
209 sideband = isReceivePackSideBand(req);
210
211 if (sideband)
212 writeSideBand(buf, textForGit);
213 else
214 writePacket(pckOut, textForGit);
215 send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray());
216 }
217
218 private static boolean isReceivePackSideBand(HttpServletRequest req) {
219 try {
220
221
222
223
224 String line = new PacketLineIn(req.getInputStream()).readString();
225 FirstCommand parsed = FirstCommand.fromLine(line);
226 return parsed.getCapabilities().contains(CAPABILITY_SIDE_BAND_64K);
227 } catch (IOException e) {
228
229
230 return false;
231 }
232 }
233
234 private static void writeSideBand(OutputStream out, String textForGit)
235 throws IOException {
236 try (OutputStream msg = new SideBandOutputStream(CH_ERROR, SMALL_BUF,
237 out)) {
238 msg.write(Constants.encode("error: " + textForGit));
239 msg.flush();
240 }
241 }
242
243 private static void writePacket(PacketLineOut pckOut, String textForGit)
244 throws IOException {
245 pckOut.writeString("ERR " + textForGit);
246 }
247
248 private static void send(HttpServletRequest req, HttpServletResponse res,
249 String type, byte[] buf) throws IOException {
250 ServletUtils.consumeRequestBody(req);
251 res.setStatus(HttpServletResponse.SC_OK);
252 res.setContentType(type);
253 res.setContentLength(buf.length);
254 try (OutputStream os = res.getOutputStream()) {
255 os.write(buf);
256 }
257 }
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 public static String getResponseContentType(HttpServletRequest req) {
273 if (isInfoRefs(req))
274 return infoRefsResultType(req.getParameter("service"));
275 else if (isUploadPack(req))
276 return UPLOAD_PACK_RESULT_TYPE;
277 else if (isReceivePack(req))
278 return RECEIVE_PACK_RESULT_TYPE;
279 else
280 throw new IllegalArgumentException();
281 }
282
283 static String infoRefsResultType(String svc) {
284 return "application/x-" + svc + "-advertisement";
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298 public static String stripServiceSuffix(String path) {
299 for (String suffix : SERVICE_SUFFIXES) {
300 if (path.endsWith(suffix))
301 return path.substring(0, path.length() - suffix.length());
302 }
303 return path;
304 }
305
306
307
308
309
310
311
312
313 public static boolean isInfoRefs(HttpServletRequest req) {
314 return req.getRequestURI().endsWith(INFO_REFS_PATH)
315 && VALID_SERVICES.contains(req.getParameter("service"));
316 }
317
318
319
320
321
322
323
324
325 public static boolean isUploadPack(String pathOrUri) {
326 return pathOrUri != null && pathOrUri.endsWith(UPLOAD_PACK_PATH);
327 }
328
329
330
331
332
333
334
335
336 public static boolean isUploadPack(HttpServletRequest req) {
337 return isUploadPack(req.getRequestURI())
338 && UPLOAD_PACK_REQUEST_TYPE.equals(req.getContentType());
339 }
340
341
342
343
344
345
346
347
348 public static boolean isReceivePack(HttpServletRequest req) {
349 String uri = req.getRequestURI();
350 return uri != null && uri.endsWith(RECEIVE_PACK_PATH)
351 && RECEIVE_PACK_REQUEST_TYPE.equals(req.getContentType());
352 }
353
354 private GitSmartHttpTools() {
355 }
356 }