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.httpclient4; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.net.URL; 026import java.util.ArrayList; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030import java.util.Map.Entry; 031 032import lucee.commons.io.IOUtil; 033import lucee.commons.io.TemporaryStream; 034import lucee.commons.io.res.Resource; 035import lucee.commons.lang.ExceptionUtil; 036import lucee.commons.lang.StringUtil; 037import lucee.commons.net.http.Entity; 038import lucee.commons.net.http.HTTPResponse; 039import lucee.commons.net.http.httpclient4.entity.ByteArrayHttpEntity; 040import lucee.commons.net.http.httpclient4.entity.EmptyHttpEntity; 041import lucee.commons.net.http.httpclient4.entity.ResourceHttpEntity; 042import lucee.commons.net.http.httpclient4.entity.TemporaryStreamHttpEntity; 043import lucee.runtime.PageContextImpl; 044import lucee.runtime.engine.ThreadLocalPageContext; 045import lucee.runtime.exp.PageException; 046import lucee.runtime.net.http.ReqRspUtil; 047import lucee.runtime.net.proxy.ProxyData; 048import lucee.runtime.net.proxy.ProxyDataImpl; 049import lucee.runtime.op.Caster; 050import lucee.runtime.op.Decision; 051import lucee.runtime.type.dt.TimeSpan; 052import lucee.runtime.type.dt.TimeSpanImpl; 053import lucee.runtime.type.util.CollectionUtil; 054 055import org.apache.http.Header; 056import org.apache.http.HttpEntity; 057import org.apache.http.HttpEntityEnclosingRequest; 058import org.apache.http.HttpHost; 059import org.apache.http.HttpMessage; 060import org.apache.http.HttpResponse; 061import org.apache.http.NameValuePair; 062import org.apache.http.auth.AuthScope; 063import org.apache.http.auth.NTCredentials; 064import org.apache.http.auth.UsernamePasswordCredentials; 065import org.apache.http.client.AuthCache; 066import org.apache.http.client.ClientProtocolException; 067import org.apache.http.client.CredentialsProvider; 068import org.apache.http.client.HttpClient; 069import org.apache.http.client.methods.HttpDelete; 070import org.apache.http.client.methods.HttpGet; 071import org.apache.http.client.methods.HttpHead; 072import org.apache.http.client.methods.HttpPost; 073import org.apache.http.client.methods.HttpPut; 074import org.apache.http.client.methods.HttpUriRequest; 075import org.apache.http.client.params.ClientPNames; 076import org.apache.http.client.protocol.ClientContext; 077import org.apache.http.conn.params.ConnRoutePNames; 078import org.apache.http.entity.ByteArrayEntity; 079import org.apache.http.entity.StringEntity; 080import org.apache.http.impl.auth.BasicScheme; 081import org.apache.http.impl.client.BasicAuthCache; 082import org.apache.http.impl.client.DefaultHttpClient; 083import org.apache.http.impl.cookie.BasicClientCookie; 084import org.apache.http.message.BasicNameValuePair; 085import org.apache.http.params.BasicHttpParams; 086import org.apache.http.params.HttpConnectionParams; 087import org.apache.http.params.HttpParams; 088import org.apache.http.protocol.BasicHttpContext; 089import org.apache.http.protocol.HttpContext; 090 091public class HTTPEngine4Impl { 092 093 /** 094 * does a http get request 095 * @param url 096 * @param username 097 * @param password 098 * @param timeout in ms 099 * @param charset 100 * @param useragent 101 * @param proxyserver 102 * @param proxyport 103 * @param proxyuser 104 * @param proxypassword 105 * @param headers 106 * @return 107 * @throws IOException 108 */ 109 public static HTTPResponse get(URL url, String username,String password, long timeout, int maxRedirect, 110 String charset, String useragent, 111 ProxyData proxy, lucee.commons.net.http.Header[] headers) throws IOException { 112 HttpGet get = new HttpGet(url.toExternalForm()); 113 return _invoke(url,get, username, password, timeout,maxRedirect, charset, useragent, proxy, headers,null); 114 } 115 116 /** 117 * does a http post request 118 * @param url 119 * @param username 120 * @param password 121 * @param timeout in ms 122 * @param charset 123 * @param useragent 124 * @param proxyserver 125 * @param proxyport 126 * @param proxyuser 127 * @param proxypassword 128 * @param headers 129 * @return 130 * @throws IOException 131 */ 132 public static HTTPResponse post(URL url, String username,String password, long timeout, int maxRedirect, 133 String charset, String useragent, 134 ProxyData proxy, lucee.commons.net.http.Header[] headers) throws IOException { 135 HttpPost post = new HttpPost(url.toExternalForm()); 136 return _invoke(url,post, username, password, timeout,maxRedirect, charset, useragent, proxy, headers,null); 137 } 138 139 140 141 142 public static HTTPResponse post(URL url, String username,String password, long timeout, int maxRedirect, 143 String charset, String useragent, 144 ProxyData proxy, lucee.commons.net.http.Header[] headers,Map<String,String> formfields) throws IOException { 145 HttpPost post = new HttpPost(url.toExternalForm()); 146 147 return _invoke(url,post, username, password, timeout,maxRedirect, charset, useragent, proxy, headers,formfields); 148 } 149 150 151 /** 152 * does a http put request 153 * @param url 154 * @param username 155 * @param password 156 * @param timeout in ms 157 * @param charset 158 * @param useragent 159 * @param proxyserver 160 * @param proxyport 161 * @param proxyuser 162 * @param proxypassword 163 * @param headers 164 * @param body 165 * @return 166 * @throws IOException 167 * @throws PageException 168 */ 169 public static HTTPResponse put(URL url, String username,String password, long timeout, int maxRedirect, 170 String mimetype,String charset, String useragent, 171 ProxyData proxy, lucee.commons.net.http.Header[] headers, Object body) throws IOException { 172 HttpPut put= new HttpPut(url.toExternalForm()); 173 setBody(put,body,mimetype,charset); 174 return _invoke(url,put, username, password, timeout, maxRedirect, charset, useragent, proxy, headers,null); 175 176 } 177 178 /** 179 * does a http delete request 180 * @param url 181 * @param username 182 * @param password 183 * @param timeout in ms 184 * @param charset 185 * @param useragent 186 * @param proxyserver 187 * @param proxyport 188 * @param proxyuser 189 * @param proxypassword 190 * @param headers 191 * @return 192 * @throws IOException 193 */ 194 public static HTTPResponse delete(URL url, String username,String password, long timeout, int maxRedirect, 195 String charset, String useragent, 196 ProxyData proxy, lucee.commons.net.http.Header[] headers) throws IOException { 197 HttpDelete delete= new HttpDelete(url.toExternalForm()); 198 return _invoke(url,delete, username, password, timeout, maxRedirect, charset, useragent, proxy, headers,null); 199 } 200 201 /** 202 * does a http head request 203 * @param url 204 * @param username 205 * @param password 206 * @param timeout in ms 207 * @param charset 208 * @param useragent 209 * @param proxyserver 210 * @param proxyport 211 * @param proxyuser 212 * @param proxypassword 213 * @param headers 214 * @return 215 * @throws IOException 216 */ 217 public static HTTPResponse head(URL url, String username,String password, long timeout, int maxRedirect, 218 String charset, String useragent, 219 ProxyData proxy, lucee.commons.net.http.Header[] headers) throws IOException { 220 HttpHead head= new HttpHead(url.toExternalForm()); 221 return _invoke(url,head, username, password, timeout, maxRedirect, charset, useragent, proxy, headers,null); 222 } 223 224 225 226 public static lucee.commons.net.http.Header header(String name, String value) { 227 return new HeaderImpl(name, value); 228 } 229 230 231 private static Header toHeader(lucee.commons.net.http.Header header) { 232 if(header instanceof Header) return (Header) header; 233 if(header instanceof HeaderWrap) return ((HeaderWrap)header).header; 234 return new HeaderImpl(header.getName(), header.getValue()); 235 } 236 237 private static HTTPResponse _invoke(URL url,HttpUriRequest request,String username,String password, long timeout, int maxRedirect, 238 String charset, String useragent, 239 ProxyData proxy, lucee.commons.net.http.Header[] headers, Map<String,String> formfields) throws IOException { 240 241 // TODO HttpConnectionManager manager=new SimpleHttpConnectionManager();//MultiThreadedHttpConnectionManager(); 242 BasicHttpParams params = new BasicHttpParams(); 243 DefaultHttpClient client = createClient(params,maxRedirect); 244 HttpHost hh=new HttpHost(url.getHost(),url.getPort()); 245 setHeader(request,headers); 246 if(CollectionUtil.isEmpty(formfields))setContentType(request,charset); 247 setFormFields(request,formfields,charset); 248 setUserAgent(request,useragent); 249 setTimeout(params,timeout>0?TimeSpanImpl.fromMillis(timeout):null); 250 HttpContext context=setCredentials(client,hh, username, password,false); 251 setProxy(client,request,proxy); 252 if(context==null)context = new BasicHttpContext(); 253 return new HTTPResponse4Impl(url,context,request,execute(client,request,context)); 254 } 255 256 private static void setFormFields(HttpUriRequest request, Map<String, String> formfields, String charset) throws IOException { 257 if(!CollectionUtil.isEmpty(formfields)) { 258 if(!(request instanceof HttpPost)) throw new IOException("form fields are only suppported for post request"); 259 HttpPost post=(HttpPost) request; 260 List<NameValuePair> list = new ArrayList<NameValuePair>(); 261 Iterator<Entry<String, String>> it = formfields.entrySet().iterator(); 262 Entry<String, String> e; 263 while(it.hasNext()){ 264 e = it.next(); 265 list.add(new BasicNameValuePair(e.getKey(),e.getValue())); 266 } 267 if(StringUtil.isEmpty(charset)) charset=((PageContextImpl)ThreadLocalPageContext.get()).getWebCharset().name(); 268 269 post.setEntity(new org.apache.http.client.entity.UrlEncodedFormEntity(list,charset)); 270 } 271 } 272 273 public static DefaultHttpClient createClient(BasicHttpParams params, int maxRedirect) { 274 params.setParameter(ClientPNames.HANDLE_REDIRECTS, maxRedirect==0?Boolean.FALSE:Boolean.TRUE); 275 if(maxRedirect>0)params.setParameter(ClientPNames.MAX_REDIRECTS, new Integer(maxRedirect)); 276 params.setParameter(ClientPNames.REJECT_RELATIVE_REDIRECT, Boolean.FALSE); 277 return new DefaultHttpClient(params); 278 } 279 280 private static void setUserAgent(HttpMessage hm, String useragent) { 281 if(useragent!=null)hm.setHeader("User-Agent",useragent); 282 } 283 284 private static void setContentType(HttpMessage hm, String charset) { 285 if(charset!=null) hm.setHeader("Content-type", "text/html; charset="+charset); 286 } 287 288 private static void setHeader(HttpMessage hm,lucee.commons.net.http.Header[] headers) { 289 addHeader(hm, headers); 290 } 291 292 private static void addHeader(HttpMessage hm,lucee.commons.net.http.Header[] headers) { 293 if(headers!=null) { 294 for(int i=0;i<headers.length;i++) 295 hm.addHeader(toHeader(headers[i])); 296 } 297 } 298 299 /** 300 * sets the timeout for the connection and socket (same value) 301 */ 302 public static void setTimeout(HttpParams params, TimeSpan timeout) { 303 if(timeout!=null && timeout.getMillis()>0){ 304 int ms = (int)timeout.getMillis(); 305 if(ms<0) ms=Integer.MAX_VALUE; 306 307 HttpConnectionParams.setConnectionTimeout(params,ms); 308 HttpConnectionParams.setSoTimeout(params, ms); 309 } 310 } 311 312 public static BasicHttpContext setCredentials(DefaultHttpClient client, HttpHost httpHost, String username,String password, boolean preAuth) { 313 // set Username and Password 314 if(!StringUtil.isEmpty(username,true)) { 315 if(password==null)password=""; 316 CredentialsProvider cp = client.getCredentialsProvider(); 317 cp.setCredentials( 318 new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), 319 new UsernamePasswordCredentials(username,password)); 320 321 322 323 BasicHttpContext httpContext = new BasicHttpContext(); 324 if(preAuth) { 325 AuthCache authCache = new BasicAuthCache(); 326 authCache.put(httpHost, new BasicScheme()); 327 httpContext.setAttribute(ClientContext.AUTH_CACHE, authCache); 328 } 329 return httpContext; 330 } 331 return null; 332 } 333 334 public static void setNTCredentials(DefaultHttpClient client, String username,String password, String workStation, String domain) { 335 // set Username and Password 336 if(!StringUtil.isEmpty(username,true)) { 337 if(password==null)password=""; 338 CredentialsProvider cp = client.getCredentialsProvider(); 339 cp.setCredentials( 340 new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), 341 new NTCredentials(username,password,workStation,domain)); 342 //httpMethod.setDoAuthentication( true ); 343 } 344 } 345 346 public static void setBody(HttpEntityEnclosingRequest req, Object body, String mimetype,String charset) throws IOException { 347 if(body!=null)req.setEntity(toHttpEntity(body,mimetype,charset)); 348 } 349 350 public static void setProxy(DefaultHttpClient client, HttpUriRequest request, ProxyData proxy) { 351 // set Proxy 352 if(ProxyDataImpl.isValid(proxy)) { 353 HttpHost host = new HttpHost(proxy.getServer(), proxy.getPort()==-1?80:proxy.getPort()); 354 client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, host); 355 if(!StringUtil.isEmpty(proxy.getUsername())) { 356 357 client.getCredentialsProvider().setCredentials( 358 new AuthScope(proxy.getServer(), proxy.getPort()), 359 new UsernamePasswordCredentials(proxy.getUsername(),proxy.getPassword())); 360 } 361 } 362 } 363 364 public static void addCookie(DefaultHttpClient client, String domain, String name, String value, String path, String charset) { 365 if(ReqRspUtil.needEncoding(name,false)) name=ReqRspUtil.encode(name, charset); 366 if(ReqRspUtil.needEncoding(value,false)) value=ReqRspUtil.encode(value, charset); 367 BasicClientCookie cookie = new BasicClientCookie(name, value); 368 if(!StringUtil.isEmpty(domain,true))cookie.setDomain(domain); 369 if(!StringUtil.isEmpty(path,true))cookie.setPath(path); 370 371 client.getCookieStore().addCookie(cookie); 372 } 373 374 /** 375 * convert input to HTTP Entity 376 * @param value 377 * @param mimetype not used for binary input 378 * @param charset not used for binary input 379 * @return 380 * @throws IOException 381 */ 382 private static HttpEntity toHttpEntity(Object value, String mimetype, String charset) throws IOException { 383 if(value instanceof HttpEntity) return (HttpEntity) value; 384 try{ 385 if(value instanceof InputStream) { 386 return new ByteArrayEntity(IOUtil.toBytes((InputStream)value)); 387 } 388 else if(Decision.isCastableToBinary(value,false)){ 389 return new ByteArrayEntity(Caster.toBinary(value)); 390 } 391 else { 392 return new StringEntity(Caster.toString(value),mimetype,charset); 393 } 394 } 395 catch(Exception e){ 396 throw ExceptionUtil.toIOException(e); 397 } 398 } 399 400 401 public static Entity getEmptyEntity(String contentType) { 402 return new EmptyHttpEntity(contentType); 403 } 404 405 public static Entity getByteArrayEntity(byte[] barr, String contentType) { 406 return new ByteArrayHttpEntity(barr,contentType); 407 } 408 409 public static Entity getTemporaryStreamEntity(TemporaryStream ts,String contentType) { 410 return new TemporaryStreamHttpEntity(ts,contentType); 411 } 412 413 public static Entity getResourceEntity(Resource res, String contentType) { 414 return new ResourceHttpEntity(res,contentType); 415 } 416 417 /* 418 * this method exist because the method execute is returning a different type depending on the version of the library 419 */ 420 public static HttpResponse execute(HttpClient client, HttpUriRequest req, HttpContext context) throws ClientProtocolException, IOException { 421 try { 422 Method exe = client.getClass().getMethod("execute", new Class[]{HttpUriRequest.class,HttpContext.class}); 423 return (HttpResponse)exe.invoke(client, new Object[]{req,context}); 424 } 425 catch (InvocationTargetException ite) { 426 Throwable t = ite.getTargetException(); 427 if(t instanceof IOException)throw (IOException)t; 428 if(t instanceof ClientProtocolException)throw (ClientProtocolException)t; 429 throw new RuntimeException(t); 430 } 431 catch (Exception e) { 432 throw new RuntimeException(e); 433 } 434 } 435 436 437}