16
16
17
17
package nextflow.file.http
18
18
19
+ import static nextflow.file.http.XFileSystemConfig.*
20
+
19
21
import java.nio.ByteBuffer
20
22
import java.nio.channels.SeekableByteChannel
21
23
import java.nio.file.AccessDeniedException
@@ -44,13 +46,9 @@ import groovy.transform.PackageScope
44
46
import groovy.util.logging.Slf4j
45
47
import nextflow.SysEnv
46
48
import nextflow.extension.FilesEx
49
+ import nextflow.file.FileHelper
47
50
import nextflow.util.InsensitiveMap
48
51
import sun.net.www.protocol.ftp.FtpURLConnection
49
-
50
- import static XFileSystemConfig.*
51
-
52
- import static nextflow.file.http.XFileSystemConfig.config
53
-
54
52
/**
55
53
* Implements a read-only JSR-203 compliant file system provider for http/ftp protocols
56
54
*
@@ -64,6 +62,7 @@ abstract class XFileSystemProvider extends FileSystemProvider {
64
62
65
63
private Map<URI , FileSystem > fileSystemMap = new LinkedHashMap<> (20 )
66
64
65
+ private static final int [] REDIRECT_CODES = [301 , 302 , 307 , 308 ]
67
66
68
67
protected static String config (String name , def defValue ) {
69
68
return SysEnv . containsKey(name) ? SysEnv . get(name) : defValue. toString()
@@ -185,18 +184,25 @@ abstract class XFileSystemProvider extends FileSystemProvider {
185
184
protected URLConnection toConnection0 (URL url , int attempt ) {
186
185
final conn = url. openConnection()
187
186
conn. setRequestProperty(" User-Agent" , ' Nextflow/httpfs' )
187
+ if ( conn instanceof HttpURLConnection ) {
188
+ // by default HttpURLConnection does redirect only within the same host
189
+ // disable the built-in to implement custom redirection logic (see below)
190
+ conn. setInstanceFollowRedirects(false )
191
+ }
188
192
if ( url. userInfo ) {
189
193
conn. setRequestProperty(" Authorization" , auth(url. userInfo));
190
194
}
191
195
else {
192
196
XAuthRegistry . instance. authorize(conn)
193
197
}
194
- if ( conn instanceof HttpURLConnection && conn. getResponseCode() in [ 307 , 308 ] && attempt < MAX_REDIRECT_HOPS ) {
198
+ if ( conn instanceof HttpURLConnection && conn. getResponseCode() in REDIRECT_CODES && attempt < MAX_REDIRECT_HOPS ) {
195
199
final header = InsensitiveMap . of(conn. getHeaderFields())
196
- String location = header. get(" Location" )?. get(0 )
197
- URL newPath = new URI (location). toURL()
198
- log. debug " Remote redirect URL: $newPath "
199
- return toConnection0(newPath, attempt+1 )
200
+ final location = header. get(" Location" )?. get(0 )
201
+ log. debug " Remote redirect location: $location "
202
+ final newUrl = new URI (absLocation(location,url)). toURL()
203
+ if ( url. protocol== ' https' && newUrl. protocol== ' http' )
204
+ throw new IOException (" Refuse to follow redirection from HTTPS to HTTP (unsafe) URL - origin: $url - target: $newUrl " )
205
+ return toConnection0(newUrl, attempt+1 )
200
206
}
201
207
else if ( conn instanceof HttpURLConnection && conn. getResponseCode() in config(). retryCodes() && attempt < config(). maxAttempts() ) {
202
208
final delay = (Math . pow(config(). backOffBase(), attempt) as long ) * config(). backOffDelay()
@@ -212,6 +218,18 @@ abstract class XFileSystemProvider extends FileSystemProvider {
212
218
return conn
213
219
}
214
220
221
+ protected String absLocation (String location , URL target ) {
222
+ assert location, " Missing location argument"
223
+ assert target, " Missing target URL argument"
224
+
225
+ final base = FileHelper . baseUrl(location)
226
+ if ( base )
227
+ return location
228
+ if ( ! location. startsWith(' /' ) )
229
+ location = ' /' + location
230
+ return " ${ target.protocol} ://${ target.authority} $location "
231
+ }
232
+
215
233
@Override
216
234
SeekableByteChannel newByteChannel (Path path , Set<? extends OpenOption > options , FileAttribute<?> ... attrs ) throws IOException {
217
235
0 commit comments