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.net.http.httpclient3; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.net.MalformedURLException; 024import java.net.URL; 025import java.util.Iterator; 026import java.util.Map; 027import java.util.Map.Entry; 028 029import lucee.commons.io.TemporaryStream; 030import lucee.commons.io.res.Resource; 031import lucee.commons.lang.ExceptionUtil; 032import lucee.commons.lang.StringUtil; 033import lucee.commons.net.http.Entity; 034import lucee.commons.net.http.HTTPEngine; 035import lucee.commons.net.http.HTTPResponse; 036import lucee.commons.net.http.Header; 037import lucee.commons.net.http.httpclient3.entity.EmptyRequestEntity; 038import lucee.commons.net.http.httpclient3.entity.ResourceRequestEntity; 039import lucee.commons.net.http.httpclient3.entity.TemporaryStreamRequestEntity; 040import lucee.commons.net.http.httpclient3.entity._ByteArrayRequestEntity; 041import lucee.runtime.exp.PageException; 042import lucee.runtime.net.proxy.ProxyData; 043import lucee.runtime.net.proxy.ProxyDataImpl; 044import lucee.runtime.op.Caster; 045import lucee.runtime.op.Decision; 046import lucee.runtime.type.util.CollectionUtil; 047 048import org.apache.commons.httpclient.HostConfiguration; 049import org.apache.commons.httpclient.HttpClient; 050import org.apache.commons.httpclient.HttpException; 051import org.apache.commons.httpclient.HttpMethod; 052import org.apache.commons.httpclient.HttpState; 053import org.apache.commons.httpclient.NameValuePair; 054import org.apache.commons.httpclient.UsernamePasswordCredentials; 055import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; 056import org.apache.commons.httpclient.methods.DeleteMethod; 057import org.apache.commons.httpclient.methods.EntityEnclosingMethod; 058import org.apache.commons.httpclient.methods.GetMethod; 059import org.apache.commons.httpclient.methods.HeadMethod; 060import org.apache.commons.httpclient.methods.InputStreamRequestEntity; 061import org.apache.commons.httpclient.methods.PostMethod; 062import org.apache.commons.httpclient.methods.PutMethod; 063import org.apache.commons.httpclient.methods.RequestEntity; 064import org.apache.commons.httpclient.methods.StringRequestEntity; 065 066/** 067 * 068 */ 069public final class HTTPEngine3Impl { 070 071 072 073 public static HTTPResponse get(URL url, String username, String password, long timeout, int maxRedirect, 074 String charset, String useragent,ProxyData proxy, Header[] headers) throws IOException { 075 return _invoke(new GetMethod(url.toExternalForm()), url, username, password, timeout,maxRedirect, charset, useragent, proxy, headers,null,null); 076 } 077 078 public static HTTPResponse head(URL url, String username, String password, int timeout, int maxRedirect, 079 String charset, String useragent,ProxyData proxy, Header[] headers) throws IOException { 080 return _invoke(new HeadMethod(url.toExternalForm()), url, username, password, timeout,maxRedirect, charset, useragent, proxy, headers,null,null); 081 } 082 083 public static HTTPResponse post(URL url, String username, String password, long timeout, int maxRedirect, 084 String charset, String useragent, ProxyData proxy, Header[] headers, Map<String,String> params) throws IOException { 085 return _invoke(new PostMethod(url.toExternalForm()), url, username, password, timeout,maxRedirect, charset, useragent, proxy, headers,params,null); 086 } 087 088 public static HTTPResponse put(URL url, String username, String password, int timeout, int maxRedirect, 089 String charset, String useragent,ProxyData proxy, Header[] headers, Object body) throws IOException { 090 return _invoke(new PutMethod(url.toExternalForm()), url, username, password, timeout,maxRedirect, charset, useragent, proxy, headers,null,body); 091 } 092 093 public static HTTPResponse delete(URL url, String username, String password, int timeout, int maxRedirect, 094 String charset, String useragent,ProxyData proxy, Header[] headers) throws IOException { 095 return _invoke(new DeleteMethod(url.toExternalForm()), url, username, password, timeout,maxRedirect, charset, useragent, proxy, headers,null,null); 096 } 097 098 099 private static HTTPResponse _invoke(HttpMethod httpMethod, URL url, String username, String password, long timeout, int maxRedirect, 100 String charset, String useragent, ProxyData proxy, Header[] headers, Map<String,String> params, Object body) throws IOException { 101 102 HttpClient client = new HttpClient(); 103 HostConfiguration config = client.getHostConfiguration(); 104 HttpState state = client.getState(); 105 106 setHeader(httpMethod,headers); 107 if(CollectionUtil.isEmpty(params))setContentType(httpMethod,charset); 108 setUserAgent(httpMethod,useragent); 109 setTimeout(client,timeout); 110 setParams(httpMethod,params); 111 setCredentials(client,httpMethod,username,password); 112 setProxy(config,state,proxy); 113 if(body!=null && httpMethod instanceof EntityEnclosingMethod)setBody((EntityEnclosingMethod)httpMethod,body); 114 return new HTTPResponse3Impl(execute(client,httpMethod,maxRedirect),url); 115 } 116 117 /** 118 * Execute a HTTTP Client and follow redirect over different hosts 119 * @param client 120 * @param method 121 * @param doRedirect 122 * @return 123 * @throws IOException 124 * @throws HttpException 125 */ 126 public static HttpMethod execute(HttpClient client, HttpMethod method, int maxRedirect) throws HttpException, IOException { 127 short count=0; 128 method.setFollowRedirects(false); 129 130 while(isRedirect(client.executeMethod(method)) && count++ < maxRedirect) { 131 method=rewrite(method); 132 } 133 return method; 134 } 135 136 /** 137 * rewrite request method 138 * @param method 139 * @return 140 * @throws MalformedURLException 141 */ 142 private static HttpMethod rewrite(HttpMethod method) throws MalformedURLException { 143 org.apache.commons.httpclient.Header location = method.getResponseHeader("location"); 144 if(location==null) return method; 145 146 HostConfiguration config = method.getHostConfiguration(); 147 URL url; 148 try { 149 url = new URL(location.getValue()); 150 } 151 catch (MalformedURLException e) { 152 153 url=new URL(config.getProtocol().getScheme(), 154 config.getHost(), 155 config.getPort(), 156 mergePath(method.getPath(),location.getValue())); 157 } 158 159 method= clone(method,url); 160 161 return method; 162 } 163 164 /** 165 * FUNKTIONIERT NICHT, HOST WIRD NICHT UEBERNOMMEN 166 * Clones a http method and sets a new url 167 * @param src 168 * @param url 169 * @return 170 */ 171 private static HttpMethod clone(HttpMethod src, URL url) { 172 HttpMethod trg = HttpMethodCloner.clone(src); 173 HostConfiguration trgConfig = trg.getHostConfiguration(); 174 trgConfig.setHost(url.getHost(),url.getPort(),url.getProtocol()); 175 trg.setPath(url.getPath()); 176 trg.setQueryString(url.getQuery()); 177 178 return trg; 179 } 180 181 /** 182 * merge to pathes to one 183 * @param current 184 * @param relPath 185 * @return 186 * @throws MalformedURLException 187 */ 188 private static String mergePath(String current, String relPath) throws MalformedURLException { 189 190 // get current directory 191 String currDir; 192 if(current==null || current.indexOf('/')==-1)currDir="/"; 193 else if(current.endsWith("/"))currDir=current; 194 else currDir=current.substring(0,current.lastIndexOf('/')+1); 195 196 // merge together 197 String path; 198 if(relPath.startsWith("./"))path=currDir+relPath.substring(2); 199 else if(relPath.startsWith("/"))path=relPath; 200 else if(!relPath.startsWith("../"))path=currDir+relPath; 201 else { 202 while(relPath.startsWith("../") || currDir.length()==0) { 203 relPath=relPath.substring(3); 204 currDir=currDir.substring(0,currDir.length()-1); 205 int index = currDir.lastIndexOf('/'); 206 if(index==-1)throw new MalformedURLException("invalid relpath definition for URL"); 207 currDir=currDir.substring(0,index+1); 208 } 209 path=currDir+relPath; 210 } 211 212 return path; 213 } 214 215 /** 216 * checks if status code is a redirect 217 * @param status 218 * @return is redirect 219 */ 220 private static boolean isRedirect(int status) { 221 return 222 status==HTTPEngine.STATUS_REDIRECT_FOUND || 223 status==HTTPEngine.STATUS_REDIRECT_MOVED_PERMANENTLY || 224 status==HTTPEngine.STATUS_REDIRECT_SEE_OTHER; 225 } 226 227 228 229 private static void setBody(EntityEnclosingMethod httpMethod, Object body) throws IOException { 230 if(body!=null) 231 try { 232 httpMethod.setRequestEntity(toRequestEntity(body)); 233 } catch (PageException e) { 234 throw ExceptionUtil.toIOException(e); 235 } 236 } 237 238 private static void setProxy(HostConfiguration config, HttpState state, ProxyData data) { 239 // set Proxy 240 if(ProxyDataImpl.isValid(data)) { 241 config.setProxy(data.getServer(),data.getPort()<=0?80:data.getPort()); 242 if(ProxyDataImpl.hasCredentials(data)) { 243 state.setProxyCredentials(null,null,new UsernamePasswordCredentials(data.getUsername(),StringUtil.emptyIfNull(data.getPassword()))); 244 } 245 } 246 } 247 248 private static void setCredentials(HttpClient client, HttpMethod httpMethod, String username,String password) { 249 // set Username and Password 250 if(username!=null) { 251 if(password==null)password=""; 252 client.getState().setCredentials(null,null,new UsernamePasswordCredentials(username, password)); 253 httpMethod.setDoAuthentication( true ); 254 } 255 } 256 257 private static void setTimeout(HttpClient client, long timeout) { 258 if(timeout>0){ 259 client.setConnectionTimeout((int)timeout); 260 client.setTimeout((int)timeout); 261 } 262 } 263 264 private static void setUserAgent(HttpMethod httpMethod, String useragent) { 265 if(useragent!=null)httpMethod.setRequestHeader("User-Agent",useragent); 266 } 267 268 private static void setContentType(HttpMethod httpMethod, String charset) { 269 if(charset!=null)httpMethod.addRequestHeader("Content-type", "text/html; charset="+charset ); 270 } 271 272 private static void setHeader(HttpMethod httpMethod,Header[] headers) { 273 if(headers!=null) { 274 for(int i=0;i<headers.length;i++) 275 httpMethod.addRequestHeader(headers[i].getName(), headers[i].getValue()); 276 } 277 } 278 279 private static void setParams(HttpMethod httpMethod, Map<String, String> params) { 280 if(params==null || !(httpMethod instanceof PostMethod)) return; 281 PostMethod post=(PostMethod) httpMethod; 282 Iterator<Entry<String, String>> it = params.entrySet().iterator(); 283 Entry<String, String> e; 284 while(it.hasNext()){ 285 e = it.next(); 286 post.addParameter(new NameValuePair(e.getKey(),e.getValue())); 287 } 288 } 289 290 private static RequestEntity toRequestEntity(Object value) throws PageException { 291 if(value instanceof RequestEntity) return (RequestEntity) value; 292 293 else if(value instanceof InputStream) { 294 return new InputStreamRequestEntity((InputStream)value,"application/octet-stream"); 295 } 296 else if(Decision.isCastableToBinary(value,false)){ 297 return new ByteArrayRequestEntity(Caster.toBinary(value)); 298 } 299 else { 300 return new StringRequestEntity(Caster.toString(value)); 301 } 302 } 303 304 public static Header header(String name, String value) { 305 return new HeaderImpl(name, value); 306 } 307 308 public static Entity getEmptyEntity(String contentType) { 309 return new EmptyRequestEntity(contentType); 310 } 311 312 public static Entity getByteArrayEntity(byte[] barr, String contentType) { 313 return new _ByteArrayRequestEntity(barr, contentType); 314 } 315 316 public static Entity getTemporaryStreamEntity(TemporaryStream ts, String contentType) { 317 return new TemporaryStreamRequestEntity(ts,contentType); 318 } 319 320 public static Entity getResourceEntity(Resource res, String contentType) { 321 return new ResourceRequestEntity(res,contentType); 322 } 323}