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