001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.io.res.type.http;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.net.URL;
024
025import lucee.commons.io.IOUtil;
026import lucee.commons.io.res.ContentType;
027import lucee.commons.io.res.Resource;
028import lucee.commons.io.res.ResourceProvider;
029import lucee.commons.io.res.util.ReadOnlyResourceSupport;
030import lucee.commons.io.res.util.ResourceUtil;
031import lucee.commons.lang.StringUtil;
032import lucee.commons.net.http.HTTPEngine;
033import lucee.commons.net.http.HTTPResponse;
034import lucee.commons.net.http.Header;
035import lucee.runtime.net.proxy.ProxyData;
036import lucee.runtime.net.proxy.ProxyDataImpl;
037import lucee.runtime.op.Caster;
038
039
040public class HTTPResource extends ReadOnlyResourceSupport {
041        
042        private final HTTPResourceProvider provider;
043        private final HTTPConnectionData data;
044        private final String path;
045        private final String name;
046        private HTTPResponse http;
047        
048
049        public HTTPResource(HTTPResourceProvider provider, HTTPConnectionData data) {
050                this.provider=provider;
051                this.data=data;
052
053                String[] pathName=ResourceUtil.translatePathName(data.path);
054                this.path=pathName[0];
055                this.name=pathName[1];
056
057        }
058
059        private HTTPResponse getHTTPResponse(boolean create) throws IOException {
060                if(create || http==null) {
061                        //URL url = HTTPUtil.toURL("http://"+data.host+":"+data.port+"/"+data.path);
062                        URL url = new URL(provider.getProtocol(),data.host,data.port,data.path);
063                        // TODO Support for proxy
064                        ProxyData pd=data.hasProxyData()?data.proxyData:ProxyDataImpl.NO_PROXY;
065                                
066                        http = HTTPEngine.get(url, data.username, data.password, _getTimeout(),HTTPEngine.MAX_REDIRECT,null, data.userAgent, 
067                                        pd,
068                                        null);
069                }
070                return http;
071        }
072
073        private int getStatusCode() throws IOException {
074                if(http==null) {
075                        URL url = new URL(provider.getProtocol(),data.host,data.port,data.path);
076                        ProxyData pd=data.hasProxyData()?data.proxyData:ProxyDataImpl.NO_PROXY;
077                        return HTTPEngine.head(url, data.username, data.password, _getTimeout(),HTTPEngine.MAX_REDIRECT, 
078                                        null, data.userAgent, 
079                                        pd,
080                                        null).getStatusCode();
081                }
082                return http.getStatusCode();
083        }
084
085        public ContentType getContentType() throws IOException {
086                if(http==null) {
087                        URL url = new URL(provider.getProtocol(),data.host,data.port,data.path);
088                        ProxyData pd=data.hasProxyData()?data.proxyData:ProxyDataImpl.NO_PROXY;
089                        return HTTPEngine.head(url, data.username, data.password, _getTimeout(),HTTPEngine.MAX_REDIRECT, 
090                                        null, data.userAgent, 
091                                        pd,
092                                        null).getContentType();
093                }
094                return http.getContentType();
095        }
096        
097        
098
099        public boolean exists() {
100                try {
101                        provider.read(this);
102                        int code = getStatusCode();//getHttpMethod().getStatusCode();
103                        return code!=404;
104                }
105                catch (IOException e) {
106                        return false;
107                }
108        }
109        
110        public int statusCode() {
111                try {
112                        provider.read(this);
113                        return getHTTPResponse(false).getStatusCode();
114                } catch (IOException e) {
115                        return 0;
116                }
117        }
118
119        public InputStream getInputStream() throws IOException {
120                //ResourceUtil.checkGetInputStreamOK(this);
121                //provider.lock(this);
122                provider.read(this);
123                HTTPResponse method = getHTTPResponse(true);
124                try {
125                        return IOUtil.toBufferedInputStream(method.getContentAsStream());
126                } 
127                catch (IOException e) {
128                        //provider.unlock(this);
129                        throw e;
130                }
131        }
132
133        @Override
134        public String getName() {
135                return name;
136        }
137
138        @Override
139        public String getParent() {
140                if(isRoot()) return null;
141                return provider.getProtocol().concat("://").concat(data.key()).concat(path.substring(0,path.length()-1));
142        }
143
144        private boolean isRoot() {
145                return StringUtil.isEmpty(name);
146        }
147
148        @Override
149        public Resource getParentResource() {
150                if(isRoot()) return null;
151                return new HTTPResource(provider,
152                                new HTTPConnectionData(data.username,data.password,data.host,data.port,path,data.proxyData,data.userAgent));
153        }
154
155        @Override
156        public String getPath() {
157                return provider.getProtocol().concat("://").concat(data.key()).concat(path).concat(name);
158        }
159
160        @Override
161        public Resource getRealResource(String realpath) {
162                realpath=ResourceUtil.merge(path.concat(name), realpath);
163                if(realpath.startsWith("../"))return null;
164                return new HTTPResource(provider,new HTTPConnectionData(data.username,data.password,data.host,data.port,realpath,data.proxyData,data.userAgent));
165        }
166
167        @Override
168        public ResourceProvider getResourceProvider() {
169                return provider;
170        }
171
172        @Override
173        public boolean isAbsolute() {
174                return true;
175        }
176
177        @Override
178        public boolean isDirectory() {
179                return false;
180        }
181
182        @Override
183        public boolean isFile() {
184                return exists();
185        }
186
187        @Override
188        public boolean isReadable() {
189                return exists();
190        }
191
192        public long lastModified() {
193                int last=0;
194                try {
195                        Header cl=getHTTPResponse(false).getLastHeaderIgnoreCase("last-modified");
196                        if(cl!=null && exists()) last=Caster.toIntValue(cl.getValue(),0);
197                }
198                catch (IOException e) {}
199                return last;
200        }
201
202        public long length() {
203                try {
204                        if(!exists()) return 0;
205                        return getHTTPResponse(false).getContentLength();       
206                }
207                catch (IOException e) {
208                        return 0;
209                }
210        }
211
212        public Resource[] listResources() {
213                return null;
214        }
215
216        public void setProxyData(ProxyData pd) {
217                this.http=null;
218                this.data.setProxyData(pd);
219        }
220
221        public void setUserAgent(String userAgent) {
222                this.http=null;
223                this.data.userAgent=userAgent;
224        }
225
226        public void setTimeout(int timeout) {
227                this.http=null;
228                data.timeout=timeout;
229        }
230
231        
232        private int _getTimeout() {
233                return data.timeout<provider.getSocketTimeout()?data.timeout:provider.getSocketTimeout();
234        }
235}