1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package de.pdark.dsmp;
17
18 import java.io.BufferedInputStream;
19 import java.io.BufferedOutputStream;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileWriter;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.net.Socket;
27 import java.net.SocketException;
28 import java.net.URL;
29 import java.text.SimpleDateFormat;
30 import java.util.Date;
31 import java.util.HashMap;
32
33 import org.apache.commons.lang.StringUtils;
34 import org.apache.commons.lang.SystemUtils;
35 import org.apache.log4j.Logger;
36 import org.codehaus.plexus.digest.DigesterException;
37 import org.codehaus.plexus.digest.Md5Digester;
38 import org.codehaus.plexus.digest.Sha1Digester;
39
40 /**
41 * Handle a connection from a maven.
42 *
43 * @author digulla
44 *
45 */
46 public class RequestHandler extends Thread
47 {
48 public static final Logger log = Logger.getLogger(RequestHandler.class);
49
50 private Socket clientSocket;
51
52 public RequestHandler (Socket clientSocket)
53 {
54 this.clientSocket = clientSocket;
55 }
56
57 @Override
58 public void run ()
59 {
60 if (clientSocket == null)
61 throw new RuntimeException ("Connection is already closed");
62
63 try
64 {
65 log.debug ("Got connection from "+clientSocket.getInetAddress());
66
67 String line;
68 boolean keepAlive = false;
69 do
70 {
71 String downloadURL = null;
72 StringBuffer fullRequest = new StringBuffer (1024);
73 while ((line = readLine ()) != null)
74 {
75 if (line.length() == 0)
76 break;
77
78
79 fullRequest.append (line);
80 fullRequest.append ('\n');
81
82 if ("Proxy-Connection: keep-alive".equals (line))
83 keepAlive = true;
84
85 if (line.startsWith("GET "))
86 {
87 int pos = line.lastIndexOf(' ');
88 line = line.substring(4, pos);
89 downloadURL = line;
90 }
91 }
92
93 if (downloadURL == null)
94 {
95 if (line == null)
96 break;
97
98 log.error ("Found no URL to download in request:\n"+fullRequest.toString());
99 }
100 else
101 {
102 log.info ("Got request for "+downloadURL);
103 serveURL (downloadURL);
104 }
105 }
106 while (line != null && keepAlive);
107
108 log.debug ("Terminating connection with "+clientSocket.getInetAddress());
109 }
110 catch (Exception e)
111 {
112 log.error ("Conversation with client aborted", e);
113 }
114 finally
115 {
116 close();
117 }
118 }
119
120 public void close ()
121 {
122 try
123 {
124 if (out != null)
125 out.close();
126 }
127 catch (Exception e)
128 {
129 log.error ("Exception while closing the outputstream", e);
130 }
131 out = null;
132
133 try
134 {
135 if (in != null)
136 in.close();
137 }
138 catch (Exception e)
139 {
140 log.error ("Exception while closing the inputstream", e);
141 }
142 in = null;
143
144 try
145 {
146 if (clientSocket != null)
147 clientSocket.close();
148 }
149 catch (Exception e)
150 {
151 log.error ("Exception while closing the socket", e);
152 }
153 clientSocket = null;
154 }
155
156 private void serveURL (String downloadURL) throws IOException
157 {
158 URL url = new URL (downloadURL);
159 url = Config.getMirror (url);
160
161 if (!"http".equals(url.getProtocol()))
162 throw new IOException ("Can only handle HTTP requests, got "+downloadURL);
163
164 File f = getPatchFile(url);
165 if (!f.exists())
166 f = getCacheFile(url);
167
168 if (!f.exists())
169 {
170 ProxyDownload d = new ProxyDownload (url, f);
171 try
172 {
173 d.download();
174 }
175 catch (DownloadFailed e)
176 {
177 log.error(e.getMessage());
178
179 println (e.getStatusLine());
180 println ();
181 getOut().flush();
182 return;
183 }
184 }
185 else
186 {
187 log.debug ("Serving from local cache "+f.getAbsolutePath());
188 }
189
190 println ("HTTP/1.1 200 OK");
191 print ("Date: ");
192 Date d = new Date (f.lastModified());
193 println (INTERNET_FORMAT.format(d));
194 print ("Content-length: ");
195 println (String.valueOf(f.length()));
196 print ("Content-type: ");
197 String ext = StringUtils.substringAfterLast(downloadURL, ".").toLowerCase();
198 String type = CONTENT_TYPES.get (ext);
199 if (type == null)
200 {
201 log.warn("Unknown extension "+ext+". Using content type text/plain.");
202 type = "text/plain";
203 }
204 println (type);
205 println ();
206 InputStream data = new BufferedInputStream (new FileInputStream (f));
207 IOUtils.copy (data, out);
208 data.close();
209 }
210
211 public static File getPatchFile (URL url)
212 {
213 File dir = Config.getPatchesDirectory();
214 File f = getCacheFile(url, dir);
215
216 if (!f.exists())
217 {
218 String ext = StringUtils.substringAfterLast(url.getPath(), ".").toLowerCase();
219 if ("md5".equals (ext) || "sha1".equals (ext))
220 {
221 File source = new File (StringUtils.substringBeforeLast(f.getAbsolutePath(), "."));
222 if (source.exists())
223 {
224 generateChecksum (source, f, ext);
225 }
226 }
227 }
228
229 return f;
230 }
231
232 public static void generateChecksum (File source, File f, String ext)
233 {
234 try
235 {
236 String checksum = null;
237 if ("md5".equals (ext))
238 {
239 Md5Digester digester = new Md5Digester ();
240 checksum = digester.calc(source);
241 }
242 else if ("sha1".equals (ext))
243 {
244 Sha1Digester digester = new Sha1Digester ();
245 checksum = digester.calc(source);
246 }
247
248 if (checksum != null)
249 {
250 FileWriter w = new FileWriter (f);
251 w.write(checksum);
252 w.write(SystemUtils.LINE_SEPARATOR);
253 w.close ();
254 }
255 }
256 catch (DigesterException e)
257 {
258 log.warn ("Error creating "+ext.toUpperCase()+" checksum for "+source.getAbsolutePath(), e);
259 }
260 catch (IOException e)
261 {
262 log.warn ("Error writing "+ext.toUpperCase()+" checksum for "+source.getAbsolutePath()+" to "+f.getAbsolutePath(), e);
263 }
264
265 }
266
267 public static File getCacheFile (URL url)
268 {
269 File dir = Config.getCacheDirectory();
270 return getCacheFile(url, dir);
271 }
272
273 public static File getCacheFile (URL url, File root)
274 {
275 root = new File (root, url.getHost());
276 if (url.getPort() != -1 && url.getPort() != 80)
277 root = new File (root, String.valueOf(url.getPort()));
278 File f = new File (root, url.getPath());
279 return f;
280 }
281
282 public final static HashMap<String,String> CONTENT_TYPES = new HashMap<String,String> ();
283 static {
284 CONTENT_TYPES.put ("xml", "application/xml");
285 CONTENT_TYPES.put ("pom", "application/xml");
286
287 CONTENT_TYPES.put ("jar", "application/java-archive");
288
289 CONTENT_TYPES.put ("md5", "text/plain");
290 CONTENT_TYPES.put ("sha1", "text/plain");
291 CONTENT_TYPES.put ("asc", "text/plain");
292
293 CONTENT_TYPES.put ("", "");
294 CONTENT_TYPES.put ("", "");
295 CONTENT_TYPES.put ("", "");
296 CONTENT_TYPES.put ("", "");
297 CONTENT_TYPES.put ("", "");
298 CONTENT_TYPES.put ("", "");
299 CONTENT_TYPES.put ("", "");
300 CONTENT_TYPES.put ("", "");
301 CONTENT_TYPES.put ("", "");
302 CONTENT_TYPES.put ("", "");
303 CONTENT_TYPES.put ("", "");
304 CONTENT_TYPES.put ("", "");
305 CONTENT_TYPES.put ("", "");
306 CONTENT_TYPES.put ("", "");
307 }
308
309 private final static SimpleDateFormat INTERNET_FORMAT = new SimpleDateFormat ("EEE, d MMM yyyy HH:mm:ss zzz");
310 private byte[] NEW_LINE = new byte[] { '\r', '\n' };
311
312 private void println (String string) throws IOException
313 {
314 print (string);
315 println ();
316 }
317
318 private void println () throws IOException
319 {
320 getOut().write(NEW_LINE);
321 }
322
323 private void print (String string) throws IOException
324 {
325 getOut().write (string.getBytes("ISO-8859-1"));
326 }
327
328 private OutputStream out;
329
330 protected OutputStream getOut () throws IOException
331 {
332 if (out == null)
333 out = new BufferedOutputStream (clientSocket.getOutputStream());
334
335 return out;
336 }
337
338 private BufferedInputStream in;
339
340 private String readLine () throws IOException
341 {
342 if (in == null)
343 in = new BufferedInputStream (clientSocket.getInputStream());
344
345 StringBuffer buffer = new StringBuffer (256);
346 int c;
347
348 try
349 {
350 while ((c = in.read()) != -1)
351 {
352 if (c == '\r')
353 continue;
354
355 if (c == '\n')
356 break;
357
358 buffer.append((char)c);
359 }
360 }
361 catch (SocketException e)
362 {
363 if ("Connection reset".equals (e.getMessage()))
364 return null;
365
366 throw e;
367 }
368
369 if (c == -1)
370 return null;
371
372 if (buffer.length() == 0)
373 return "";
374
375 return buffer.toString();
376 }
377 }