Author: ggregory Date: Fri Nov 2 01:53:29 2018 New Revision: 1845527 URL: http://svn.apache.org/viewvc?rev=1845527&view=rev Log: [VFS-360] Migrate to HttpComponent HttpClient. This patch from Woonsan Ko (woonsan on GitHub) adds a new provider "http4" using Apache HttpComponents HttpClient 4. PR https://github.com/apache/commons-vfs/pull/38. Closes #38.
Added: commons/proper/vfs/trunk/commons-vfs2-examples/README.md commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileName.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileNameParser.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileContentInfoFactory.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileNameParser.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileProvider.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystem.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystemConfigBuilder.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4RandomAccessContent.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/MonitoredHttpResponseContentInputStream.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/package.html commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4s/ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4s/Http4sFileNameParser.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4s/Http4sFileProvider.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4s/package.html commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/URIBitSets.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/URIUtils.java commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/http4/ commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/http4/test/ commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/http4/test/Http4FilesCacheTestCase.java commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/http4/test/Http4GetContentInfoTest.java commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/http4/test/Http4ProviderTestCase.java commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/http4s/ commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/http4s/test/ commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/http4s/test/Http4sGetContentInfoTest.java Modified: commons/proper/vfs/trunk/commons-vfs2-examples/pom.xml commons/proper/vfs/trunk/commons-vfs2-examples/src/main/java/org/apache/commons/vfs2/example/Shell.java commons/proper/vfs/trunk/commons-vfs2-sandbox/pom.xml commons/proper/vfs/trunk/commons-vfs2/pom.xml commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileName.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileNameParser.java commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/url/UrlFileProvider.java commons/proper/vfs/trunk/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/DefaultFileContentTest.java commons/proper/vfs/trunk/pom.xml Added: commons/proper/vfs/trunk/commons-vfs2-examples/README.md URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2-examples/README.md?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2-examples/README.md (added) +++ commons/proper/vfs/trunk/commons-vfs2-examples/README.md Fri Nov 2 01:53:29 2018 @@ -0,0 +1,35 @@ +<!--- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +# Test Provider(s) with the Shell + +## Build modules in the parent folder + + mvn clean install + +## Test `http` and `https` providers + + mvn -Pshell -Dhttp + +## Test `http4` and `http4s` providers + + mvn -Pshell -Dhttp4 + +## Test `http`, `https`, `http4` and `http4s` providers together + + mvn -Pshell -Dhttp -Dhttp4 + Modified: commons/proper/vfs/trunk/commons-vfs2-examples/pom.xml URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2-examples/pom.xml?rev=1845527&r1=1845526&r2=1845527&view=diff ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2-examples/pom.xml (original) +++ commons/proper/vfs/trunk/commons-vfs2-examples/pom.xml Fri Nov 2 01:53:29 2018 @@ -51,11 +51,6 @@ <optional>true</optional> </dependency> <dependency> - <groupId>commons-httpclient</groupId> - <artifactId>commons-httpclient</artifactId> - <optional>true</optional> - </dependency> - <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <optional>true</optional> @@ -79,4 +74,67 @@ </resources> </build> + <profiles> + + <profile> + <id>shell</id> + <build> + <defaultGoal>validate</defaultGoal> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.6.0</version> + <executions> + <execution> + <phase>validate</phase> + <goals> + <goal>java</goal> + </goals> + </execution> + </executions> + <configuration> + <mainClass>org.apache.commons.vfs2.example.Shell</mainClass> + </configuration> + </plugin> + </plugins> + </build> + </profile> + + <profile> + <id>with-http</id> + <activation> + <activeByDefault>true</activeByDefault> + <property> + <name>http</name> + </property> + </activation> + <dependencies> + <dependency> + <groupId>commons-httpclient</groupId> + <artifactId>commons-httpclient</artifactId> + <scope>runtime</scope> + </dependency> + </dependencies> + </profile> + + <profile> + <id>with-http4</id> + <activation> + <activeByDefault>true</activeByDefault> + <property> + <name>http4</name> + </property> + </activation> + <dependencies> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <scope>runtime</scope> + </dependency> + </dependencies> + </profile> + + </profiles> + </project> Modified: commons/proper/vfs/trunk/commons-vfs2-examples/src/main/java/org/apache/commons/vfs2/example/Shell.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2-examples/src/main/java/org/apache/commons/vfs2/example/Shell.java?rev=1845527&r1=1845526&r2=1845527&view=diff ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2-examples/src/main/java/org/apache/commons/vfs2/example/Shell.java (original) +++ commons/proper/vfs/trunk/commons-vfs2-examples/src/main/java/org/apache/commons/vfs2/example/Shell.java Fri Nov 2 01:53:29 2018 @@ -38,7 +38,9 @@ import org.apache.commons.vfs2.FileType; import org.apache.commons.vfs2.FileUtil; import org.apache.commons.vfs2.Selectors; import org.apache.commons.vfs2.VFS; +import org.apache.commons.vfs2.impl.DefaultFileSystemManager; import org.apache.commons.vfs2.operations.FileOperationProvider; +import org.apache.commons.vfs2.provider.FileProvider; /** * A simple command-line shell for performing file operations. @@ -46,12 +48,30 @@ import org.apache.commons.vfs2.operation * See <a href="https://wiki.apache.org/commons/VfsExampleShell">Commons VFS Shell Examples</a> in Apache Commons Wiki. */ public final class Shell { + private final FileSystemManager mgr; private FileObject cwd; private final BufferedReader reader; private Shell() throws IOException { mgr = VFS.getManager(); + + // TODO: VFS-360 - Remove this manual registration of http4 once http4 becomes part of standard providers. + boolean httpClient4Available = false; + try { + Class.forName("org.apache.http.client.HttpClient"); + httpClient4Available = true; + final DefaultFileSystemManager manager = (DefaultFileSystemManager) VFS.getManager(); + if (!manager.hasProvider("http4")) { + manager.addProvider("http4", (FileProvider) Class.forName("org.apache.commons.vfs2.provider.http4.Http4FileProvider").newInstance()); + manager.addProvider("http4s", (FileProvider) Class.forName("org.apache.commons.vfs2.provider.http4s.Http4sFileProvider").newInstance()); + } + } catch (Exception e) { + if (httpClient4Available) { + e.printStackTrace(); + } + } + cwd = mgr.toFileObject(new File(System.getProperty("user.dir"))); reader = new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset())); } Modified: commons/proper/vfs/trunk/commons-vfs2-sandbox/pom.xml URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2-sandbox/pom.xml?rev=1845527&r1=1845526&r2=1845527&view=diff ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2-sandbox/pom.xml (original) +++ commons/proper/vfs/trunk/commons-vfs2-sandbox/pom.xml Fri Nov 2 01:53:29 2018 @@ -55,6 +55,11 @@ <optional>true</optional> </dependency> <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <optional>true</optional> + </dependency> + <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <optional>true</optional> Modified: commons/proper/vfs/trunk/commons-vfs2/pom.xml URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/pom.xml?rev=1845527&r1=1845526&r2=1845527&view=diff ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/pom.xml (original) +++ commons/proper/vfs/trunk/commons-vfs2/pom.xml Fri Nov 2 01:53:29 2018 @@ -75,6 +75,11 @@ <optional>true</optional> </dependency> <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <optional>true</optional> + </dependency> + <dependency> <groupId>org.apache.jackrabbit</groupId> <artifactId>jackrabbit-webdav</artifactId> <optional>true</optional> Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileName.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileName.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileName.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileName.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider; + +import java.net.URISyntaxException; + +import org.apache.commons.vfs2.FileName; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileType; +import org.apache.commons.vfs2.util.URIUtils; + +/** + * Generic file name that represents a URL. + */ +public class GenericURLFileName extends GenericFileName { + + private static final int BUFFER_SIZE = 250; + + private final String queryString; + + public GenericURLFileName(final String scheme, final String hostName, final int port, final int defaultPort, + final String userName, final String password, final String path, final FileType type, + final String queryString) { + super(scheme, hostName, port, defaultPort, userName, password, path, type); + this.queryString = queryString; + } + + /** + * Get the query string. + * + * @return the query string part of the filename + */ + public String getQueryString() { + return queryString; + } + + /** + * Get the path and query string e.g. /path/servlet?param1=true. + * + * @return the path and its query string + */ + public String getPathQuery() { + final StringBuilder sb = new StringBuilder(BUFFER_SIZE); + sb.append(getPath()); + sb.append("?"); + sb.append(getQueryString()); + + return sb.toString(); + } + + /** + * Get the path encoded suitable for url like filesystem e.g. (http, webdav). + * + * @param charset the charset used for the path encoding + * @return The encoded path. + * @throws URISyntaxException If an error occurs encoding the URI. + * @throws FileSystemException If some other error occurs. + */ + public String getPathQueryEncoded(final String charset) throws URISyntaxException, FileSystemException { + if (getQueryString() == null) { + if (charset != null) { + return URIUtils.encodePath(getPathDecoded(), charset); + } else { + return URIUtils.encodePath(getPathDecoded()); + } + } + + final StringBuilder sb = new StringBuilder(BUFFER_SIZE); + if (charset != null) { + sb.append(URIUtils.encodePath(getPathDecoded(), charset)); + } else { + sb.append(URIUtils.encodePath(getPathDecoded())); + } + sb.append("?"); + sb.append(getQueryString()); + return sb.toString(); + } + + /** + * Create a FileName. + * + * @param absPath The absolute path. + * @param type The FileType. + * @return The FileName + */ + @Override + public FileName createName(final String absPath, final FileType type) { + return new GenericURLFileName(getScheme(), getHostName(), getPort(), getDefaultPort(), getUserName(), getPassword(), + absPath, type, getQueryString()); + } + + /** + * Append query string to the uri. + * + * @return the uri + */ + @Override + protected String createURI() { + if (getQueryString() != null) { + final StringBuilder sb = new StringBuilder(BUFFER_SIZE); + sb.append(super.createURI()); + sb.append("?"); + sb.append(getQueryString()); + + return sb.toString(); + } + + return super.createURI(); + } + + /** + * Encode a URI. + * + * @param charset The character set. + * @return The encoded URI + * @throws FileSystemException if some other exception occurs. + * @throws URISyntaxException if an exception occurs encoding the URI. + */ + public String getURIEncoded(final String charset) throws FileSystemException, URISyntaxException { + final StringBuilder sb = new StringBuilder(BUFFER_SIZE); + appendRootUri(sb, true); + sb.append(getPathQueryEncoded(charset)); + return sb.toString(); + } +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileNameParser.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileNameParser.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileNameParser.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/GenericURLFileNameParser.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider; + +import org.apache.commons.vfs2.FileName; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileType; + +/** + * Generic implementation for any url based filesystem, without depending a specific library. + * <p> + * Parses the url into user/password/host/port/path/queryString. + */ +public class GenericURLFileNameParser extends HostFileNameParser { + + public GenericURLFileNameParser(final int defaultPort) { + super(defaultPort); + } + + @Override + public boolean encodeCharacter(final char ch) { + return super.encodeCharacter(ch) || ch == '?'; + } + + @Override + public FileName parseUri(final VfsComponentContext context, final FileName base, final String filename) + throws FileSystemException { + // FTP URI are generic URI (as per RFC 2396) + final StringBuilder name = new StringBuilder(); + + // Extract the scheme and authority parts + final Authority auth = extractToPath(filename, name); + + // Extract the queryString + final String queryString = UriParser.extractQueryString(name); + + // Decode and normalise the file name + UriParser.canonicalizePath(name, 0, name.length(), this); + UriParser.fixSeparators(name); + final FileType fileType = UriParser.normalisePath(name); + final String path = name.toString(); + + return new GenericURLFileName(auth.getScheme(), auth.getHostName(), auth.getPort(), getDefaultPort(), + auth.getUserName(), auth.getPassword(), path, fileType, queryString); + } +} Modified: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileName.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileName.java?rev=1845527&r1=1845526&r2=1845527&view=diff ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileName.java (original) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileName.java Fri Nov 2 01:53:29 2018 @@ -24,7 +24,9 @@ import org.apache.commons.vfs2.FileType; /** * A file name that represents URL. + * @deprecated Use {@link GenericURLFileName} as it doesn't depend on Http Client v3 API directly. */ +@Deprecated public class URLFileName extends GenericFileName { private static final int BUFFER_SIZE = 250; Modified: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileNameParser.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileNameParser.java?rev=1845527&r1=1845526&r2=1845527&view=diff ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileNameParser.java (original) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/URLFileNameParser.java Fri Nov 2 01:53:29 2018 @@ -24,7 +24,9 @@ import org.apache.commons.vfs2.FileType; * Implementation for any url based filesystem. * <p> * Parses the url into user/password/host/port/path/queryString. + * @deprecated Use {@link GenericURLFileNameParser} as it doesn't depend on Http Client v3 API directly. */ +@Deprecated public class URLFileNameParser extends HostFileNameParser { public URLFileNameParser(final int defaultPort) { super(defaultPort); Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileContentInfoFactory.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileContentInfoFactory.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileContentInfoFactory.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileContentInfoFactory.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider.http4; + +import java.io.IOException; + +import org.apache.commons.vfs2.FileContent; +import org.apache.commons.vfs2.FileContentInfo; +import org.apache.commons.vfs2.FileContentInfoFactory; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.impl.DefaultFileContentInfo; +import org.apache.commons.vfs2.util.FileObjectUtils; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.entity.ContentType; +import org.apache.http.protocol.HTTP; + +/** + * Creates <code>FileContentInfoFactory</code> instances for http4 provider. + */ +public class Http4FileContentInfoFactory implements FileContentInfoFactory { + + @SuppressWarnings("unchecked") + @Override + public FileContentInfo create(final FileContent fileContent) throws FileSystemException { + String contentMimeType = null; + String contentCharset = null; + + try (final Http4FileObject<Http4FileSystem> http4File = (Http4FileObject<Http4FileSystem>) FileObjectUtils + .getAbstractFileObject(fileContent.getFile())) { + final HttpResponse lastHeadResponse = http4File.getLastHeadResponse(); + + final Header header = lastHeadResponse.getFirstHeader(HTTP.CONTENT_TYPE); + + if (header != null) { + final ContentType contentType = ContentType.parse(header.getValue()); + contentMimeType = contentType.getMimeType(); + + if (contentType.getCharset() != null) { + contentCharset = contentType.getCharset().name(); + } + } + + return new DefaultFileContentInfo(contentMimeType, contentCharset); + } catch (final IOException e) { + throw new FileSystemException(e); + } + } +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileNameParser.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileNameParser.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileNameParser.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileNameParser.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider.http4; + +import org.apache.commons.vfs2.provider.FileNameParser; +import org.apache.commons.vfs2.provider.GenericURLFileNameParser; + +/** + * <code>FileNameParser</code> implementation for http4 provider, setting default port to 80. + */ +public class Http4FileNameParser extends GenericURLFileNameParser { + + private static final int DEFAULT_PORT = 80; + + private static final Http4FileNameParser INSTANCE = new Http4FileNameParser(); + + public Http4FileNameParser() { + super(DEFAULT_PORT); + } + + public static FileNameParser getInstance() { + return INSTANCE; + } +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider.http4; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.commons.vfs2.FileContentInfoFactory; +import org.apache.commons.vfs2.FileNotFoundException; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileSystemOptions; +import org.apache.commons.vfs2.FileType; +import org.apache.commons.vfs2.RandomAccessContent; +import org.apache.commons.vfs2.provider.AbstractFileName; +import org.apache.commons.vfs2.provider.AbstractFileObject; +import org.apache.commons.vfs2.provider.GenericURLFileName; +import org.apache.commons.vfs2.util.RandomAccessMode; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.DateUtils; +import org.apache.http.client.utils.URIUtils; +import org.apache.http.protocol.HTTP; + +/** + * A file object backed by Apache HttpComponents HttpClient. + * + * @param <FS> An {@link Http4FileSystem} subclass + */ +public class Http4FileObject<FS extends Http4FileSystem> extends AbstractFileObject<FS> { + + /** + * URL charset string. + */ + private final String urlCharset; + + /** + * Internal URI mapped to this <code>FileObject</code>. + * For example, the internal URI of <code>http4://example.com/a.txt</code> is <code>http://example.com/a.txt</code>. + */ + private final URI internalURI; + + /** + * The last executed HEAD <code>HttpResponse</code> object. + */ + private HttpResponse lastHeadResponse; + + /** + * Construct <code>Http4FileObject</code>. + * @param name file name + * @param fileSystem file system + * @throws FileSystemException if any error occurs + * @throws URISyntaxException if given file name cannot be converted to a URI due to URI syntax error + */ + protected Http4FileObject(final AbstractFileName name, final FS fileSystem) + throws FileSystemException, URISyntaxException { + this(name, fileSystem, Http4FileSystemConfigBuilder.getInstance()); + } + + /** + * Construct <code>Http4FileObject</code>. + * @param name file name + * @param fileSystem file system + * @param builder <code>Http4FileSystemConfigBuilder</code> object + * @throws FileSystemException if any error occurs + * @throws URISyntaxException if given file name cannot be converted to a URI due to URI syntax error + */ + protected Http4FileObject(final AbstractFileName name, final FS fileSystem, + final Http4FileSystemConfigBuilder builder) throws FileSystemException, URISyntaxException { + super(name, fileSystem); + final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions(); + urlCharset = builder.getUrlCharset(fileSystemOptions); + final String pathEncoded = ((GenericURLFileName) name).getPathQueryEncoded(getUrlCharset()); + internalURI = URIUtils.resolve(fileSystem.getInternalBaseURI(), pathEncoded); + } + + @Override + protected FileType doGetType() throws Exception { + lastHeadResponse = executeHttpUriRequest(new HttpHead(getInternalURI())); + final int status = lastHeadResponse.getStatusLine().getStatusCode(); + + if (status == HttpStatus.SC_OK + || status == HttpStatus.SC_METHOD_NOT_ALLOWED /* method is not allowed, but resource exist */) { + return FileType.FILE; + } else if (status == HttpStatus.SC_NOT_FOUND || status == HttpStatus.SC_GONE) { + return FileType.IMAGINARY; + } else { + throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status)); + } + } + + @Override + protected long doGetContentSize() throws Exception { + if (lastHeadResponse == null) { + return 0L; + } + + final Header header = lastHeadResponse.getFirstHeader(HTTP.CONTENT_LEN); + + if (header == null) { + // Assume 0 content-length + return 0; + } + + return Long.parseLong(header.getValue()); + } + + @Override + protected long doGetLastModifiedTime() throws Exception { + if (lastHeadResponse == null) { + throw new FileSystemException("vfs.provider.http/last-modified.error", getName()); + } + + final Header header = lastHeadResponse.getFirstHeader("Last-Modified"); + + if (header == null) { + throw new FileSystemException("vfs.provider.http/last-modified.error", getName()); + } + + return DateUtils.parseDate(header.getValue()).getTime(); + } + + + @Override + protected InputStream doGetInputStream() throws Exception { + final HttpGet getRequest = new HttpGet(getInternalURI()); + final HttpResponse httpResponse = executeHttpUriRequest(getRequest); + final int status = httpResponse.getStatusLine().getStatusCode(); + + if (status == HttpStatus.SC_NOT_FOUND) { + throw new FileNotFoundException(getName()); + } + + if (status != HttpStatus.SC_OK) { + throw new FileSystemException("vfs.provider.http/get.error", getName(), Integer.valueOf(status)); + } + + return new MonitoredHttpResponseContentInputStream(httpResponse); + } + + @Override + protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception { + return new Http4RandomAccessContent<>(this, mode); + } + + @Override + protected String[] doListChildren() throws Exception { + throw new UnsupportedOperationException("Not implemented."); + } + + @Override + protected boolean doIsWriteable() throws Exception { + return false; + } + + @Override + protected FileContentInfoFactory getFileContentInfoFactory() { + return new Http4FileContentInfoFactory(); + } + + @Override + protected void doDetach() throws Exception { + lastHeadResponse = null; + } + + /** + * Return URL charset string. + * @return URL charset string + */ + protected String getUrlCharset() { + return urlCharset; + } + + /** + * Return the internal <code>URI</code> object mapped to this file object. + * @return the internal <code>URI</code> object mapped to this file object + * @throws FileSystemException if any error occurs + */ + protected URI getInternalURI() throws FileSystemException { + return internalURI; + } + + /** + * Return the last executed HEAD <code>HttpResponse</code> object. + * @return the last executed HEAD <code>HttpResponse</code> object + * @throws IOException if IO error occurs + */ + HttpResponse getLastHeadResponse() throws IOException { + if (lastHeadResponse != null) { + return lastHeadResponse; + } + + return executeHttpUriRequest(new HttpHead(getInternalURI())); + } + + /** + * Execute the request using the given {@code httpRequest} and return a <code>HttpResponse</code> from the execution. + * @param httpRequest <code>HttpUriRequest</code> object + * @return <code>HttpResponse</code> from the execution + * @throws IOException if IO error occurs + */ + HttpResponse executeHttpUriRequest(final HttpUriRequest httpRequest) throws IOException { + final HttpClient httpClient = getAbstractFileSystem().getHttpClient(); + final HttpClientContext httpClientContext = getAbstractFileSystem().getHttpClientContext(); + return httpClient.execute(httpRequest, httpClientContext); + } + +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileProvider.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileProvider.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileProvider.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileProvider.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,363 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider.http4; + +import java.io.File; +import java.io.IOException; +import java.net.ProxySelector; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; + +import org.apache.commons.vfs2.Capability; +import org.apache.commons.vfs2.FileName; +import org.apache.commons.vfs2.FileSystem; +import org.apache.commons.vfs2.FileSystemConfigBuilder; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileSystemOptions; +import org.apache.commons.vfs2.UserAuthenticationData; +import org.apache.commons.vfs2.UserAuthenticator; +import org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider; +import org.apache.commons.vfs2.provider.GenericFileName; +import org.apache.commons.vfs2.util.UserAuthenticatorUtils; +import org.apache.http.ConnectionReuseStrategy; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.AuthCache; +import org.apache.http.client.CookieStore; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.config.SocketConfig; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.routing.HttpRoutePlanner; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.TrustAllStrategy; +import org.apache.http.cookie.Cookie; +import org.apache.http.impl.DefaultConnectionReuseStrategy; +import org.apache.http.impl.NoConnectionReuseStrategy; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.client.BasicAuthCache; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.impl.conn.SystemDefaultRoutePlanner; +import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.HTTP; +import org.apache.http.ssl.SSLContextBuilder; + +/** + * <code>FileProvider</code> implementation using HttpComponents HttpClient library. + */ +public class Http4FileProvider extends AbstractOriginatingFileProvider { + + /** Authenticator information. */ + static final UserAuthenticationData.Type[] AUTHENTICATOR_TYPES = + new UserAuthenticationData.Type[] { + UserAuthenticationData.USERNAME, + UserAuthenticationData.PASSWORD + }; + + /** FileProvider capabilities */ + static final Collection<Capability> capabilities = + Collections.unmodifiableCollection( + Arrays.asList( + Capability.GET_TYPE, + Capability.READ_CONTENT, + Capability.URI, + Capability.GET_LAST_MODIFIED, + Capability.ATTRIBUTES, + Capability.RANDOM_ACCESS_READ, + Capability.DIRECTORY_READ_CONTENT + ) + ); + + /** + * Constructs a new provider. + */ + public Http4FileProvider() { + super(); + setFileNameParser(Http4FileNameParser.getInstance()); + } + + @Override + public FileSystemConfigBuilder getConfigBuilder() { + return Http4FileSystemConfigBuilder.getInstance(); + } + + @Override + public Collection<Capability> getCapabilities() { + return capabilities; + } + + @Override + protected FileSystem doCreateFileSystem(FileName name, FileSystemOptions fileSystemOptions) + throws FileSystemException { + final GenericFileName rootName = (GenericFileName) name; + + UserAuthenticationData authData = null; + HttpClient httpClient = null; + HttpClientContext httpClientContext = null; + + try { + final Http4FileSystemConfigBuilder builder = Http4FileSystemConfigBuilder.getInstance(); + authData = UserAuthenticatorUtils.authenticate(fileSystemOptions, AUTHENTICATOR_TYPES); + httpClientContext = createHttpClientContext(builder, rootName, fileSystemOptions, authData); + httpClient = createHttpClient(builder, rootName, fileSystemOptions); + } finally { + UserAuthenticatorUtils.cleanup(authData); + } + + return new Http4FileSystem(rootName, fileSystemOptions, httpClient, httpClientContext); + } + + /** + * Create an {@link HttpClient} object for an http4 file system. + * @param builder Configuration options builder for http4 provider + * @param rootName The root path + * @param fileSystemOptions The file system options + * @return an {@link HttpClient} object + * @throws FileSystemException if an error occurs. + */ + protected HttpClient createHttpClient(final Http4FileSystemConfigBuilder builder, final GenericFileName rootName, + final FileSystemOptions fileSystemOptions) throws FileSystemException { + return createHttpClientBuilder(builder, rootName, fileSystemOptions).build(); + } + + /** + * Create an {@link HttpClientBuilder} object. Invoked by {@link #createHttpClient(Http4FileSystemConfigBuilder, GenericFileName, FileSystemOptions)}. + * @param builder Configuration options builder for HTTP4 provider + * @param rootName The root path + * @param fileSystemOptions The FileSystem options + * @return an {@link HttpClientBuilder} object + * @throws FileSystemException if an error occurs + */ + protected HttpClientBuilder createHttpClientBuilder(final Http4FileSystemConfigBuilder builder, final GenericFileName rootName, + final FileSystemOptions fileSystemOptions) throws FileSystemException { + final List<Header> defaultHeaders = new ArrayList<>(); + defaultHeaders.add(new BasicHeader(HTTP.USER_AGENT, builder.getUserAgent(fileSystemOptions))); + + final ConnectionReuseStrategy connectionReuseStrategy = builder.isKeepAlive(fileSystemOptions) + ? DefaultConnectionReuseStrategy.INSTANCE + : NoConnectionReuseStrategy.INSTANCE; + + final HttpClientBuilder httpClientBuilder = + HttpClients.custom() + .setRoutePlanner(createHttpRoutePlanner(builder, fileSystemOptions)) + .setConnectionManager(createConnectionManager(builder, fileSystemOptions)) + .setSSLContext(createSSLContext(builder, fileSystemOptions)) + .setSSLHostnameVerifier(createHostnameVerifier(builder, fileSystemOptions)) + .setConnectionReuseStrategy(connectionReuseStrategy) + .setDefaultRequestConfig(createDefaultRequestConfig(builder, fileSystemOptions)) + .setDefaultHeaders(defaultHeaders) + .setDefaultCookieStore(createDefaultCookieStore(builder, fileSystemOptions)); + + if (!builder.getFollowRedirect(fileSystemOptions)) { + httpClientBuilder.disableRedirectHandling(); + } + + return httpClientBuilder; + } + + /** + * Create {@link SSLContext} for HttpClient. Invoked by {@link #createHttpClientBuilder(Http4FileSystemConfigBuilder, GenericFileName, FileSystemOptions)}. + * @param builder Configuration options builder for HTTP4 provider + * @param fileSystemOptions The FileSystem options + * @return a {@link SSLContext} for HttpClient + * @throws FileSystemException if an error occurs + */ + protected SSLContext createSSLContext(final Http4FileSystemConfigBuilder builder, + final FileSystemOptions fileSystemOptions) throws FileSystemException { + try { + final SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); + + File keystoreFileObject = null; + final String keystoreFile = builder.getKeyStoreFile(fileSystemOptions); + + if (keystoreFile != null && !keystoreFile.isEmpty()) { + keystoreFileObject = new File(keystoreFile); + } + + if (keystoreFileObject != null && keystoreFileObject.exists()) { + final String keystorePass = builder.getKeyStorePass(fileSystemOptions); + final char[] keystorePassChars = (keystorePass != null) ? keystorePass.toCharArray() : null; + sslContextBuilder.loadTrustMaterial(keystoreFileObject, keystorePassChars, TrustAllStrategy.INSTANCE); + } else { + sslContextBuilder.loadTrustMaterial(TrustAllStrategy.INSTANCE); + } + + return sslContextBuilder.build(); + } catch (KeyStoreException e) { + throw new FileSystemException("Keystore error. " + e.getMessage(), e); + } catch (KeyManagementException e) { + throw new FileSystemException("Cannot retrieve keys. " + e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new FileSystemException("Algorithm error. " + e.getMessage(), e); + } catch (CertificateException e) { + throw new FileSystemException("Certificate error. " + e.getMessage(), e); + } catch (IOException e) { + throw new FileSystemException("Cannot open key file. " + e.getMessage(), e); + } + } + + /** + * Create an {@link HttpClientContext} object for an http4 file system. + * @param builder Configuration options builder for http4 provider + * @param rootName The root path + * @param fileSystemOptions The FileSystem options + * @param authData The <code>UserAuthentiationData</code> object + * @return an {@link HttpClientContext} object + * @throws FileSystemException if an error occurs + */ + protected HttpClientContext createHttpClientContext(final Http4FileSystemConfigBuilder builder, + final GenericFileName rootName, final FileSystemOptions fileSystemOptions, + final UserAuthenticationData authData) throws FileSystemException { + + final HttpClientContext clientContext = HttpClientContext.create(); + final CredentialsProvider credsProvider = new BasicCredentialsProvider(); + clientContext.setCredentialsProvider(credsProvider); + + final String username = UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData, + UserAuthenticationData.USERNAME, UserAuthenticatorUtils.toChar(rootName.getUserName()))); + final String password = UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData, + UserAuthenticationData.PASSWORD, UserAuthenticatorUtils.toChar(rootName.getPassword()))); + + if (username != null && !username.isEmpty()) { + credsProvider.setCredentials(new AuthScope(rootName.getHostName(), AuthScope.ANY_PORT), + new UsernamePasswordCredentials(username, password)); + } + + final HttpHost proxyHost = getProxyHttpHost(builder, fileSystemOptions); + + if (proxyHost != null) { + final UserAuthenticator proxyAuth = builder.getProxyAuthenticator(fileSystemOptions); + + if (proxyAuth != null) { + final UserAuthenticationData proxyAuthData = UserAuthenticatorUtils.authenticate(proxyAuth, + new UserAuthenticationData.Type[] { UserAuthenticationData.USERNAME, + UserAuthenticationData.PASSWORD }); + + if (proxyAuthData != null) { + final UsernamePasswordCredentials proxyCreds = new UsernamePasswordCredentials( + UserAuthenticatorUtils.toString( + UserAuthenticatorUtils.getData(authData, UserAuthenticationData.USERNAME, null)), + UserAuthenticatorUtils.toString( + UserAuthenticatorUtils.getData(authData, UserAuthenticationData.PASSWORD, null))); + + credsProvider.setCredentials(new AuthScope(proxyHost.getHostName(), AuthScope.ANY_PORT), + proxyCreds); + } + + if (builder.isPreemptiveAuth(fileSystemOptions)) { + final AuthCache authCache = new BasicAuthCache(); + final BasicScheme basicAuth = new BasicScheme(); + authCache.put(proxyHost, basicAuth); + clientContext.setAuthCache(authCache); + } + } + } + + return clientContext; + } + + private HttpClientConnectionManager createConnectionManager(final Http4FileSystemConfigBuilder builder, + final FileSystemOptions fileSystemOptions) throws FileSystemException { + final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); + connManager.setMaxTotal(builder.getMaxTotalConnections(fileSystemOptions)); + connManager.setDefaultMaxPerRoute(builder.getMaxConnectionsPerHost(fileSystemOptions)); + + final SocketConfig socketConfig = + SocketConfig + .custom() + .setSoTimeout(builder.getSoTimeout(fileSystemOptions)) + .build(); + + connManager.setDefaultSocketConfig(socketConfig); + + return connManager; + } + + private RequestConfig createDefaultRequestConfig(final Http4FileSystemConfigBuilder builder, + final FileSystemOptions fileSystemOptions) { + return RequestConfig.custom() + .setConnectTimeout(builder.getConnectionTimeout(fileSystemOptions)) + .build(); + } + + private HttpRoutePlanner createHttpRoutePlanner(final Http4FileSystemConfigBuilder builder, + final FileSystemOptions fileSystemOptions) { + final HttpHost proxyHost = getProxyHttpHost(builder, fileSystemOptions); + + if (proxyHost != null) { + return new DefaultProxyRoutePlanner(proxyHost); + } + + return new SystemDefaultRoutePlanner(ProxySelector.getDefault()); + } + + private HttpHost getProxyHttpHost(final Http4FileSystemConfigBuilder builder, + final FileSystemOptions fileSystemOptions) { + final String proxyHost = builder.getProxyHost(fileSystemOptions); + final int proxyPort = builder.getProxyPort(fileSystemOptions); + + if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) { + return new HttpHost(proxyHost, proxyPort); + } + + return null; + } + + private CookieStore createDefaultCookieStore(final Http4FileSystemConfigBuilder builder, + final FileSystemOptions fileSystemOptions) { + final CookieStore cookieStore = new BasicCookieStore(); + final Cookie[] cookies = builder.getCookies(fileSystemOptions); + + if (cookies != null) { + for (Cookie cookie : cookies) { + cookieStore.addCookie(cookie); + } + } + + return cookieStore; + } + + private HostnameVerifier createHostnameVerifier(final Http4FileSystemConfigBuilder builder, + final FileSystemOptions fileSystemOptions) throws FileSystemException { + if (!builder.isHostnameVerificationEnabled(fileSystemOptions)) { + return NoopHostnameVerifier.INSTANCE; + } + + return new DefaultHostnameVerifier(); + } + +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystem.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystem.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystem.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystem.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider.http4; + +import java.io.IOException; +import java.net.URI; +import java.util.Collection; + +import org.apache.commons.vfs2.Capability; +import org.apache.commons.vfs2.FileName; +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSystemOptions; +import org.apache.commons.vfs2.provider.AbstractFileName; +import org.apache.commons.vfs2.provider.AbstractFileSystem; +import org.apache.http.client.HttpClient; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.CloseableHttpClient; + +/** + * http4 file system. + */ +public class Http4FileSystem extends AbstractFileSystem { + + /** + * Internal base URI of this file system. + */ + private final URI internalBaseURI; + + /** + * Internal <code>HttpClient</code> instance of this file system. + */ + private final HttpClient httpClient; + + /** + * Internal <code>HttpClientContext</code> instance of this file system. + */ + private final HttpClientContext httpClientContext; + + /** + * Construct <code>Http4FileSystem</code>. + * @param rootName root base name + * @param fileSystemOptions file system options + * @param httpClient {@link HttpClient} instance + * @param httpClientContext {@link HttpClientContext} instance + */ + protected Http4FileSystem(FileName rootName, FileSystemOptions fileSystemOptions, HttpClient httpClient, + HttpClientContext httpClientContext) { + super(rootName, null, fileSystemOptions); + + final String rootURI = getRootURI(); + final int offset = rootURI.indexOf(':'); + final char lastCharOfScheme = (offset > 0) ? rootURI.charAt(offset - 1) : 0; + + // if scheme is 'http*s' or 'HTTP*S', then the internal base URI should be 'https'. 'http' otherwise. + if (lastCharOfScheme == 's' || lastCharOfScheme == 'S') { + this.internalBaseURI = URI.create("https" + rootURI.substring(offset)); + } else { + this.internalBaseURI = URI.create("http" + rootURI.substring(offset)); + } + + this.httpClient = httpClient; + this.httpClientContext = httpClientContext; + } + + @Override + protected FileObject createFile(AbstractFileName name) throws Exception { + return new Http4FileObject<>(name, this); + } + + @Override + protected void addCapabilities(Collection<Capability> caps) { + caps.addAll(Http4FileProvider.capabilities); + } + + @Override + protected void doCloseCommunicationLink() { + if (httpClient instanceof CloseableHttpClient) { + try { + ((CloseableHttpClient) httpClient).close(); + } catch (IOException e) { + throw new RuntimeException("Error closing HttpClient", e); + } + } + } + + /** + * Return the internal {@link HttpClient} instance. + * @return the internal {@link HttpClient} instance + */ + protected HttpClient getHttpClient() { + return httpClient; + } + + /** + * Return the internal {@link HttpClientContext} instance. + * @return the internal {@link HttpClientContext} instance + */ + protected HttpClientContext getHttpClientContext() { + return httpClientContext; + } + + /** + * Return the internal base <code>URI</code> instance. + * @return the internal base <code>URI</code> instance + */ + protected URI getInternalBaseURI() { + return internalBaseURI; + } +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystemConfigBuilder.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystemConfigBuilder.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystemConfigBuilder.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileSystemConfigBuilder.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,515 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider.http4; + +import org.apache.commons.vfs2.FileSystem; +import org.apache.commons.vfs2.FileSystemConfigBuilder; +import org.apache.commons.vfs2.FileSystemOptions; +import org.apache.commons.vfs2.UserAuthenticator; +import org.apache.http.cookie.Cookie; + +/** + * Configuration options builder utility for http4 provider. + */ +public class Http4FileSystemConfigBuilder extends FileSystemConfigBuilder { + + private static final Http4FileSystemConfigBuilder BUILDER = new Http4FileSystemConfigBuilder(); + + /** + * Defines the maximum number of connections allowed overall. This value only applies + * to the number of connections from a particular instance of HTTP connection manager. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + */ + private static final String MAX_TOTAL_CONNECTIONS = "http.connection-manager.max-total"; + + /** + * Defines the maximum number of connections allowed per host configuration. + * These values only apply to the number of connections from a particular instance + * of HTTP connection manager. + */ + private static final String MAX_HOST_CONNECTIONS = "http.connection-manager.max-per-host"; + + /** + * Defines the connection timeout of an HTTP request. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + */ + private static final String CONNECTION_TIMEOUT = "http.connection.timeout"; + + /** + * Defines the socket timeout of an HTTP request. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + */ + private static final String SO_TIMEOUT = "http.socket.timeout"; + + /** + * Defines whether Keep-Alive option is used or not. + * <p> + * This parameter expects a value of type {@link Boolean}. + * </p> + */ + private static final String KEEP_ALIVE = "http.keepAlive"; + + /** + * Defines the keystore file path for SSL connections. + * <p> + * This parameter expects a value of type {@link String}. + * </p> + */ + private static final String KEYSTORE_FILE = "http.keystoreFile"; + + /** + * Defines the keystore pass phrase for SSL connections. + * <p> + * This parameter expects a value of type {@link String}. + * </p> + */ + private static final String KEYSTORE_PASS = "http.keystorePass"; + + /** + * Defines whether the host name should be verified or not in SSL connections. + * <p> + * This parameter expects a value of type {@link Boolean}. + * </p> + */ + private static final String HOSTNAME_VERIFICATION_ENABLED = "http.hostname-verification.enabled"; + + /** + * Defines whether the HttpClient should follow redirections from the responses. + * <p> + * This parameter expects a value of type {@link Boolean}. + * </p> + */ + private static final String KEY_FOLLOW_REDIRECT = "followRedirect"; + + /** + * Defines the User-Agent request header string of the underlying HttpClient. + * <p> + * This parameter expects a value of type {@link String}. + * </p> + */ + private static final String KEY_USER_AGENT = "userAgent"; + + /** + * Defines whether the preemptive authentication should be enabled or not. + * <p> + * This parameter expects a value of type {@link Boolean}. + * </p> + */ + private static final String KEY_PREEMPTIVE_AUTHENTICATION = "preemptiveAuth"; + + /** + * The default value for {@link #MAX_TOTAL_CONNECTIONS} configuration. + */ + private static final int DEFAULT_MAX_CONNECTIONS = 50; + + /** + * The default value for {@link #MAX_HOST_CONNECTIONS} configuration. + */ + private static final int DEFAULT_MAX_HOST_CONNECTIONS = 5; + + /** + * The default value for {@link #CONNECTION_TIMEOUT} configuration. + */ + private static final int DEFAULT_CONNECTION_TIMEOUT = 0; + + /** + * The default value for {@link #SO_TIMEOUT} configuration. + */ + private static final int DEFAULT_SO_TIMEOUT = 0; + + /** + * The default value for {@link #KEEP_ALIVE} configuration. + */ + private static final boolean DEFAULT_KEEP_ALIVE = true; + + /** + * The default value for {@link #KEY_FOLLOW_REDIRECT} configuration. + */ + private static final boolean DEFAULT_FOLLOW_REDIRECT = true; + + /** + * The default value for {@link #KEY_USER_AGENT} configuration. + */ + private static final String DEFAULT_USER_AGENT = "Jakarta-Commons-VFS"; + + /** + * The default value for {@link #HOSTNAME_VERIFICATION_ENABLED} configuration. + */ + private static final boolean DEFAULT_HOSTNAME_VERIFICATION_ENABLED = true; + + /** + * Construct an <code>Http4FileSystemConfigBuilder</code>. + * + * @param prefix String for properties of this file system. + */ + protected Http4FileSystemConfigBuilder(final String prefix) { + super(prefix); + } + + private Http4FileSystemConfigBuilder() { + super("http."); + } + + /** + * Gets the singleton builder. + * + * @return the singleton builder. + */ + public static Http4FileSystemConfigBuilder getInstance() { + return BUILDER; + } + + /** + * Sets the charset used for url encoding.<br> + * + * @param opts The FileSystem options. + * @param chaset the chaset + */ + public void setUrlCharset(final FileSystemOptions opts, final String chaset) { + setParam(opts, "urlCharset", chaset); + } + + /** + * Sets the charset used for url encoding.<br> + * + * @param opts The FileSystem options. + * @return the chaset + */ + public String getUrlCharset(final FileSystemOptions opts) { + return getString(opts, "urlCharset"); + } + + /** + * Sets the proxy to use for http connection.<br> + * You have to set the ProxyPort too if you would like to have the proxy really used. + * + * @param opts The FileSystem options. + * @param proxyHost the host + * @see #setProxyPort + */ + public void setProxyHost(final FileSystemOptions opts, final String proxyHost) { + setParam(opts, "proxyHost", proxyHost); + } + + /** + * Sets the proxy-port to use for http connection. You have to set the ProxyHost too if you would like to have the + * proxy really used. + * + * @param opts The FileSystem options. + * @param proxyPort the port + * @see #setProxyHost + */ + public void setProxyPort(final FileSystemOptions opts, final int proxyPort) { + setParam(opts, "proxyPort", Integer.valueOf(proxyPort)); + } + + /** + * Gets the proxy to use for http connection. You have to set the ProxyPort too if you would like to have the proxy + * really used. + * + * @param opts The FileSystem options. + * @return proxyHost + * @see #setProxyPort + */ + public String getProxyHost(final FileSystemOptions opts) { + return getString(opts, "proxyHost"); + } + + /** + * Gets the proxy-port to use for http the connection. You have to set the ProxyHost too if you would like to have + * the proxy really used. + * + * @param opts The FileSystem options. + * @return proxyPort: the port number or 0 if it is not set + * @see #setProxyHost + */ + public int getProxyPort(final FileSystemOptions opts) { + return getInteger(opts, "proxyPort", 0); + } + + /** + * Sets the proxy authenticator where the system should get the credentials from. + * + * @param opts The FileSystem options. + * @param authenticator The UserAuthenticator. + */ + public void setProxyAuthenticator(final FileSystemOptions opts, final UserAuthenticator authenticator) { + setParam(opts, "proxyAuthenticator", authenticator); + } + + /** + * Gets the proxy authenticator where the system should get the credentials from. + * + * @param opts The FileSystem options. + * @return The UserAuthenticator. + */ + public UserAuthenticator getProxyAuthenticator(final FileSystemOptions opts) { + return (UserAuthenticator) getParam(opts, "proxyAuthenticator"); + } + + /** + * The cookies to add to the request. + * + * @param opts The FileSystem options. + * @param cookies An array of Cookies. + */ + public void setCookies(final FileSystemOptions opts, final Cookie[] cookies) { + setParam(opts, "cookies", cookies); + } + + /** + * Sets whether to follow redirects for the connection. + * + * @param opts The FileSystem options. + * @param redirect {@code true} to follow redirects, {@code false} not to. + * @see #setFollowRedirect + */ + public void setFollowRedirect(final FileSystemOptions opts, final boolean redirect) { + setParam(opts, KEY_FOLLOW_REDIRECT, redirect); + } + + /** + * Gets the cookies to add to the request. + * + * @param opts The FileSystem options. + * @return the Cookie array. + */ + public Cookie[] getCookies(final FileSystemOptions opts) { + return (Cookie[]) getParam(opts, "cookies"); + } + + /** + * Gets whether to follow redirects for the connection. + * + * @param opts The FileSystem options. + * @return {@code true} to follow redirects, {@code false} not to. + * @see #setFollowRedirect + */ + public boolean getFollowRedirect(final FileSystemOptions opts) { + return getBoolean(opts, KEY_FOLLOW_REDIRECT, DEFAULT_FOLLOW_REDIRECT); + } + + /** + * Sets the maximum number of connections allowed. + * + * @param opts The FileSystem options. + * @param maxTotalConnections The maximum number of connections. + */ + public void setMaxTotalConnections(final FileSystemOptions opts, final int maxTotalConnections) { + setParam(opts, MAX_TOTAL_CONNECTIONS, Integer.valueOf(maxTotalConnections)); + } + + /** + * Gets the maximum number of connections allowed. + * + * @param opts The FileSystemOptions. + * @return The maximum number of connections allowed. + */ + public int getMaxTotalConnections(final FileSystemOptions opts) { + return getInteger(opts, MAX_TOTAL_CONNECTIONS, DEFAULT_MAX_CONNECTIONS); + } + + /** + * Sets the maximum number of connections allowed to any host. + * + * @param opts The FileSystem options. + * @param maxHostConnections The maximum number of connections to a host. + */ + public void setMaxConnectionsPerHost(final FileSystemOptions opts, final int maxHostConnections) { + setParam(opts, MAX_HOST_CONNECTIONS, Integer.valueOf(maxHostConnections)); + } + + /** + * Gets the maximum number of connections allowed per host. + * + * @param opts The FileSystemOptions. + * @return The maximum number of connections allowed per host. + */ + public int getMaxConnectionsPerHost(final FileSystemOptions opts) { + return getInteger(opts, MAX_HOST_CONNECTIONS, DEFAULT_MAX_HOST_CONNECTIONS); + } + + /** + * Determines if the FileSystemOptions indicate that preemptive authentication is requested. + * + * @param opts The FileSystemOptions. + * @return true if preemptiveAuth is requested. + */ + public boolean isPreemptiveAuth(final FileSystemOptions opts) { + return getBoolean(opts, KEY_PREEMPTIVE_AUTHENTICATION, Boolean.FALSE).booleanValue(); + } + + /** + * Sets the given value for preemptive HTTP authentication (using BASIC) on the given FileSystemOptions object. + * Defaults to false if not set. It may be appropriate to set to true in cases when the resulting chattiness of the + * conversation outweighs any architectural desire to use a stronger authentication scheme than basic/preemptive. + * + * @param opts The FileSystemOptions. + * @param preemptiveAuth the desired setting; true=enabled and false=disabled. + */ + public void setPreemptiveAuth(final FileSystemOptions opts, final boolean preemptiveAuth) { + setParam(opts, KEY_PREEMPTIVE_AUTHENTICATION, Boolean.valueOf(preemptiveAuth)); + } + + /** + * The connection timeout. + * + * @param opts The FileSystem options. + * @param connectionTimeout The connection timeout. + */ + public void setConnectionTimeout(final FileSystemOptions opts, final int connectionTimeout) { + setParam(opts, CONNECTION_TIMEOUT, Integer.valueOf(connectionTimeout)); + } + + /** + * Gets the connection timeout. + * + * @param opts The FileSystem options. + * @return The connection timeout. + */ + public int getConnectionTimeout(final FileSystemOptions opts) { + return getInteger(opts, CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); + } + + /** + * The socket timeout. + * + * @param opts The FileSystem options. + * @param soTimeout socket timeout. + */ + public void setSoTimeout(final FileSystemOptions opts, final int soTimeout) { + setParam(opts, SO_TIMEOUT, Integer.valueOf(soTimeout)); + } + + /** + * Gets the socket timeout. + * + * @param opts The FileSystemOptions. + * @return The socket timeout. + */ + public int getSoTimeout(final FileSystemOptions opts) { + return getInteger(opts, SO_TIMEOUT, DEFAULT_SO_TIMEOUT); + } + + /** + * Sets if the FileSystemOptions indicate that HTTP Keep-Alive is respected. + * + * @param opts The FileSystemOptions. + * @param keepAlive whether the FileSystemOptions indicate that HTTP Keep-Alive is respected or not. + */ + public void setKeepAlive(final FileSystemOptions opts, boolean keepAlive) { + setParam(opts, KEEP_ALIVE, Boolean.valueOf(keepAlive)); + } + + /** + * Determines if the FileSystemOptions indicate that HTTP Keep-Alive is respected. + * + * @param opts The FileSystemOptions. + * @return true if if the FileSystemOptions indicate that HTTP Keep-Alive is respected. + */ + public boolean isKeepAlive(final FileSystemOptions opts) { + return getBoolean(opts, KEEP_ALIVE, DEFAULT_KEEP_ALIVE); + } + + /** + * Sets the user agent to attach to the outgoing http methods + * + * @param opts the file system options to modify + * @param userAgent User Agent String + */ + public void setUserAgent(final FileSystemOptions opts, final String userAgent) { + setParam(opts, "userAgent", userAgent); + } + + /** + * Gets the user agent string + * + * @param opts the file system options to modify + * @return User provided User-Agent string, otherwise default of: Commons-VFS + */ + public String getUserAgent(final FileSystemOptions opts) { + final String userAgent = (String) getParam(opts, KEY_USER_AGENT); + return userAgent != null ? userAgent : DEFAULT_USER_AGENT; + } + + /** + * Set keystore file path for SSL connections. + * @param opts the file system options to modify + * @param keyStoreFile keystore file path + */ + public void setKeyStoreFile(final FileSystemOptions opts, String keyStoreFile) { + setParam(opts, KEYSTORE_FILE, keyStoreFile); + } + + /** + * Return keystore file path to be used in SSL connections. + * @param opts the file system options to modify + * @return keystore file path to be used in SSL connections + */ + public String getKeyStoreFile(final FileSystemOptions opts) { + return (String) getParam(opts, KEYSTORE_FILE); + } + + /** + * Set keystore pass phrase for SSL connecdtions. + * @param opts the file system options to modify + * @param keyStorePass keystore pass phrase for SSL connecdtions + */ + public void setKeyStorePass(final FileSystemOptions opts, String keyStorePass) { + setParam(opts, KEYSTORE_PASS, keyStorePass); + } + + /** + * Return keystore pass phrase for SSL connections. + * @param opts the file system options to modify + * @return keystore pass phrase for SSL connections + */ + String getKeyStorePass(final FileSystemOptions opts) { + return (String) getParam(opts, KEYSTORE_PASS); + } + + /** + * Sets if the hostname should be verified in SSL context. + * + * @param opts The FileSystemOptions. + * @param hostnameVerificationEnabled whether hostname should be verified + */ + public void setHostnameVerificationEnabled(final FileSystemOptions opts, boolean hostnameVerificationEnabled) { + setParam(opts, HOSTNAME_VERIFICATION_ENABLED, Boolean.valueOf(hostnameVerificationEnabled)); + } + + /** + * Determines if the hostname should be verified in SSL context. + * + * @param opts The FileSystemOptions. + * @return true if if the FileSystemOptions indicate that HTTP Keep-Alive is respected. + */ + public boolean isHostnameVerificationEnabled(final FileSystemOptions opts) { + return getBoolean(opts, HOSTNAME_VERIFICATION_ENABLED, DEFAULT_HOSTNAME_VERIFICATION_ENABLED); + } + + @Override + protected Class<? extends FileSystem> getConfigClass() { + return Http4FileSystem.class; + } +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4RandomAccessContent.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4RandomAccessContent.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4RandomAccessContent.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4RandomAccessContent.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider.http4; + +import java.io.DataInputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.net.HttpURLConnection; + +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.provider.AbstractRandomAccessStreamContent; +import org.apache.commons.vfs2.util.MonitorInputStream; +import org.apache.commons.vfs2.util.RandomAccessMode; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; + +/** + * RandomAccess content using <code>Http4FileObject</code>. + */ +class Http4RandomAccessContent<FS extends Http4FileSystem> extends AbstractRandomAccessStreamContent { + + protected long filePointer = 0; + + private final Http4FileObject<FS> fileObject; + + private DataInputStream dis = null; + private MonitorInputStream mis = null; + + Http4RandomAccessContent(final Http4FileObject<FS> fileObject, final RandomAccessMode mode) { + super(mode); + this.fileObject = fileObject; + } + + @Override + public long getFilePointer() throws IOException { + return filePointer; + } + + @Override + public void seek(final long pos) throws IOException { + if (pos == filePointer) { + // no change + return; + } + + if (pos < 0) { + throw new FileSystemException("vfs.provider/random-access-invalid-position.error", Long.valueOf(pos)); + } + + if (dis != null) { + close(); + } + + filePointer = pos; + } + + @Override + protected DataInputStream getDataInputStream() throws IOException { + if (dis != null) { + return dis; + } + + final HttpGet httpGet = new HttpGet(fileObject.getInternalURI()); + httpGet.setHeader("Range", "bytes=" + filePointer + "-"); + final HttpResponse httpResponse = fileObject.executeHttpUriRequest(httpGet); + final int status = httpResponse.getStatusLine().getStatusCode(); + + if (status != HttpURLConnection.HTTP_PARTIAL && status != HttpURLConnection.HTTP_OK) { + throw new FileSystemException("vfs.provider.http/get-range.error", fileObject.getName(), + Long.valueOf(filePointer), Integer.valueOf(status)); + } + + mis = new MonitoredHttpResponseContentInputStream(httpResponse); + + // If the range request was ignored + if (status == HttpURLConnection.HTTP_OK) { + final long skipped = mis.skip(filePointer); + if (skipped != filePointer) { + throw new FileSystemException("vfs.provider.http/get-range.error", fileObject.getName(), + Long.valueOf(filePointer), Integer.valueOf(status)); + } + } + + dis = new DataInputStream(new FilterInputStream(mis) { + @Override + public int read() throws IOException { + final int ret = super.read(); + if (ret > -1) { + filePointer++; + } + return ret; + } + + @Override + public int read(final byte[] b) throws IOException { + final int ret = super.read(b); + if (ret > -1) { + filePointer += ret; + } + return ret; + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + final int ret = super.read(b, off, len); + if (ret > -1) { + filePointer += ret; + } + return ret; + } + }); + + return dis; + } + + @Override + public void close() throws IOException { + if (dis != null) { + dis.close(); + dis = null; + mis = null; + } + } + + @Override + public long length() throws IOException { + return fileObject.getContent().getSize(); + } +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/MonitoredHttpResponseContentInputStream.java URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/MonitoredHttpResponseContentInputStream.java?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/MonitoredHttpResponseContentInputStream.java (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/MonitoredHttpResponseContentInputStream.java Fri Nov 2 01:53:29 2018 @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.provider.http4; + +import java.io.IOException; + +import org.apache.commons.vfs2.util.MonitorInputStream; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; + +/** + * An InputStream that cleans up the <code>org.apache.http.client.methods.CloseableHttpResponse</code> on close. + */ +class MonitoredHttpResponseContentInputStream extends MonitorInputStream { + + private final HttpResponse httpResponse; + + public MonitoredHttpResponseContentInputStream(final HttpResponse httpResponse) throws IOException { + super(httpResponse.getEntity().getContent()); + this.httpResponse = httpResponse; + } + + @Override + protected void onClose() throws IOException { + if (httpResponse instanceof CloseableHttpResponse) { + ((CloseableHttpResponse) httpResponse).close(); + } + } + +} Added: commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/package.html URL: http://svn.apache.org/viewvc/commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/package.html?rev=1845527&view=auto ============================================================================== --- commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/package.html (added) +++ commons/proper/vfs/trunk/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/package.html Fri Nov 2 01:53:29 2018 @@ -0,0 +1,19 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<body> +<p>The HTTP4 File Provider</p> +</body>