001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.vfs2.provider.http; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.HttpURLConnection; 022 023import org.apache.commons.httpclient.Header; 024import org.apache.commons.httpclient.HttpClient; 025import org.apache.commons.httpclient.HttpMethod; 026import org.apache.commons.httpclient.URIException; 027import org.apache.commons.httpclient.methods.GetMethod; 028import org.apache.commons.httpclient.methods.HeadMethod; 029import org.apache.commons.httpclient.util.DateUtil; 030import org.apache.commons.httpclient.util.URIUtil; 031import org.apache.commons.vfs2.FileContentInfoFactory; 032import org.apache.commons.vfs2.FileNotFoundException; 033import org.apache.commons.vfs2.FileSystemException; 034import org.apache.commons.vfs2.FileSystemOptions; 035import org.apache.commons.vfs2.FileType; 036import org.apache.commons.vfs2.RandomAccessContent; 037import org.apache.commons.vfs2.provider.AbstractFileName; 038import org.apache.commons.vfs2.provider.AbstractFileObject; 039import org.apache.commons.vfs2.provider.URLFileName; 040import org.apache.commons.vfs2.util.MonitorInputStream; 041import org.apache.commons.vfs2.util.RandomAccessMode; 042 043/** 044 * A file object backed by Apache Commons HttpClient. 045 * <p> 046 * TODO - status codes. 047 * </p> 048 * 049 * @param <FS> An {@link HttpFileSystem} subclass 050 * @deprecated Use {@link org.apache.commons.vfs2.provider.http5}. 051 */ 052@Deprecated 053public class HttpFileObject<FS extends HttpFileSystem> extends AbstractFileObject<FS> { 054 055 /** 056 * An InputStream that cleans up the HTTP connection on close. 057 */ 058 static class HttpInputStream extends MonitorInputStream { 059 private final GetMethod method; 060 061 HttpInputStream(final GetMethod method) throws IOException { 062 super(method.getResponseBodyAsStream()); 063 this.method = method; 064 } 065 066 HttpInputStream(final GetMethod method, final int bufferSize) throws IOException { 067 super(method.getResponseBodyAsStream(), bufferSize); 068 this.method = method; 069 } 070 071 /** 072 * Called after the stream has been closed. 073 */ 074 @Override 075 protected void onClose() throws IOException { 076 method.releaseConnection(); 077 } 078 } 079 080 private final String urlCharset; 081 private final String userAgent; 082 private final boolean followRedirect; 083 private HeadMethod method; 084 085 /** 086 * Constructs a new instance. 087 * 088 * @param fileName the file name. 089 * @param fileSystem the file system. 090 */ 091 protected HttpFileObject(final AbstractFileName fileName, final FS fileSystem) { 092 this(fileName, fileSystem, HttpFileSystemConfigBuilder.getInstance()); 093 } 094 095 /** 096 * Constructs a new instance. 097 * 098 * @param fileName the file name. 099 * @param fileSystem the file system. 100 * @param builder Configuration options for HTTP. 101 */ 102 protected HttpFileObject(final AbstractFileName fileName, final FS fileSystem, 103 final HttpFileSystemConfigBuilder builder) { 104 super(fileName, fileSystem); 105 final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions(); 106 urlCharset = builder.getUrlCharset(fileSystemOptions); 107 userAgent = builder.getUserAgent(fileSystemOptions); 108 followRedirect = builder.getFollowRedirect(fileSystemOptions); 109 } 110 111 /** 112 * Detaches this file object from its file resource. 113 */ 114 @Override 115 protected void doDetach() throws Exception { 116 synchronized (getFileSystem()) { 117 method = null; 118 } 119 } 120 121 /** 122 * Returns the size of the file content (in bytes). 123 */ 124 @Override 125 protected long doGetContentSize() throws Exception { 126 final Header header = getHeadMethod().getResponseHeader("content-length"); 127 if (header == null) { 128 // Assume 0 content-length 129 return 0; 130 } 131 return Long.parseLong(header.getValue()); 132 } 133 134 /** 135 * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns 136 * {@link FileType#FILE}. 137 * <p> 138 * It is guaranteed that there are no open output streams for this file when this method is called. 139 * </p> 140 * <p> 141 * The returned stream does not have to be buffered. 142 * </p> 143 */ 144 @Override 145 protected InputStream doGetInputStream(final int bufferSize) throws Exception { 146 final GetMethod getMethod = new GetMethod(); 147 setupMethod(getMethod); 148 final int status = getAbstractFileSystem().getClient().executeMethod(getMethod); 149 if (status == HttpURLConnection.HTTP_NOT_FOUND) { 150 throw new FileNotFoundException(getName()); 151 } 152 if (status != HttpURLConnection.HTTP_OK) { 153 throw new FileSystemException("vfs.provider.http/get.error", getName(), Integer.valueOf(status)); 154 } 155 156 return new HttpInputStream(getMethod, bufferSize); 157 } 158 159 /** 160 * Returns the last modified time of this file. 161 * <p> 162 * This implementation throws an exception. 163 * </p> 164 */ 165 @Override 166 protected long doGetLastModifiedTime() throws Exception { 167 final Header header = getHeadMethod().getResponseHeader("last-modified"); 168 FileSystemException.requireNonNull(header, "vfs.provider.http/last-modified.error", getName()); 169 return DateUtil.parseDate(header.getValue()).getTime(); 170 } 171 172 @Override 173 protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception { 174 return new HttpRandomAccessContent<>(this, mode); 175 } 176 177 /** 178 * Determines the type of this file. Must not return null. The return value of this method is cached, so the 179 * implementation can be expensive. 180 */ 181 @Override 182 protected FileType doGetType() throws Exception { 183 // Use the HEAD method to probe the file. 184 final int status = this.getHeadMethod().getStatusCode(); 185 if (status == HttpURLConnection.HTTP_OK 186 || status == HttpURLConnection.HTTP_BAD_METHOD /* method is bad, but resource exist */) { 187 return FileType.FILE; 188 } 189 if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) { 190 return FileType.IMAGINARY; 191 } 192 throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status)); 193 } 194 195 @Override 196 protected boolean doIsWriteable() throws Exception { 197 return false; 198 } 199 200 /** 201 * Throws UnsupportedOperationException. 202 * 203 * @throws UnsupportedOperationException always thrown. 204 */ 205 @Override 206 protected String[] doListChildren() throws Exception { 207 throw new UnsupportedOperationException("Not implemented."); 208 } 209 210 /** 211 * Encodes the given path. 212 * 213 * @param unescaped An unescaped path. 214 * @return the encoded path. 215 * @throws URIException if the default protocol charset is not supported 216 */ 217 protected String encodePath(final String unescaped) throws URIException { 218 return URIUtil.encodePath(unescaped); 219 } 220 221 /** 222 * Gets a new FileContentInfoFactory. 223 * 224 * @return a new FileContentInfoFactory. 225 */ 226 @Override 227 protected FileContentInfoFactory getFileContentInfoFactory() { 228 return new HttpFileContentInfoFactory(); 229 } 230 231 /** 232 * Gets whether to follow redirects. 233 * 234 * @return whether to follow redirects. 235 */ 236 protected boolean getFollowRedirect() { 237 return followRedirect; 238 } 239 240 /** 241 * Gets a new HeadMethod. 242 * 243 * @return a new HeadMethod. 244 * @throws IOException if an IO error occurs. 245 */ 246 HeadMethod getHeadMethod() throws IOException { 247 // need to synchronize on the file system as the detach method will clear out "method" 248 synchronized (getFileSystem()) { 249 if (method != null) { 250 return method; 251 } 252 method = new HeadMethod(); 253 try { 254 setupMethod(method); 255 final HttpClient client = getAbstractFileSystem().getClient(); 256 client.executeMethod(method); 257 } finally { 258 method.releaseConnection(); 259 } 260 return method; 261 } 262 } 263 264 /** 265 * Gets the URL charset name. 266 * 267 * @return the URL charset name. 268 */ 269 protected String getUrlCharset() { 270 return urlCharset; 271 } 272 273 /** 274 * Gets the user agent. 275 * 276 * @return the user agent. 277 */ 278 protected String getUserAgent() { 279 return userAgent; 280 } 281 282 /** 283 * Prepares a HttpMethod object. 284 * 285 * @param method The object which gets prepared to access the file object. 286 * @throws FileSystemException if an error occurs. 287 * @throws URIException if path cannot be represented. 288 * @since 2.0 (was package) 289 */ 290 protected void setupMethod(final HttpMethod method) throws FileSystemException, URIException { 291 final String pathEncoded = ((URLFileName) getName()).getPathQueryEncoded(this.getUrlCharset()); 292 method.setPath(pathEncoded); 293 method.setFollowRedirects(this.getFollowRedirect()); 294 method.setRequestHeader("User-Agent", this.getUserAgent()); 295 } 296 297 /* 298 * protected Map doGetAttributes() throws Exception { TreeMap map = new TreeMap(); 299 * 300 * Header contentType = method.getResponseHeader("content-type"); if (contentType != null) { HeaderElement[] element 301 * = contentType.getValues(); if (element != null && element.length > 0) { map.put("content-type", 302 * element[0].getName()); } } 303 * 304 * map.put("content-encoding", method.getResponseCharSet()); return map; } 305 */ 306}