File size: 8,755 Bytes
d46f4a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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
141
142
143
144
145
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package net.minecraft.util;

import com.google.common.hash.Funnels;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.mojang.logging.LogUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.ServerSocket;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.Map;
import java.util.OptionalLong;
import javax.annotation.Nullable;
import net.minecraft.FileUtil;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;

public class HttpUtil {
    private static final Logger LOGGER = LogUtils.getLogger();

    private HttpUtil() {
    }

    public static Path downloadFile(
        Path p_312337_,
        URL p_309490_,
        Map<String, String> p_311545_,
        HashFunction p_312368_,
        @Nullable HashCode p_309569_,
        int p_312993_,
        Proxy p_311636_,
        HttpUtil.DownloadProgressListener p_310347_
    ) {
        HttpURLConnection httpurlconnection = null;
        InputStream inputstream = null;
        p_310347_.requestStart();
        Path path;
        if (p_309569_ != null) {
            path = cachedFilePath(p_312337_, p_309569_);

            try {
                if (checkExistingFile(path, p_312368_, p_309569_)) {
                    LOGGER.info("Returning cached file since actual hash matches requested");
                    p_310347_.requestFinished(true);
                    updateModificationTime(path);
                    return path;
                }
            } catch (IOException ioexception1) {
                LOGGER.warn("Failed to check cached file {}", path, ioexception1);
            }

            try {
                LOGGER.warn("Existing file {} not found or had mismatched hash", path);
                Files.deleteIfExists(path);
            } catch (IOException ioexception) {
                p_310347_.requestFinished(false);
                throw new UncheckedIOException("Failed to remove existing file " + path, ioexception);
            }
        } else {
            path = null;
        }

        Path $$18;
        try {
            httpurlconnection = (HttpURLConnection)p_309490_.openConnection(p_311636_);
            httpurlconnection.setInstanceFollowRedirects(true);
            p_311545_.forEach(httpurlconnection::setRequestProperty);
            inputstream = httpurlconnection.getInputStream();
            long i = httpurlconnection.getContentLengthLong();
            OptionalLong optionallong = i != -1L ? OptionalLong.of(i) : OptionalLong.empty();
            FileUtil.createDirectoriesSafe(p_312337_);
            p_310347_.downloadStart(optionallong);
            if (optionallong.isPresent() && optionallong.getAsLong() > (long)p_312993_) {
                throw new IOException("Filesize is bigger than maximum allowed (file is " + optionallong + ", limit is " + p_312993_ + ")");
            }

            if (path == null) {
                Path path3 = Files.createTempFile(p_312337_, "download", ".tmp");

                try {
                    HashCode hashcode1 = downloadAndHash(p_312368_, p_312993_, p_310347_, inputstream, path3);
                    Path path2 = cachedFilePath(p_312337_, hashcode1);
                    if (!checkExistingFile(path2, p_312368_, hashcode1)) {
                        Files.move(path3, path2, StandardCopyOption.REPLACE_EXISTING);
                    } else {
                        updateModificationTime(path2);
                    }

                    p_310347_.requestFinished(true);
                    return path2;
                } finally {
                    Files.deleteIfExists(path3);
                }
            }

            HashCode hashcode = downloadAndHash(p_312368_, p_312993_, p_310347_, inputstream, path);
            if (!hashcode.equals(p_309569_)) {
                throw new IOException("Hash of downloaded file (" + hashcode + ") did not match requested (" + p_309569_ + ")");
            }

            p_310347_.requestFinished(true);
            $$18 = path;
        } catch (Throwable throwable) {
            if (httpurlconnection != null) {
                InputStream inputstream1 = httpurlconnection.getErrorStream();
                if (inputstream1 != null) {
                    try {
                        LOGGER.error("HTTP response error: {}", IOUtils.toString(inputstream1, StandardCharsets.UTF_8));
                    } catch (Exception exception) {
                        LOGGER.error("Failed to read response from server");
                    }
                }
            }

            p_310347_.requestFinished(false);
            throw new IllegalStateException("Failed to download file " + p_309490_, throwable);
        } finally {
            IOUtils.closeQuietly(inputstream);
        }

        return $$18;
    }

    private static void updateModificationTime(Path p_311353_) {
        try {
            Files.setLastModifiedTime(p_311353_, FileTime.from(Instant.now()));
        } catch (IOException ioexception) {
            LOGGER.warn("Failed to update modification time of {}", p_311353_, ioexception);
        }
    }

    private static HashCode hashFile(Path p_310985_, HashFunction p_312320_) throws IOException {
        Hasher hasher = p_312320_.newHasher();

        try (
            OutputStream outputstream = Funnels.asOutputStream(hasher);
            InputStream inputstream = Files.newInputStream(p_310985_);
        ) {
            inputstream.transferTo(outputstream);
        }

        return hasher.hash();
    }

    private static boolean checkExistingFile(Path p_309713_, HashFunction p_311423_, HashCode p_312149_) throws IOException {
        if (Files.exists(p_309713_)) {
            HashCode hashcode = hashFile(p_309713_, p_311423_);
            if (hashcode.equals(p_312149_)) {
                return true;
            }

            LOGGER.warn("Mismatched hash of file {}, expected {} but found {}", p_309713_, p_312149_, hashcode);
        }

        return false;
    }

    private static Path cachedFilePath(Path p_310769_, HashCode p_311855_) {
        return p_310769_.resolve(p_311855_.toString());
    }

    private static HashCode downloadAndHash(HashFunction p_312168_, int p_311506_, HttpUtil.DownloadProgressListener p_311732_, InputStream p_312120_, Path p_310124_) throws IOException {
        HashCode hashcode;
        try (OutputStream outputstream = Files.newOutputStream(p_310124_, StandardOpenOption.CREATE)) {
            Hasher hasher = p_312168_.newHasher();
            byte[] abyte = new byte[8196];
            long j = 0L;

            int i;
            while ((i = p_312120_.read(abyte)) >= 0) {
                j += (long)i;
                p_311732_.downloadedBytes(j);
                if (j > (long)p_311506_) {
                    throw new IOException("Filesize was bigger than maximum allowed (got >= " + j + ", limit was " + p_311506_ + ")");
                }

                if (Thread.interrupted()) {
                    LOGGER.error("INTERRUPTED");
                    throw new IOException("Download interrupted");
                }

                outputstream.write(abyte, 0, i);
                hasher.putBytes(abyte, 0, i);
            }

            hashcode = hasher.hash();
        }

        return hashcode;
    }

    public static int getAvailablePort() {
        try {
            int i;
            try (ServerSocket serversocket = new ServerSocket(0)) {
                i = serversocket.getLocalPort();
            }

            return i;
        } catch (IOException ioexception) {
            return 25564;
        }
    }

    public static boolean isPortAvailable(int p_259872_) {
        if (p_259872_ >= 0 && p_259872_ <= 65535) {
            try {
                boolean flag;
                try (ServerSocket serversocket = new ServerSocket(p_259872_)) {
                    flag = serversocket.getLocalPort() == p_259872_;
                }

                return flag;
            } catch (IOException ioexception) {
                return false;
            }
        } else {
            return false;
        }
    }

    public interface DownloadProgressListener {
        void requestStart();

        void downloadStart(OptionalLong p_311723_);

        void downloadedBytes(long p_309797_);

        void requestFinished(boolean p_311898_);
    }
}