|
5 | 5 | import static java.lang.System.currentTimeMillis; |
6 | 6 | import static java.util.Objects.requireNonNull; |
7 | 7 |
|
| 8 | +import io.netty.handler.codec.http.HttpHeaders; |
8 | 9 | import io.netty.handler.codec.http.HttpResponseStatus; |
9 | 10 | import java.io.IOException; |
10 | 11 | import java.net.URI; |
|
17 | 18 | import me.itzg.helpers.errors.GenericException; |
18 | 19 | import me.itzg.helpers.files.ReactiveFileUtils; |
19 | 20 | import reactor.core.publisher.Mono; |
| 21 | +import reactor.netty.ByteBufFlux; |
| 22 | +import reactor.netty.http.client.HttpClientResponse; |
20 | 23 |
|
21 | 24 | @Slf4j |
22 | 25 | @Accessors(fluent = true) |
@@ -65,69 +68,78 @@ public Mono<Path> assemble() { |
65 | 68 | final boolean useIfModifiedSince = skipUpToDate && Files.exists(file); |
66 | 69 |
|
67 | 70 | return useReactiveClient(client -> |
68 | | - client |
69 | | - .doOnRequest((httpClientRequest, connection) -> |
70 | | - statusHandler.call(FileDownloadStatus.DOWNLOADING, uri, file) |
71 | | - ) |
72 | | - .headers(headers -> { |
73 | | - if (useIfModifiedSince) { |
74 | | - try { |
75 | | - final FileTime lastModifiedTime; |
76 | | - lastModifiedTime = Files.getLastModifiedTime(file); |
77 | | - headers.set( |
78 | | - IF_MODIFIED_SINCE, |
79 | | - httpDateTimeFormatter.format(lastModifiedTime.toInstant()) |
| 71 | + getConcurrencyLimiter().limit( |
| 72 | + client |
| 73 | + .doOnRequest((httpClientRequest, connection) -> |
| 74 | + statusHandler.call(FileDownloadStatus.DOWNLOADING, uri, file) |
| 75 | + ) |
| 76 | + .headers(headers -> |
| 77 | + setupHeaders(headers, useIfModifiedSince) |
| 78 | + ) |
| 79 | + .followRedirect(true) |
| 80 | + .doOnRequest(debugLogRequest(log, "file fetch")) |
| 81 | + .get() |
| 82 | + .uri(uri) |
| 83 | + .response((resp, byteBufFlux) -> |
| 84 | + processResponse(resp, byteBufFlux, useIfModifiedSince, uri) |
| 85 | + ) |
| 86 | + .last() |
| 87 | + .contextWrite(context -> context.put("downloadStart", currentTimeMillis())) |
| 88 | + ) |
| 89 | + ); |
| 90 | + } |
| 91 | + |
| 92 | + private Mono<Path> processResponse(HttpClientResponse resp, ByteBufFlux byteBufFlux, boolean useIfModifiedSince, URI uri) { |
| 93 | + final HttpResponseStatus status = resp.status(); |
| 94 | + |
| 95 | + if (useIfModifiedSince && status == NOT_MODIFIED) { |
| 96 | + log.debug("The file {} is already up to date", file); |
| 97 | + statusHandler.call(FileDownloadStatus.SKIP_FILE_UP_TO_DATE, uri, file); |
| 98 | + return Mono.just(file); |
| 99 | + } |
| 100 | + |
| 101 | + if (notSuccess(resp)) { |
| 102 | + return failedRequestMono(resp, byteBufFlux.aggregate(), "Trying to retrieve file"); |
| 103 | + } |
| 104 | + |
| 105 | + if (notExpectedContentType(resp)) { |
| 106 | + return failedContentTypeMono(resp); |
| 107 | + } |
| 108 | + |
| 109 | + return ReactiveFileUtils.copyByteBufFluxToFile(byteBufFlux, file) |
| 110 | + .flatMap(fileSize -> { |
| 111 | + statusHandler.call(FileDownloadStatus.DOWNLOADED, uri, file); |
| 112 | + downloadedHandler.call(uri, file, fileSize); |
| 113 | + return Mono |
| 114 | + .deferContextual(contextView -> { |
| 115 | + if (log.isDebugEnabled()) { |
| 116 | + final long durationMillis = |
| 117 | + currentTimeMillis() - contextView.<Long>get("downloadStart"); |
| 118 | + log.debug("Download of {} took {} at {}", |
| 119 | + uri, formatDuration(durationMillis), transferRate(durationMillis, fileSize) |
80 | 120 | ); |
81 | | - } catch (IOException e) { |
82 | | - throw new GenericException("Unable to get last modified time of " + file, e); |
83 | 121 | } |
| 122 | + return Mono.just(file); |
| 123 | + }); |
| 124 | + }); |
| 125 | + } |
84 | 126 |
|
85 | | - } |
| 127 | + private void setupHeaders(HttpHeaders headers, boolean useIfModifiedSince) { |
| 128 | + if (useIfModifiedSince) { |
| 129 | + try { |
| 130 | + final FileTime lastModifiedTime; |
| 131 | + lastModifiedTime = Files.getLastModifiedTime(file); |
| 132 | + headers.set( |
| 133 | + IF_MODIFIED_SINCE, |
| 134 | + httpDateTimeFormatter.format(lastModifiedTime.toInstant()) |
| 135 | + ); |
| 136 | + } catch (IOException e) { |
| 137 | + throw new GenericException("Unable to get last modified time of " + file, e); |
| 138 | + } |
86 | 139 |
|
87 | | - applyHeaders(headers); |
88 | | - }) |
89 | | - .followRedirect(true) |
90 | | - .doOnRequest(debugLogRequest(log, "file fetch")) |
91 | | - .get() |
92 | | - .uri(uri) |
93 | | - .response((resp, byteBufFlux) -> { |
94 | | - final HttpResponseStatus status = resp.status(); |
| 140 | + } |
95 | 141 |
|
96 | | - if (useIfModifiedSince && status == NOT_MODIFIED) { |
97 | | - log.debug("The file {} is already up to date", file); |
98 | | - statusHandler.call(FileDownloadStatus.SKIP_FILE_UP_TO_DATE, uri, file); |
99 | | - return Mono.just(file); |
100 | | - } |
101 | | - |
102 | | - if (notSuccess(resp)) { |
103 | | - return failedRequestMono(resp, byteBufFlux.aggregate(), "Trying to retrieve file"); |
104 | | - } |
105 | | - |
106 | | - if (notExpectedContentType(resp)) { |
107 | | - return failedContentTypeMono(resp); |
108 | | - } |
109 | | - |
110 | | - return ReactiveFileUtils.copyByteBufFluxToFile(byteBufFlux, file) |
111 | | - .flatMap(fileSize -> { |
112 | | - statusHandler.call(FileDownloadStatus.DOWNLOADED, uri, file); |
113 | | - downloadedHandler.call(uri, file, fileSize); |
114 | | - return Mono |
115 | | - .deferContextual(contextView -> { |
116 | | - if (log.isDebugEnabled()) { |
117 | | - final long durationMillis = |
118 | | - currentTimeMillis() - contextView.<Long>get("downloadStart"); |
119 | | - log.debug("Download of {} took {} at {}", |
120 | | - uri, formatDuration(durationMillis), transferRate(durationMillis, fileSize) |
121 | | - ); |
122 | | - } |
123 | | - return Mono.just(file); |
124 | | - }); |
125 | | - }); |
126 | | - |
127 | | - }) |
128 | | - .last() |
129 | | - .contextWrite(context -> context.put("downloadStart", currentTimeMillis())) |
130 | | - ); |
| 142 | + applyHeaders(headers); |
131 | 143 | } |
132 | 144 |
|
133 | 145 | } |
0 commit comments