2 // Getdown - application installer, patcher and launcher
3 // Copyright (C) 2004-2018 Getdown authors
4 // https://github.com/threerings/getdown/blob/master/LICENSE
6 package com.threerings.getdown.net;
9 import java.io.FileOutputStream;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.net.HttpURLConnection;
13 import java.net.Proxy;
15 import java.net.URLConnection;
16 import java.nio.channels.Channels;
17 import java.nio.channels.ReadableByteChannel;
19 import com.threerings.getdown.data.Resource;
20 import com.threerings.getdown.util.ConnectionUtil;
22 import static com.threerings.getdown.Log.log;
25 * Implements downloading files over HTTP
27 public class HTTPDownloader extends Downloader
29 public HTTPDownloader (Proxy proxy)
34 @Override protected long checkSize (Resource rsrc) throws IOException
36 URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0);
38 // if we're accessing our data via HTTP, we only need a HEAD request
39 if (conn instanceof HttpURLConnection) {
40 HttpURLConnection hcon = (HttpURLConnection)conn;
41 hcon.setRequestMethod("HEAD");
43 // make sure we got a satisfactory response code
44 if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) {
45 throw new IOException("Unable to check up-to-date for " +
46 rsrc.getRemote() + ": " + hcon.getResponseCode());
49 return conn.getContentLength();
52 // let it be known that we're done with this connection
53 conn.getInputStream().close();
57 @Override protected void download (Resource rsrc) throws IOException
59 // TODO: make FileChannel download impl (below) robust and allow apps to opt-into it via a
62 // download the resource from the specified URL
63 URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0);
66 // make sure we got a satisfactory response code
67 if (conn instanceof HttpURLConnection) {
68 HttpURLConnection hcon = (HttpURLConnection)conn;
69 if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) {
70 throw new IOException("Unable to download resource " + rsrc.getRemote() + ": " +
71 hcon.getResponseCode());
74 long actualSize = conn.getContentLength();
75 log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize);
76 long currentSize = 0L;
77 byte[] buffer = new byte[4*4096];
78 try (InputStream in = conn.getInputStream();
79 FileOutputStream out = new FileOutputStream(rsrc.getLocalNew())) {
81 // TODO: look to see if we have a download info file
82 // containing info on potentially partially downloaded data;
83 // if so, use a "Range: bytes=HAVE-" header.
85 // read in the file data
87 while ((read = in.read(buffer)) != -1) {
88 // abort the download if the downloader is aborted
89 if (_state == State.ABORTED) {
92 // write it out to our local copy
93 out.write(buffer, 0, read);
94 // note that we've downloaded some data
96 reportProgress(rsrc, currentSize, actualSize);
101 log.info("Downloading resource", "url", rsrc.getRemote(), "size", "unknown");
102 File localNew = rsrc.getLocalNew();
103 try (ReadableByteChannel rbc = Channels.newChannel(rsrc.getRemote().openStream());
104 FileOutputStream fos = new FileOutputStream(localNew)) {
105 // TODO: more work is needed here, transferFrom can fail to transfer the entire
106 // file, in which case it's not clear what we're supposed to do.. call it again?
107 // will it repeatedly fail?
108 fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
109 reportProgress(rsrc, localNew.length(), localNew.length());
114 protected final Proxy _proxy;