001 package railo.runtime.tag; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.EOFException; 005 import java.io.FileNotFoundException; 006 import java.io.IOException; 007 import java.io.InputStream; 008 import java.io.PrintStream; 009 import java.io.UnsupportedEncodingException; 010 import java.net.MalformedURLException; 011 import java.net.URL; 012 import java.util.ArrayList; 013 import java.util.Iterator; 014 import java.util.zip.GZIPInputStream; 015 016 import org.apache.http.HttpEntityEnclosingRequest; 017 import org.apache.http.HttpHost; 018 import org.apache.http.HttpResponse; 019 import org.apache.http.NameValuePair; 020 import org.apache.http.client.methods.HttpDelete; 021 import org.apache.http.client.methods.HttpGet; 022 import org.apache.http.client.methods.HttpHead; 023 import org.apache.http.client.methods.HttpOptions; 024 import org.apache.http.client.methods.HttpPost; 025 import org.apache.http.client.methods.HttpPut; 026 import org.apache.http.client.methods.HttpRequestBase; 027 import org.apache.http.client.methods.HttpTrace; 028 import org.apache.http.client.methods.HttpUriRequest; 029 import org.apache.http.entity.StringEntity; 030 import org.apache.http.entity.mime.FormBodyPart; 031 import org.apache.http.entity.mime.HttpMultipartMode; 032 import org.apache.http.entity.mime.MultipartEntity; 033 import org.apache.http.entity.mime.content.ContentBody; 034 import org.apache.http.entity.mime.content.StringBody; 035 import org.apache.http.impl.client.DefaultHttpClient; 036 import org.apache.http.message.BasicNameValuePair; 037 import org.apache.http.params.BasicHttpParams; 038 import org.apache.http.protocol.BasicHttpContext; 039 import org.apache.http.protocol.HttpContext; 040 041 import railo.print; 042 import railo.commons.io.CharsetUtil; 043 import railo.commons.io.IOUtil; 044 import railo.commons.io.SystemUtil; 045 import railo.commons.io.res.Resource; 046 import railo.commons.io.res.util.ResourceUtil; 047 import railo.commons.lang.StringUtil; 048 import railo.commons.lang.mimetype.ContentType; 049 import railo.commons.net.HTTPUtil; 050 import railo.commons.net.URLEncoder; 051 import railo.commons.net.http.HTTPEngine; 052 import railo.commons.net.http.Header; 053 import railo.commons.net.http.httpclient4.CachingGZIPInputStream; 054 import railo.commons.net.http.httpclient4.HTTPEngine4Impl; 055 import railo.commons.net.http.httpclient4.HTTPPatchFactory; 056 import railo.commons.net.http.httpclient4.HTTPResponse4Impl; 057 import railo.commons.net.http.httpclient4.ResourceBody; 058 import railo.runtime.config.Config; 059 import railo.runtime.config.ConfigWeb; 060 import railo.runtime.engine.ThreadLocalPageContext; 061 import railo.runtime.exp.ApplicationException; 062 import railo.runtime.exp.ExpressionException; 063 import railo.runtime.exp.HTTPException; 064 import railo.runtime.exp.NativeException; 065 import railo.runtime.exp.PageException; 066 import railo.runtime.ext.tag.BodyTagImpl; 067 import railo.runtime.net.http.MultiPartResponseUtils; 068 import railo.runtime.net.http.ReqRspUtil; 069 import railo.runtime.net.proxy.ProxyData; 070 import railo.runtime.net.proxy.ProxyDataImpl; 071 import railo.runtime.op.Caster; 072 import railo.runtime.text.csv.CSVParser; 073 import railo.runtime.type.Array; 074 import railo.runtime.type.ArrayImpl; 075 import railo.runtime.type.Collection.Key; 076 import railo.runtime.type.KeyImpl; 077 import railo.runtime.type.Query; 078 import railo.runtime.type.Struct; 079 import railo.runtime.type.StructImpl; 080 import railo.runtime.type.util.KeyConstants; 081 import railo.runtime.type.util.ListUtil; 082 import railo.runtime.util.URLResolver; 083 084 // MUST change behavor of mltiple headers now is a array, it das so? 085 086 /** 087 * Lets you execute HTTP POST and GET operations on files. Using cfhttp, you can execute standard 088 * GET operations and create a query object from a text file. POST operations lets you upload MIME file 089 * types to a server, or post cookie, formfield, URL, file, or CGI variables directly to a specified server. 090 * 091 * 092 * 093 * 094 **/ 095 public final class Http4 extends BodyTagImpl implements Http { 096 097 public static final String MULTIPART_RELATED = "multipart/related"; 098 public static final String MULTIPART_FORM_DATA = "multipart/form-data"; 099 100 /** 101 * Maximum redirect count (5) 102 */ 103 public static final short MAX_REDIRECT=15; 104 105 /** 106 * Constant value for HTTP Status Code "moved Permanently 301" 107 */ 108 public static final int STATUS_REDIRECT_MOVED_PERMANENTLY=301; 109 /** 110 * Constant value for HTTP Status Code "Found 302" 111 */ 112 public static final int STATUS_REDIRECT_FOUND=302; 113 /** 114 * Constant value for HTTP Status Code "see other 303" 115 */ 116 public static final int STATUS_REDIRECT_SEE_OTHER=303; 117 118 119 public static final int STATUS_REDIRECT_TEMPORARY_REDIRECT = 307; 120 121 122 123 124 125 126 private static final short METHOD_GET=0; 127 private static final short METHOD_POST=1; 128 private static final short METHOD_HEAD=2; 129 private static final short METHOD_PUT=3; 130 private static final short METHOD_DELETE=4; 131 private static final short METHOD_OPTIONS=5; 132 private static final short METHOD_TRACE=6; 133 private static final short METHOD_PATCH=7; 134 135 private static final String NO_MIMETYPE="Unable to determine MIME type of file."; 136 137 private static final short GET_AS_BINARY_NO=0; 138 private static final short GET_AS_BINARY_YES=1; 139 private static final short GET_AS_BINARY_AUTO=2; 140 141 private static final Key STATUSCODE = KeyConstants._statuscode; 142 private static final Key CHARSET = KeyConstants._charset; 143 144 private static final Key ERROR_DETAIL = KeyImpl.intern("errordetail"); 145 private static final Key STATUS_CODE = KeyImpl.intern("status_code"); 146 private static final Key STATUS_TEXT = KeyImpl.intern("status_text"); 147 private static final Key HTTP_VERSION = KeyImpl.intern("http_version"); 148 149 150 private static final Key FILE_CONTENT = KeyImpl.intern("filecontent"); 151 private static final Key EXPLANATION = KeyImpl.intern("explanation"); 152 private static final Key RESPONSEHEADER = KeyImpl.intern("responseheader"); 153 private static final Key SET_COOKIE = KeyImpl.intern("set-cookie"); 154 155 private static final short AUTH_TYPE_BASIC = 0; 156 private static final short AUTH_TYPE_NTLM = 1; 157 158 159 160 161 static { 162 //Protocol myhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443); 163 //Protocol.registerProtocol("https", new Protocol("https", new EasySSLProtocolSocketFactory(), 443)); 164 } 165 166 167 private ArrayList<HttpParamBean> params=new ArrayList<HttpParamBean>(); 168 169 170 /** When required by a server, a valid password. */ 171 private String password; 172 173 /** Required for creating a query. Options are a tab or comma. Default is a comma. */ 174 private char delimiter=','; 175 176 /** Yes or No. Default is No. For GET and POST operations, if Yes, page reference returned into the 177 ** fileContent internal variable has its internal URLs fully resolved, including port number, so that 178 ** links remain intact. */ 179 private boolean resolveurl; 180 181 /** A value, in seconds. When a URL timeout is specified in the browser */ 182 private long timeout=-1; 183 184 /** Host name or IP address of a proxy server. */ 185 private String proxyserver; 186 187 /** The filename to be used for the file that is accessed. For GET operations, defaults to the name 188 ** pecified in url. Enter path information in the path attribute. */ 189 private String strFile; 190 191 /** The path to the directory in which a file is to be stored. If a path is not specified in a POST 192 ** or GET operation, a variable is created (cfhttp.fileContent) that you can use to display the results 193 ** of the POST operation in a cfoutput. */ 194 private String strPath; 195 196 /** Boolean indicating whether to throw an exception that can be caught by using the cftry and 197 ** cfcatch tags. The default is NO. */ 198 private boolean throwonerror; 199 200 /** set the charset for the call. */ 201 private String charset=null; 202 203 /** The port number on the proxy server from which the object is requested. Default is 80. When 204 ** used with resolveURL, the URLs of retrieved documents that specify a port number are automatically 205 ** resolved to preserve links in the retrieved document. */ 206 private int proxyport=80; 207 208 /** Specifies the column names for a query when creating a query as a result of a cfhttp GET. */ 209 private String[] columns; 210 211 /** The port number on the server from which the object is requested. Default is 80. When used with 212 ** resolveURL, the URLs of retrieved documents that specify a port number are automatically resolved to 213 ** preserve links in the retrieved document. If a port number is specified in the url attribute, the port 214 ** value overrides the value of the port attribute. */ 215 private int port=-1; 216 217 /** User agent request header. */ 218 private String useragent="Railo (CFML Engine)"; 219 220 /** Required for creating a query. Indicates the start and finish of a column. Should be 221 ** appropriately escaped when embedded in a column. For example, if the qualifier is a double quotation 222 ** mark, it should be escaped as """". If there is no text qualifier in the file, specify it as " ". 223 ** Default is the double quotation mark ("). */ 224 private char textqualifier='"'; 225 226 /** When required by a server, a valid username. */ 227 private String username; 228 229 /** Full URL of the host name or IP address of the server on which the file resides. The URL must be 230 ** an absolute URL, including the protocol (http or https) and hostname. It may optionally contain a port 231 ** number. Port numbers specified in the url attribute override the port attribute. */ 232 private String url; 233 234 /** Boolean indicating whether to redirect execution or stop execution.*/ 235 private boolean redirect=true; 236 237 238 /** The name to assign to a query if the a query is constructed from a file. */ 239 private String name; 240 241 /** GET or POST. Use GET to download a text or binary file or to create a query from the contents 242 ** of a text file. Use POST to send information to a server page or a CGI program for processing. POST 243 ** requires the use of a cfhttpparam tag. */ 244 private short method=METHOD_GET; 245 246 //private boolean hasBody=false; 247 248 private boolean firstrowasheaders=true; 249 250 private String proxyuser=null; 251 private String proxypassword=""; 252 private boolean multiPart=false; 253 private String multiPartType=MULTIPART_FORM_DATA; 254 255 private short getAsBinary=GET_AS_BINARY_NO; 256 private String result="cfhttp"; 257 258 private boolean addtoken=false; 259 260 private short authType=AUTH_TYPE_BASIC; 261 private String workStation=null; 262 private String domain=null; 263 private boolean preauth=true; 264 265 266 @Override 267 public void release() { 268 super.release(); 269 params.clear(); 270 password=null; 271 delimiter=','; 272 resolveurl=false; 273 timeout=-1L; 274 proxyserver=null; 275 proxyport=80; 276 proxyuser=null; 277 proxypassword=""; 278 strFile=null; 279 throwonerror=false; 280 charset=null; 281 columns=null; 282 port=-1; 283 useragent="Railo (CFML Engine)"; 284 textqualifier='"'; 285 username=null; 286 url=null; 287 redirect=true; 288 strPath=null; 289 name=null; 290 method=METHOD_GET; 291 //hasBody=false; 292 firstrowasheaders=true; 293 294 getAsBinary=GET_AS_BINARY_NO; 295 multiPart=false; 296 multiPartType=MULTIPART_FORM_DATA; 297 result="cfhttp"; 298 addtoken=false; 299 300 authType=AUTH_TYPE_BASIC; 301 workStation=null; 302 domain=null; 303 preauth=true; 304 } 305 306 /** 307 * @param firstrowasheaders 308 */ 309 public void setFirstrowasheaders(boolean firstrowasheaders) { 310 this.firstrowasheaders=firstrowasheaders; 311 } 312 313 /** set the value password 314 * When required by a server, a valid password. 315 * @param password value to set 316 **/ 317 public void setPassword(String password) { 318 this.password=password; 319 } 320 /** set the value password 321 * When required by a proxy server, a valid password. 322 * @param proxypassword value to set 323 **/ 324 public void setProxypassword(String proxypassword) { 325 this.proxypassword=proxypassword; 326 } 327 328 /** set the value delimiter 329 * Required for creating a query. Options are a tab or comma. Default is a comma. 330 * @param delimiter value to set 331 **/ 332 public void setDelimiter(String delimiter) { 333 this.delimiter=delimiter.length()==0?',':delimiter.charAt(0); 334 } 335 336 /** set the value resolveurl 337 * Yes or No. Default is No. For GET and POST operations, if Yes, page reference returned into the 338 * fileContent internal variable has its internal URLs fully resolved, including port number, so that 339 * links remain intact. 340 * @param resolveurl value to set 341 **/ 342 public void setResolveurl(boolean resolveurl) { 343 this.resolveurl=resolveurl; 344 } 345 346 public void setPreauth(boolean preauth) { 347 this.preauth=preauth; 348 } 349 350 351 352 /** set the value timeout 353 * @param timeout value to set 354 * @throws ExpressionException 355 **/ 356 public void setTimeout(double timeout) throws ExpressionException { 357 if(timeout<0) 358 throw new ExpressionException("invalid value ["+Caster.toString(timeout)+"] for attribute timeout, value must be a positive integer greater or equal than 0"); 359 360 long requestTimeout = pageContext.getRequestTimeout(); 361 long _timeout=(long)(timeout*1000D); 362 this.timeout=requestTimeout<_timeout?requestTimeout:_timeout; 363 //print.out("this.timeout:"+this.timeout); 364 } 365 366 /** set the value proxyserver 367 * Host name or IP address of a proxy server. 368 * @param proxyserver value to set 369 **/ 370 public void setProxyserver(String proxyserver) { 371 this.proxyserver=proxyserver; 372 } 373 374 /** set the value proxyport 375 * The port number on the proxy server from which the object is requested. Default is 80. When 376 * used with resolveURL, the URLs of retrieved documents that specify a port number are automatically 377 * resolved to preserve links in the retrieved document. 378 * @param proxyport value to set 379 **/ 380 public void setProxyport(double proxyport) { 381 this.proxyport=(int)proxyport; 382 } 383 384 /** set the value file 385 * The filename to be used for the file that is accessed. For GET operations, defaults to the name 386 * pecified in url. Enter path information in the path attribute. 387 * @param file value to set 388 **/ 389 public void setFile(String file) { 390 this.strFile=file; 391 } 392 393 /** set the value throwonerror 394 * Boolean indicating whether to throw an exception that can be caught by using the cftry and 395 * cfcatch tags. The default is NO. 396 * @param throwonerror value to set 397 **/ 398 public void setThrowonerror(boolean throwonerror) { 399 this.throwonerror=throwonerror; 400 } 401 402 /** set the value charset 403 * set the charset for the call. 404 * @param charset value to set 405 **/ 406 public void setCharset(String charset) { 407 this.charset=charset; 408 } 409 410 /** set the value columns 411 * @param columns value to set 412 * @throws PageException 413 **/ 414 public void setColumns(String columns) throws PageException { 415 this.columns=ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(columns,",")); 416 } 417 418 /** set the value port 419 * The port number on the server from which the object is requested. Default is 80. When used with 420 * resolveURL, the URLs of retrieved documents that specify a port number are automatically resolved to 421 * preserve links in the retrieved document. If a port number is specified in the url attribute, the port 422 * value overrides the value of the port attribute. 423 * @param port value to set 424 **/ 425 public void setPort(double port) { 426 this.port=(int) port; 427 } 428 429 /** set the value useragent 430 * User agent request header. 431 * @param useragent value to set 432 **/ 433 public void setUseragent(String useragent) { 434 this.useragent=useragent; 435 } 436 437 /** set the value textqualifier 438 * Required for creating a query. Indicates the start and finish of a column. Should be 439 * appropriately escaped when embedded in a column. For example, if the qualifier is a double quotation 440 * mark, it should be escaped as """". If there is no text qualifier in the file, specify it as " ". 441 * Default is the double quotation mark ("). 442 * @param textqualifier value to set 443 **/ 444 public void setTextqualifier(String textqualifier) { 445 this.textqualifier=textqualifier.length()==0?'"':textqualifier.charAt(0); 446 } 447 448 /** set the value username 449 * When required by a proxy server, a valid username. 450 * @param proxyuser value to set 451 **/ 452 public void setProxyuser(String proxyuser) { 453 this.proxyuser=proxyuser; 454 } 455 456 /** set the value username 457 * When required by a server, a valid username. 458 * @param username value to set 459 **/ 460 public void setUsername(String username) { 461 this.username=username; 462 } 463 464 /** set the value url 465 * Full URL of the host name or IP address of the server on which the file resides. The URL must be 466 * an absolute URL, including the protocol (http or https) and hostname. It may optionally contain a port 467 * number. Port numbers specified in the url attribute override the port attribute. 468 * @param url value to set 469 **/ 470 public void setUrl(String url) { 471 this.url=url; 472 } 473 474 /** set the value redirect 475 * @param redirect value to set 476 **/ 477 public void setRedirect(boolean redirect) { 478 this.redirect=redirect; 479 } 480 481 /** set the value path 482 * The path to the directory in which a file is to be stored. If a path is not specified in a POST 483 * or GET operation, a variable is created (cfhttp.fileContent) that you can use to display the results 484 * of the POST operation in a cfoutput. 485 * @param path value to set 486 **/ 487 public void setPath(String path) { 488 this.strPath=path; 489 } 490 491 /** set the value name 492 * The name to assign to a query if the a query is constructed from a file. 493 * @param name value to set 494 **/ 495 public void setName(String name) { 496 this.name=name; 497 } 498 499 public void setAuthtype(String strAuthType) throws ExpressionException{ 500 if(StringUtil.isEmpty(strAuthType,true)) return; 501 strAuthType=strAuthType.trim(); 502 if("basic".equalsIgnoreCase(strAuthType)) authType=AUTH_TYPE_BASIC; 503 else if("ntlm".equalsIgnoreCase(strAuthType)) authType=AUTH_TYPE_NTLM; 504 else 505 throw new ExpressionException("invalid value ["+strAuthType+"] for attribute authType, value must be one of the following [basic,ntlm]"); 506 } 507 508 public void setWorkstation(String workStation) { 509 this.workStation=workStation; 510 } 511 512 public void setDomain(String domain) { 513 this.domain=domain; 514 } 515 516 /** set the value method 517 * GET or POST. Use GET to download a text or binary file or to create a query from the contents 518 * of a text file. Use POST to send information to a server page or a CGI program for processing. POST 519 * requires the use of a cfhttpparam tag. 520 * @param method value to set 521 * @throws ApplicationException 522 **/ 523 public void setMethod(String method) throws ApplicationException { 524 method=method.toLowerCase().trim(); 525 if(method.equals("post")) this.method=METHOD_POST; 526 else if(method.equals("get")) this.method=METHOD_GET; 527 else if(method.equals("head")) this.method=METHOD_HEAD; 528 else if(method.equals("delete")) this.method=METHOD_DELETE; 529 else if(method.equals("put")) this.method=METHOD_PUT; 530 else if(method.equals("trace")) this.method=METHOD_TRACE; 531 else if(method.equals("options")) this.method=METHOD_OPTIONS; 532 else if(method.equals("patch")) this.method=METHOD_PATCH; 533 else throw new ApplicationException("invalid method type ["+(method.toUpperCase())+"], valid types are POST,GET,HEAD,DELETE,PUT,TRACE,OPTIONS,PATCH"); 534 } 535 536 537 @Override 538 public int doStartTag() { 539 if(addtoken) { 540 setParam("cookie","cfid",pageContext.getCFID()); 541 setParam("cookie","cftoken",pageContext.getCFToken()); 542 String jsessionid = pageContext.getJSessionId(); 543 if(jsessionid!=null)setParam("cookie","jsessionid",jsessionid); 544 } 545 546 return EVAL_BODY_INCLUDE; 547 } 548 549 private void setParam(String type, String name, String value) { 550 HttpParamBean hpb = new HttpParamBean(); 551 hpb.setType(type); 552 hpb.setName(name); 553 hpb.setValue(value); 554 setParam(hpb); 555 } 556 557 @Override 558 public int doEndTag() throws PageException { 559 Struct cfhttp=new StructImpl(); 560 cfhttp.setEL(ERROR_DETAIL,""); 561 pageContext.setVariable(result,cfhttp); 562 563 // because commons 564 PrintStream out = System.out; 565 try { 566 //System.setOut(new PrintStream(DevNullOutputStream.DEV_NULL_OUTPUT_STREAM)); 567 _doEndTag(cfhttp); 568 return EVAL_PAGE; 569 } 570 catch (IOException e) { 571 throw Caster.toPageException(e); 572 } 573 finally { 574 System.setOut(out); 575 } 576 577 } 578 579 580 581 private void _doEndTag(Struct cfhttp) throws PageException, IOException { 582 BasicHttpParams params = new BasicHttpParams(); 583 DefaultHttpClient client = HTTPEngine4Impl.createClient(params,redirect?HTTPEngine.MAX_REDIRECT:0); 584 585 586 ConfigWeb cw = pageContext.getConfig(); 587 HttpRequestBase req=null; 588 HttpContext httpContext=null; 589 //HttpRequestBase req = init(pageContext.getConfig(),this,client,params,url,port); 590 { 591 if(StringUtil.isEmpty(charset,true)) charset=cw.getWebCharset(); 592 else charset=charset.trim(); 593 594 595 // check if has fileUploads 596 boolean doUploadFile=false; 597 for(int i=0;i<this.params.size();i++) { 598 if((this.params.get(i)).getType().equalsIgnoreCase("file")) { 599 doUploadFile=true; 600 break; 601 } 602 } 603 604 // parse url (also query string) 605 int len=this.params.size(); 606 StringBuilder sbQS=new StringBuilder(); 607 for(int i=0;i<len;i++) { 608 HttpParamBean param=this.params.get(i); 609 String type=param.getType(); 610 // URL 611 if(type.equals("url")) { 612 if(sbQS.length()>0)sbQS.append('&'); 613 sbQS.append(translateEncoding(param.getName(), charset)); 614 sbQS.append('='); 615 sbQS.append(translateEncoding(param.getValueAsString(), charset)); 616 } 617 } 618 String host=null; 619 HttpHost httpHost; 620 try { 621 URL _url = HTTPUtil.toURL(url,port); 622 httpHost = new HttpHost(_url.getHost(),_url.getPort()); 623 host=_url.getHost(); 624 url=_url.toExternalForm(); 625 if(sbQS.length()>0){ 626 // no existing QS 627 if(StringUtil.isEmpty(_url.getQuery())) { 628 url+="?"+sbQS; 629 } 630 else { 631 url+="&"+sbQS; 632 } 633 634 } 635 636 637 } catch (MalformedURLException mue) { 638 throw Caster.toPageException(mue); 639 } 640 641 // select best matching method (get,post, post multpart (file)) 642 643 boolean isBinary = false; 644 boolean doMultiPart=doUploadFile || this.multiPart; 645 HttpPost post=null; 646 HttpEntityEnclosingRequest eem=null; 647 648 649 if(this.method==METHOD_GET) { 650 req=new HttpGet(url); 651 } 652 else if(this.method==METHOD_HEAD) { 653 req=new HttpHead(url); 654 } 655 else if(this.method==METHOD_DELETE) { 656 isBinary=true; 657 req=new HttpDelete(url); 658 } 659 else if(this.method==METHOD_PUT) { 660 isBinary=true; 661 HttpPut put = new HttpPut(url); 662 req=put; 663 eem=put; 664 665 } 666 else if(this.method==METHOD_TRACE) { 667 isBinary=true; 668 req=new HttpTrace(url); 669 } 670 else if(this.method==METHOD_OPTIONS) { 671 isBinary=true; 672 req=new HttpOptions(url); 673 } 674 else if(this.method==METHOD_PATCH) { 675 isBinary=true; 676 eem = HTTPPatchFactory.getHTTPPatch(url); 677 req=(HttpRequestBase) eem; 678 } 679 else { 680 isBinary=true; 681 post=new HttpPost(url); 682 req=post; 683 eem=post; 684 } 685 686 boolean hasForm=false; 687 boolean hasBody=false; 688 boolean hasContentType=false; 689 // Set http params 690 ArrayList<FormBodyPart> parts=new ArrayList<FormBodyPart>(); 691 692 StringBuilder acceptEncoding=new StringBuilder(); 693 java.util.List<NameValuePair> postParam = post!=null?new ArrayList <NameValuePair>():null; 694 695 for(int i=0;i<len;i++) { 696 HttpParamBean param=this.params.get(i); 697 String type=param.getType(); 698 699 // URL 700 if(type.equals("url")) { 701 //listQS.add(new BasicNameValuePair(translateEncoding(param.getName(), http.charset),translateEncoding(param.getValueAsString(), http.charset))); 702 } 703 // Form 704 else if(type.equals("formfield") || type.equals("form")) { 705 hasForm=true; 706 if(this.method==METHOD_GET) throw new ApplicationException("httpparam type formfield can't only be used, when method of the tag http equal post"); 707 if(post!=null){ 708 if(doMultiPart) { 709 parts.add( 710 new FormBodyPart( 711 param.getName(), 712 new StringBody( 713 param.getValueAsString(), 714 CharsetUtil.toCharset(charset) 715 ) 716 ) 717 ); 718 } 719 else { 720 postParam.add(new BasicNameValuePair(param.getName(),param.getValueAsString())); 721 } 722 } 723 //else if(multi!=null)multi.addParameter(param.getName(),param.getValueAsString()); 724 } 725 // CGI 726 else if(type.equals("cgi")) { 727 if(param.isEncoded()) 728 req.addHeader( 729 translateEncoding(param.getName(),charset), 730 translateEncoding(param.getValueAsString(),charset)); 731 else 732 req.addHeader(param.getName(),param.getValueAsString()); 733 } 734 // Header 735 else if(type.startsWith("head")) { 736 if(param.getName().equalsIgnoreCase("content-type")) hasContentType=true; 737 738 if(param.getName().equalsIgnoreCase("Content-Length")) {} 739 else if(param.getName().equalsIgnoreCase("Accept-Encoding")) { 740 acceptEncoding.append(headerValue(param.getValueAsString())); 741 acceptEncoding.append(", "); 742 } 743 else req.addHeader(param.getName(),headerValue(param.getValueAsString())); 744 } 745 // Cookie 746 else if(type.equals("cookie")) { 747 HTTPEngine4Impl.addCookie(client,host,param.getName(),param.getValueAsString(),"/",charset); 748 } 749 // File 750 else if(type.equals("file")) { 751 hasForm=true; 752 if(this.method==METHOD_GET) throw new ApplicationException("httpparam type file can't only be used, when method of the tag http equal post"); 753 String strCT = getContentType(param); 754 ContentType ct = HTTPUtil.toContentType(strCT,null); 755 756 String mt="text/xml"; 757 if(ct!=null && !StringUtil.isEmpty(ct.getMimeType(),true)) mt=ct.getMimeType(); 758 759 String cs=charset; 760 if(ct!=null && !StringUtil.isEmpty(ct.getCharset(),true)) cs=ct.getCharset(); 761 762 763 if(doMultiPart) { 764 try { 765 Resource res = param.getFile(); 766 parts.add(new FormBodyPart( 767 param.getName(), 768 new ResourceBody(res, mt, res.getName(), cs) 769 )); 770 //parts.add(new ResourcePart(param.getName(),new ResourcePartSource(param.getFile()),getContentType(param),_charset)); 771 } 772 catch (FileNotFoundException e) { 773 throw new ApplicationException("can't upload file, path is invalid",e.getMessage()); 774 } 775 } 776 } 777 // XML 778 else if(type.equals("xml")) { 779 ContentType ct = HTTPUtil.toContentType(param.getMimeType(),null); 780 781 String mt="text/xml"; 782 if(ct!=null && !StringUtil.isEmpty(ct.getMimeType(),true)) mt=ct.getMimeType(); 783 784 String cs=charset; 785 if(ct!=null && !StringUtil.isEmpty(ct.getCharset(),true)) cs=ct.getCharset(); 786 787 hasBody=true; 788 hasContentType=true; 789 req.addHeader("Content-type", mt+"; charset="+cs); 790 if(eem==null)throw new ApplicationException("type xml is only supported for type post and put"); 791 HTTPEngine4Impl.setBody(eem, param.getValueAsString(),mt,cs); 792 } 793 // Body 794 else if(type.equals("body")) { 795 ContentType ct = HTTPUtil.toContentType(param.getMimeType(),null); 796 797 String mt=null; 798 if(ct!=null && !StringUtil.isEmpty(ct.getMimeType(),true)) mt=ct.getMimeType(); 799 800 String cs=charset; 801 if(ct!=null && !StringUtil.isEmpty(ct.getCharset(),true)) cs=ct.getCharset(); 802 803 804 hasBody=true; 805 if(eem==null)throw new ApplicationException("type body is only supported for type post and put"); 806 HTTPEngine4Impl.setBody(eem, param.getValue(),mt,cs); 807 808 } 809 else { 810 throw new ApplicationException("invalid type ["+type+"]"); 811 } 812 813 } 814 815 // post params 816 if(postParam!=null && postParam.size()>0) 817 post.setEntity(new org.apache.http.client.entity.UrlEncodedFormEntity(postParam,charset)); 818 819 req.setHeader("Accept-Encoding",acceptEncoding.append("gzip").toString()); 820 821 // multipart 822 if(doMultiPart && eem!=null) { 823 hasContentType=true; 824 boolean doIt=true; 825 if(!this.multiPart && parts.size()==1){ 826 ContentBody body = parts.get(0).getBody(); 827 if(body instanceof StringBody){ 828 StringBody sb=(StringBody)body; 829 try { 830 String str = IOUtil.toString(sb.getReader()); 831 StringEntity entity = new StringEntity(str,sb.getMimeType(),sb.getCharset()); 832 eem.setEntity(entity); 833 834 } catch (IOException e) { 835 throw Caster.toPageException(e); 836 } 837 doIt=false; 838 } 839 } 840 if(doIt) { 841 MultipartEntity mpe = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE,null,CharsetUtil.toCharset(charset)); 842 Iterator<FormBodyPart> it = parts.iterator(); 843 while(it.hasNext()) { 844 FormBodyPart part = it.next(); 845 mpe.addPart(part.getName(),part.getBody()); 846 } 847 eem.setEntity(mpe); 848 } 849 //eem.setRequestEntity(new MultipartRequestEntityFlex(parts.toArray(new Part[parts.size()]), eem.getParams(),http.multiPartType)); 850 } 851 852 853 854 if(hasBody && hasForm) 855 throw new ApplicationException("mixing httpparam type file/formfield and body/XML is not allowed"); 856 857 if(!hasContentType) { 858 if(isBinary) { 859 if(hasBody) req.addHeader("Content-type", "application/octet-stream"); 860 else req.addHeader("Content-type", "application/x-www-form-urlencoded; charset="+charset); 861 } 862 else { 863 if(hasBody) 864 req.addHeader("Content-type", "text/html; charset="+charset ); 865 } 866 } 867 868 869 // set User Agent 870 if(!hasHeaderIgnoreCase(req,"User-Agent")) 871 req.setHeader("User-Agent",this.useragent); 872 873 // set timeout 874 if(this.timeout>0L)HTTPEngine4Impl.setTimeout(params, (int)this.timeout); 875 876 // set Username and Password 877 if(this.username!=null) { 878 if(this.password==null)this.password=""; 879 if(AUTH_TYPE_NTLM==this.authType) { 880 if(StringUtil.isEmpty(this.workStation,true)) 881 throw new ApplicationException("attribute workstation is required when authentication type is [NTLM]"); 882 if(StringUtil.isEmpty(this.domain,true)) 883 throw new ApplicationException("attribute domain is required when authentication type is [NTLM]"); 884 885 HTTPEngine4Impl.setNTCredentials(client, this.username, this.password, this.workStation,this.domain); 886 } 887 else httpContext=HTTPEngine4Impl.setCredentials(client, httpHost, this.username, this.password,preauth); 888 } 889 890 // set Proxy 891 ProxyData proxy=null; 892 if(!StringUtil.isEmpty(this.proxyserver)) { 893 proxy=ProxyDataImpl.getInstance(this.proxyserver, this.proxyport, this.proxyuser, this.proxypassword) ; 894 } 895 if(pageContext.getConfig().isProxyEnableFor(host)) { 896 proxy=pageContext.getConfig().getProxyData(); 897 } 898 HTTPEngine4Impl.setProxy(client, req, proxy); 899 900 } 901 902 903 904 905 906 try { 907 if(httpContext==null)httpContext = new BasicHttpContext(); 908 909 /////////////////////////////////////////// EXECUTE ///////////////////////////////////////////////// 910 Executor4 e = new Executor4(this,client,httpContext,req,redirect); 911 HTTPResponse4Impl rsp=null; 912 if(timeout<0){ 913 try{ 914 rsp = e.execute(httpContext); 915 } 916 917 catch(Throwable t){ 918 if(!throwonerror){ 919 setUnknownHost(cfhttp, t); 920 return; 921 } 922 throw toPageException(t); 923 924 } 925 } 926 else { 927 e.start(); 928 try { 929 synchronized(this){//print.err(timeout); 930 this.wait(timeout); 931 } 932 } catch (InterruptedException ie) { 933 throw Caster.toPageException(ie); 934 } 935 if(e.t!=null){ 936 if(!throwonerror){ 937 setUnknownHost(cfhttp,e.t); 938 return; 939 } 940 throw toPageException(e.t); 941 } 942 943 rsp=e.response; 944 945 946 if(!e.done){ 947 req.abort(); 948 if(throwonerror) 949 throw new HTTPException("408 Request Time-out","a timeout occurred in tag http",408,"Time-out",rsp.getURL()); 950 setRequestTimeout(cfhttp); 951 return; 952 //throw new ApplicationException("timeout"); 953 } 954 } 955 956 /////////////////////////////////////////// EXECUTE ///////////////////////////////////////////////// 957 String responseCharset=rsp.getCharset(); 958 // Write Response Scope 959 //String rawHeader=httpMethod.getStatusLine().toString(); 960 String mimetype=null; 961 String contentEncoding=null; 962 963 // status code 964 cfhttp.set(STATUSCODE,((rsp.getStatusCode()+" "+rsp.getStatusText()).trim())); 965 cfhttp.set(STATUS_CODE,new Double(rsp.getStatusCode())); 966 cfhttp.set(STATUS_TEXT,(rsp.getStatusText())); 967 cfhttp.set(HTTP_VERSION,(rsp.getProtocolVersion())); 968 969 //responseHeader 970 railo.commons.net.http.Header[] headers = rsp.getAllHeaders(); 971 StringBuffer raw=new StringBuffer(rsp.getStatusLine()+" "); 972 Struct responseHeader = new StructImpl(); 973 Array setCookie = new ArrayImpl(); 974 975 for(int i=0;i<headers.length;i++) { 976 railo.commons.net.http.Header header=headers[i]; 977 //print.ln(header); 978 979 raw.append(header.toString()+" "); 980 if(header.getName().equalsIgnoreCase("Set-Cookie")) 981 setCookie.append(header.getValue()); 982 else { 983 //print.ln(header.getName()+"-"+header.getValue()); 984 Object value=responseHeader.get(KeyImpl.getInstance(header.getName()),null); 985 if(value==null) responseHeader.set(KeyImpl.getInstance(header.getName()),header.getValue()); 986 else { 987 Array arr=null; 988 if(value instanceof Array) { 989 arr=(Array) value; 990 } 991 else { 992 arr=new ArrayImpl(); 993 responseHeader.set(KeyImpl.getInstance(header.getName()),arr); 994 arr.appendEL(value); 995 } 996 arr.appendEL(header.getValue()); 997 } 998 } 999 1000 // Content-Type 1001 if(header.getName().equalsIgnoreCase("Content-Type")) { 1002 mimetype=header.getValue(); 1003 if(mimetype==null)mimetype=NO_MIMETYPE; 1004 } 1005 1006 // Content-Encoding 1007 if(header.getName().equalsIgnoreCase("Content-Encoding")) { 1008 contentEncoding=header.getValue(); 1009 } 1010 1011 } 1012 cfhttp.set(RESPONSEHEADER,responseHeader); 1013 responseHeader.set(STATUS_CODE,new Double(rsp.getStatusCode())); 1014 responseHeader.set(EXPLANATION,(rsp.getStatusText())); 1015 if(setCookie.size()>0)responseHeader.set(SET_COOKIE,setCookie); 1016 1017 // is text 1018 boolean isText= 1019 mimetype == null || 1020 mimetype == NO_MIMETYPE || HTTPUtil.isTextMimeType(mimetype); 1021 1022 // is multipart 1023 boolean isMultipart= MultiPartResponseUtils.isMultipart(mimetype); 1024 1025 cfhttp.set(KeyConstants._text,Caster.toBoolean(isText)); 1026 1027 // mimetype charset 1028 //boolean responseProvideCharset=false; 1029 if(!StringUtil.isEmpty(mimetype,true)){ 1030 if(isText) { 1031 String[] types=HTTPUtil.splitMimeTypeAndCharset(mimetype,null); 1032 if(types[0]!=null)cfhttp.set(KeyConstants._mimetype,types[0]); 1033 if(types[1]!=null)cfhttp.set(CHARSET,types[1]); 1034 1035 } 1036 else cfhttp.set(KeyConstants._mimetype,mimetype); 1037 } 1038 else cfhttp.set(KeyConstants._mimetype,NO_MIMETYPE); 1039 1040 // File 1041 Resource file=null; 1042 1043 if(strFile!=null && strPath!=null) { 1044 file=ResourceUtil.toResourceNotExisting(pageContext, strPath).getRealResource(strFile); 1045 } 1046 else if(strFile!=null) { 1047 file=ResourceUtil.toResourceNotExisting(pageContext, strFile); 1048 } 1049 else if(strPath!=null) { 1050 file=ResourceUtil.toResourceNotExisting(pageContext, strPath); 1051 //Resource dir = file.getParentResource(); 1052 if(file.isDirectory()){ 1053 file=file.getRealResource(req.getURI().getPath());// TODO was getName() ->http://hc.apache.org/httpclient-3.x/apidocs/org/apache/commons/httpclient/URI.html#getName() 1054 } 1055 1056 } 1057 if(file!=null)pageContext.getConfig().getSecurityManager().checkFileLocation(file); 1058 1059 1060 // filecontent 1061 //try { 1062 //print.ln(">> "+responseCharset); 1063 1064 InputStream is=null; 1065 if(isText && getAsBinary!=GET_AS_BINARY_YES) { 1066 String str; 1067 try { 1068 is = rsp.getContentAsStream(); 1069 if(is!=null &&isGzipEncoded(contentEncoding)) 1070 is = rsp.getStatusCode()!=200? new CachingGZIPInputStream(is):new GZIPInputStream(is); 1071 1072 try { 1073 try{ 1074 str = is==null?"":IOUtil.toString(is,responseCharset); 1075 } 1076 catch(EOFException eof){ 1077 if(is instanceof CachingGZIPInputStream) { 1078 str = IOUtil.toString(is=((CachingGZIPInputStream)is).getRawData(),responseCharset); 1079 } 1080 else throw eof; 1081 } 1082 } 1083 catch (UnsupportedEncodingException uee) { 1084 str = IOUtil.toString(is,null); 1085 } 1086 } 1087 catch (IOException ioe) { 1088 throw Caster.toPageException(ioe); 1089 } 1090 finally { 1091 IOUtil.closeEL(is); 1092 } 1093 1094 if(str==null)str=""; 1095 if(resolveurl){ 1096 //if(e.redirectURL!=null)url=e.redirectURL.toExternalForm(); 1097 str=new URLResolver().transform(str,e.response.getTargetURL(),false); 1098 } 1099 cfhttp.set(FILE_CONTENT,str); 1100 try { 1101 if(file!=null){ 1102 IOUtil.write(file,str,pageContext.getConfig().getWebCharset(),false); 1103 } 1104 } 1105 catch (IOException e1) {} 1106 1107 if(name!=null) { 1108 Query qry = CSVParser.toQuery( str, delimiter, textqualifier, columns, firstrowasheaders ); 1109 pageContext.setVariable(name,qry); 1110 } 1111 } 1112 // Binary 1113 else { 1114 byte[] barr=null; 1115 if(isGzipEncoded(contentEncoding)){ 1116 is=rsp.getContentAsStream(); 1117 is = rsp.getStatusCode()!=200?new CachingGZIPInputStream(is) :new GZIPInputStream(is); 1118 try { 1119 try{ 1120 barr = IOUtil.toBytes(is); 1121 } 1122 catch(EOFException eof){ 1123 if(is instanceof CachingGZIPInputStream) 1124 barr = IOUtil.toBytes(((CachingGZIPInputStream)is).getRawData()); 1125 else throw eof; 1126 } 1127 } 1128 catch (IOException t) { 1129 throw Caster.toPageException(t); 1130 } 1131 finally{ 1132 IOUtil.closeEL(is); 1133 } 1134 } 1135 else { 1136 try { 1137 barr = rsp.getContentAsByteArray(); 1138 } 1139 catch (IOException t) { 1140 throw Caster.toPageException(t); 1141 } 1142 } 1143 //IF Multipart response get file content and parse parts 1144 if(isMultipart) { 1145 cfhttp.set(FILE_CONTENT,MultiPartResponseUtils.getParts(barr,mimetype)); 1146 } else { 1147 cfhttp.set(FILE_CONTENT,barr); 1148 } 1149 1150 if(file!=null) { 1151 try { 1152 if(barr!=null)IOUtil.copy(new ByteArrayInputStream(barr),file,true); 1153 } 1154 catch (IOException ioe) { 1155 throw Caster.toPageException(ioe); 1156 } 1157 } 1158 } 1159 1160 // header 1161 cfhttp.set(KeyConstants._header,raw.toString()); 1162 if(!isStatusOK(rsp.getStatusCode())){ 1163 String msg=rsp.getStatusCode()+" "+rsp.getStatusText(); 1164 cfhttp.setEL(ERROR_DETAIL,msg); 1165 if(throwonerror){ 1166 throw new HTTPException(msg,null,rsp.getStatusCode(),rsp.getStatusText(),rsp.getURL()); 1167 } 1168 } 1169 } 1170 finally { 1171 //rsp.release(); 1172 } 1173 1174 } 1175 1176 public static boolean isStatusOK(int statusCode) { 1177 return statusCode>=200 && statusCode<=299; 1178 } 1179 1180 private PageException toPageException(Throwable t) { 1181 PageException pe = Caster.toPageException(t); 1182 if(pe instanceof NativeException) { 1183 ((NativeException) pe).setAdditional(KeyConstants._url, url); 1184 } 1185 return pe; 1186 } 1187 1188 private void setUnknownHost(Struct cfhttp,Throwable t) { 1189 cfhttp.setEL(CHARSET,""); 1190 cfhttp.setEL(ERROR_DETAIL,"Unknown host: "+t.getMessage()); 1191 cfhttp.setEL(FILE_CONTENT,"Connection Failure"); 1192 cfhttp.setEL(KeyConstants._header,""); 1193 cfhttp.setEL(KeyConstants._mimetype,"Unable to determine MIME type of file."); 1194 cfhttp.setEL(RESPONSEHEADER,new StructImpl()); 1195 cfhttp.setEL(STATUSCODE,"Connection Failure. Status code unavailable."); 1196 cfhttp.setEL(KeyConstants._text,Boolean.TRUE); 1197 } 1198 1199 private void setRequestTimeout(Struct cfhttp) { 1200 cfhttp.setEL(CHARSET,""); 1201 cfhttp.setEL(ERROR_DETAIL,""); 1202 cfhttp.setEL(FILE_CONTENT,"Connection Timeout"); 1203 cfhttp.setEL(KeyConstants._header,""); 1204 cfhttp.setEL(KeyConstants._mimetype,"Unable to determine MIME type of file."); 1205 cfhttp.setEL(RESPONSEHEADER,new StructImpl()); 1206 cfhttp.setEL(STATUSCODE,"408 Request Time-out"); 1207 cfhttp.setEL(STATUS_CODE,new Double(408)); 1208 cfhttp.setEL(STATUS_TEXT,"Request Time-out"); 1209 cfhttp.setEL(KeyConstants._text,Boolean.TRUE); 1210 } 1211 1212 /*private static HttpMethod execute(Http http, HttpClient client, HttpMethod httpMethod, boolean redirect) throws PageException { 1213 try { 1214 // Execute Request 1215 short count=0; 1216 URL lu; 1217 1218 while(isRedirect(client.executeMethod(httpMethod)) && redirect && count++ < MAX_REDIRECT) { 1219 lu=locationURL(httpMethod); 1220 httpMethod=createMethod(http,client,lu.toExternalForm(),-1); 1221 } 1222 } 1223 catch (IOException e) { 1224 PageException pe = Caster.toPageException(e); 1225 if(pe instanceof NativeException) { 1226 ((NativeException) pe).setAdditional("url", HTTPUtil.toURL(httpMethod)); 1227 } 1228 throw pe; 1229 } 1230 return httpMethod; 1231 }*/ 1232 1233 1234 /*static URL locationURL(HttpMethod method) throws MalformedURLException, ExpressionException { 1235 Header location = method.getResponseHeader("location"); 1236 1237 if(location==null) throw new ExpressionException("missing location header definition"); 1238 1239 1240 HostConfiguration config = method.getHostConfiguration(); 1241 URL url; 1242 try { 1243 url = new URL(location.getValue()); 1244 } 1245 catch (MalformedURLException e) { 1246 url=new URL(config.getProtocol().getScheme(), 1247 config.getHost(), 1248 config.getPort(), 1249 mergePath(method.getPath(),location.getValue())); 1250 } 1251 1252 return url; 1253 }*/ 1254 1255 1256 /*static HttpRequestBase init(Config cw,Http4 http, DefaultHttpClient client, HttpParams params, String url, int port) throws PageException, IOException { 1257 String charset=http.charset; 1258 if(StringUtil.isEmpty(charset,true)) charset=cw.getWebCharset(); 1259 else charset=charset.trim(); 1260 1261 HttpRequestBase req; 1262 1263 // check if has fileUploads 1264 boolean doUploadFile=false; 1265 for(int i=0;i<http.params.size();i++) { 1266 if((http.params.get(i)).getType().equals("file")) { 1267 doUploadFile=true; 1268 break; 1269 } 1270 } 1271 1272 // parse url (also query string) 1273 int len=http.params.size(); 1274 StringBuilder sbQS=new StringBuilder(); 1275 for(int i=0;i<len;i++) { 1276 HttpParamBean param=http.params.get(i); 1277 String type=param.getType(); 1278 // URL 1279 if(type.equals("url")) { 1280 if(sbQS.length()>0)sbQS.append('&'); 1281 sbQS.append(translateEncoding(param.getName(), charset)); 1282 sbQS.append('='); 1283 sbQS.append(translateEncoding(param.getValueAsString(), charset)); 1284 } 1285 } 1286 String host=null; 1287 HttpHost httpHost; 1288 try { 1289 URL _url = HTTPUtil.toURL(url,port); 1290 httpHost = new HttpHost(_url.getHost(),_url.getPort()); 1291 host=_url.getHost(); 1292 url=_url.toExternalForm(); 1293 if(sbQS.length()>0){ 1294 // no existing QS 1295 if(StringUtil.isEmpty(_url.getQuery())) { 1296 url+="?"+sbQS; 1297 } 1298 else { 1299 url+="&"+sbQS; 1300 } 1301 1302 } 1303 1304 1305 } catch (MalformedURLException mue) { 1306 throw Caster.toPageException(mue); 1307 } 1308 1309 // select best matching method (get,post, post multpart (file)) 1310 1311 boolean isBinary = false; 1312 boolean doMultiPart=doUploadFile || http.multiPart; 1313 HttpPost post=null; 1314 HttpEntityEnclosingRequest eem=null; 1315 1316 1317 if(http.method==METHOD_GET) { 1318 req=new HttpGet(url); 1319 } 1320 else if(http.method==METHOD_HEAD) { 1321 req=new HttpHead(url); 1322 } 1323 else if(http.method==METHOD_DELETE) { 1324 isBinary=true; 1325 req=new HttpDelete(url); 1326 } 1327 else if(http.method==METHOD_PUT) { 1328 isBinary=true; 1329 HttpPut put = new HttpPut(url); 1330 req=put; 1331 eem=put; 1332 1333 } 1334 else if(http.method==METHOD_TRACE) { 1335 isBinary=true; 1336 req=new HttpTrace(url); 1337 } 1338 else if(http.method==METHOD_OPTIONS) { 1339 isBinary=true; 1340 req=new HttpOptions(url); 1341 } 1342 else { 1343 isBinary=true; 1344 post=new HttpPost(url); 1345 req=post; 1346 eem=post; 1347 } 1348 1349 boolean hasForm=false; 1350 boolean hasBody=false; 1351 boolean hasContentType=false; 1352 // Set http params 1353 ArrayList<FormBodyPart> parts=new ArrayList<FormBodyPart>(); 1354 1355 StringBuilder acceptEncoding=new StringBuilder(); 1356 java.util.List<NameValuePair> postParam = post!=null?new ArrayList <NameValuePair>():null; 1357 1358 for(int i=0;i<len;i++) { 1359 HttpParamBean param=http.params.get(i); 1360 String type=param.getType(); 1361 // URL 1362 if(type.equals("url")) { 1363 //listQS.add(new BasicNameValuePair(translateEncoding(param.getName(), http.charset),translateEncoding(param.getValueAsString(), http.charset))); 1364 } 1365 // Form 1366 else if(type.equals("formfield") || type.equals("form")) { 1367 hasForm=true; 1368 if(http.method==METHOD_GET) throw new ApplicationException("httpparam type formfield can't only be used, when method of the tag http equal post"); 1369 if(post!=null){ 1370 if(doMultiPart){ 1371 parts.add( 1372 new FormBodyPart( 1373 param.getName(), 1374 new StringBody( 1375 param.getValueAsString(), 1376 CharsetUtil.toCharset(charset) 1377 ) 1378 ) 1379 ); 1380 } 1381 else { 1382 postParam.add(new BasicNameValuePair(param.getName(),param.getValueAsString())); 1383 } 1384 } 1385 //else if(multi!=null)multi.addParameter(param.getName(),param.getValueAsString()); 1386 } 1387 // CGI 1388 else if(type.equals("cgi")) { 1389 if(param.isEncoded()) 1390 req.addHeader( 1391 translateEncoding(param.getName(),charset), 1392 translateEncoding(param.getValueAsString(),charset)); 1393 else 1394 req.addHeader(param.getName(),param.getValueAsString()); 1395 } 1396 // Header 1397 else if(type.startsWith("head")) { 1398 if(param.getName().equalsIgnoreCase("content-type")) hasContentType=true; 1399 1400 if(param.getName().equalsIgnoreCase("Accept-Encoding")) { 1401 acceptEncoding.append(headerValue(param.getValueAsString())); 1402 acceptEncoding.append(", "); 1403 } 1404 else req.addHeader(param.getName(),headerValue(param.getValueAsString())); 1405 } 1406 // Cookie 1407 else if(type.equals("cookie")) { 1408 HTTPEngine4Impl.addCookie(client,host,param.getName(),param.getValueAsString(),"/",charset); 1409 } 1410 // File 1411 else if(type.equals("file")) { 1412 hasForm=true; 1413 if(http.method==METHOD_GET) throw new ApplicationException("httpparam type file can't only be used, when method of the tag http equal post"); 1414 if(doMultiPart) { 1415 try { 1416 Resource res = param.getFile(); 1417 parts.add(new FormBodyPart( 1418 param.getName(), 1419 new ResourceBody(res, getContentType(param), res.getName(), charset) 1420 )); 1421 //parts.add(new ResourcePart(param.getName(),new ResourcePartSource(param.getFile()),getContentType(param),_charset)); 1422 } 1423 catch (FileNotFoundException e) { 1424 throw new ApplicationException("can't upload file, path is invalid",e.getMessage()); 1425 } 1426 } 1427 } 1428 // XML 1429 else if(type.equals("xml")) { 1430 hasBody=true; 1431 hasContentType=true; 1432 req.addHeader("Content-type", "text/xml; charset="+charset); 1433 if(eem==null)throw new ApplicationException("type xml is only supported for type post and put"); 1434 HTTPEngine4Impl.setBody(eem, param.getValueAsString()); 1435 } 1436 // Body 1437 else if(type.equals("body")) { 1438 hasBody=true; 1439 if(eem==null)throw new ApplicationException("type body is only supported for type post and put"); 1440 HTTPEngine4Impl.setBody(eem, param.getValue()); 1441 1442 } 1443 else { 1444 throw new ApplicationException("invalid type ["+type+"]"); 1445 } 1446 1447 } 1448 1449 // post params 1450 if(postParam!=null && postParam.size()>0) 1451 post.setEntity(new org.apache.http.client.entity.UrlEncodedFormEntity(postParam,charset)); 1452 1453 req.setHeader("Accept-Encoding",acceptEncoding.append("gzip").toString()); 1454 1455 // multipart 1456 if(doMultiPart && eem!=null) { 1457 hasContentType=true; 1458 boolean doIt=true; 1459 if(!http.multiPart && parts.size()==1){ 1460 ContentBody body = parts.get(0).getBody(); 1461 if(body instanceof StringBody){ 1462 StringBody sb=(StringBody)body; 1463 try { 1464 String str = IOUtil.toString(sb.getReader()); 1465 StringEntity entity = new StringEntity(str,sb.getMimeType(),sb.getCharset()); 1466 eem.setEntity(entity); 1467 1468 } catch (IOException e) { 1469 throw Caster.toPageException(e); 1470 } 1471 doIt=false; 1472 } 1473 } 1474 if(doIt) { 1475 MultipartEntity mpe = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE,null,CharsetUtil.toCharset(charset)); 1476 Iterator<FormBodyPart> it = parts.iterator(); 1477 while(it.hasNext()) { 1478 mpe.addPart(it.next()); 1479 } 1480 eem.setEntity(mpe); 1481 } 1482 //eem.setRequestEntity(new MultipartRequestEntityFlex(parts.toArray(new Part[parts.size()]), eem.getParams(),http.multiPartType)); 1483 } 1484 1485 1486 1487 if(hasBody && hasForm) 1488 throw new ApplicationException("mixing httpparam type file/formfield and body/XML is not allowed"); 1489 1490 if(!hasContentType) { 1491 if(isBinary) { 1492 if(hasBody) req.addHeader("Content-type", "application/octet-stream"); 1493 else req.addHeader("Content-type", "application/x-www-form-urlencoded; charset="+charset); 1494 } 1495 else { 1496 if(hasBody) 1497 req.addHeader("Content-type", "text/html; charset="+charset ); 1498 } 1499 } 1500 1501 1502 // set User Agent 1503 if(!hasHeaderIgnoreCase(req,"User-Agent")) 1504 req.setHeader("User-Agent",http.useragent); 1505 1506 // set timeout 1507 if(http.timeout>0L)HTTPEngine4Impl.setTimeout(params, (int)http.timeout); 1508 1509 // set Username and Password 1510 BasicHttpContext httpContext=null; 1511 if(http.username!=null) { 1512 if(http.password==null)http.password=""; 1513 if(AUTH_TYPE_NTLM==http.authType) { 1514 if(StringUtil.isEmpty(http.workStation,true)) 1515 throw new ApplicationException("attribute workstation is required when authentication type is [NTLM]"); 1516 if(StringUtil.isEmpty(http.domain,true)) 1517 throw new ApplicationException("attribute domain is required when authentication type is [NTLM]"); 1518 1519 HTTPEngine4Impl.setNTCredentials(client, http.username, http.password, http.workStation,http.domain); 1520 } 1521 else httpContext=HTTPEngine4Impl.setCredentials(client, httpHost, http.username, http.password); 1522 } 1523 1524 // set Proxy 1525 ProxyData proxy=null; 1526 if(!StringUtil.isEmpty(http.proxyserver)) { 1527 proxy=ProxyDataImpl.getInstance(http.proxyserver, http.proxyport, http.proxyuser, http.proxypassword) ; 1528 } 1529 if(http.pageContext.getConfig().isProxyEnableFor(host)) { 1530 proxy=http.pageContext.getConfig().getProxyData(); 1531 } 1532 HTTPEngine4Impl.setProxy(client, req, proxy); 1533 1534 return req; 1535 }*/ 1536 1537 private static boolean hasHeaderIgnoreCase(HttpRequestBase req,String name) { 1538 org.apache.http.Header[] headers = req.getAllHeaders(); 1539 if(headers==null) return false; 1540 for(int i=0;i<headers.length;i++){ 1541 if(name.equalsIgnoreCase(headers[i].getName())) return true; 1542 } 1543 return false; 1544 } 1545 1546 private static String headerValue(String value) { 1547 if(value==null) return null; 1548 value=value.trim(); 1549 value=value.replace('\n', ' '); 1550 value=value.replace('\r', ' '); 1551 /*int len=value.length(); 1552 char c; 1553 for(int i=0;i<len;i++){ 1554 c=value.charAt(i); 1555 if(c=='\n' || c=='\r') return value.substring(0,i); 1556 }*/ 1557 return value; 1558 } 1559 1560 private static String toQueryString(NameValuePair[] qsPairs) { 1561 StringBuffer sb=new StringBuffer(); 1562 for(int i=0;i<qsPairs.length;i++) { 1563 if(sb.length()>0)sb.append('&'); 1564 sb.append(qsPairs[i].getName()); 1565 if(qsPairs[i].getValue()!=null){ 1566 sb.append('='); 1567 sb.append(qsPairs[i].getValue()); 1568 } 1569 } 1570 return sb.toString(); 1571 } 1572 1573 private static String translateEncoding(String str, String charset) throws UnsupportedEncodingException { 1574 if(!ReqRspUtil.needEncoding(str,false)) return str; 1575 return URLEncoder.encode(str,charset); 1576 } 1577 1578 @Override 1579 public void doInitBody() { 1580 1581 } 1582 1583 @Override 1584 public int doAfterBody() { 1585 return SKIP_BODY; 1586 } 1587 1588 /** 1589 * sets if has body or not 1590 * @param hasBody 1591 */ 1592 public void hasBody(boolean hasBody) { 1593 1594 } 1595 1596 /** 1597 * @param param 1598 */ 1599 public void setParam(HttpParamBean param) { 1600 params.add(param); 1601 1602 } 1603 1604 1605 /** 1606 * @param getAsBinary The getasbinary to set. 1607 */ 1608 public void setGetasbinary(String getAsBinary) { 1609 // TODO support never, wird das verwendet? 1610 getAsBinary=getAsBinary.toLowerCase().trim(); 1611 if(getAsBinary.equals("yes") || getAsBinary.equals("true")) this.getAsBinary=GET_AS_BINARY_YES; 1612 else if(getAsBinary.equals("no") || getAsBinary.equals("false")) this.getAsBinary=GET_AS_BINARY_NO; 1613 else if(getAsBinary.equals("auto")) this.getAsBinary=GET_AS_BINARY_AUTO; 1614 } 1615 1616 /** 1617 * @param multipart The multipart to set. 1618 */ 1619 public void setMultipart(boolean multiPart) { 1620 this.multiPart = multiPart; 1621 } 1622 1623 /** 1624 * @param multipart The multipart to set. 1625 * @throws ApplicationException 1626 */ 1627 public void setMultiparttype(String multiPartType) throws ApplicationException { 1628 if(StringUtil.isEmpty(multiPartType))return; 1629 multiPartType=multiPartType.trim().toLowerCase(); 1630 1631 if("form-data".equals(multiPartType)) this.multiPartType=MULTIPART_FORM_DATA; 1632 //else if("related".equals(multiPartType)) this.multiPartType=MultipartRequestEntityFlex.MULTIPART_RELATED; 1633 else 1634 throw new ApplicationException("invalid value for attribute multiPartType ["+multiPartType+"]", 1635 "attribute must have one of the folloing values [form-data]"); 1636 1637 } 1638 1639 /** 1640 * @param result The result to set. 1641 */ 1642 public void setResult(String result) { 1643 this.result = result; 1644 } 1645 1646 /** 1647 * @param addtoken the addtoken to set 1648 */ 1649 public void setAddtoken(boolean addtoken) { 1650 this.addtoken = addtoken; 1651 } 1652 1653 /** 1654 * checks if status code is a redirect 1655 * @param status 1656 * @return is redirect 1657 */ 1658 1659 static boolean isRedirect(int status) { 1660 return 1661 status==STATUS_REDIRECT_FOUND || 1662 status==STATUS_REDIRECT_MOVED_PERMANENTLY || 1663 status==STATUS_REDIRECT_SEE_OTHER || 1664 status==STATUS_REDIRECT_TEMPORARY_REDIRECT; 1665 1666 1667 } 1668 1669 /** 1670 * merge to pathes to one 1671 * @param current 1672 * @param realPath 1673 * @return 1674 * @throws MalformedURLException 1675 */ 1676 public static String mergePath(String current, String realPath) throws MalformedURLException { 1677 1678 // get current directory 1679 String currDir; 1680 if(current==null || current.indexOf('/')==-1)currDir="/"; 1681 else if(current.endsWith("/"))currDir=current; 1682 else currDir=current.substring(0,current.lastIndexOf('/')+1); 1683 1684 // merge together 1685 String path; 1686 if(realPath.startsWith("./"))path=currDir+realPath.substring(2); 1687 else if(realPath.startsWith("/"))path=realPath; 1688 else if(!realPath.startsWith("../"))path=currDir+realPath; 1689 else { 1690 while(realPath.startsWith("../") || currDir.length()==0) { 1691 realPath=realPath.substring(3); 1692 currDir=currDir.substring(0,currDir.length()-1); 1693 int index = currDir.lastIndexOf('/'); 1694 if(index==-1)throw new MalformedURLException("invalid realpath definition for URL"); 1695 currDir=currDir.substring(0,index+1); 1696 } 1697 path=currDir+realPath; 1698 } 1699 1700 return path; 1701 } 1702 1703 private static String getContentType(HttpParamBean param) { 1704 String mimeType=param.getMimeType(); 1705 if(StringUtil.isEmpty(mimeType,true)) { 1706 mimeType=ResourceUtil.getMimeType(param.getFile(), ResourceUtil.MIMETYPE_CHECK_EXTENSION+ResourceUtil.MIMETYPE_CHECK_HEADER, null); 1707 } 1708 return mimeType; 1709 } 1710 1711 public static boolean isGzipEncoded(String contentEncoding) { 1712 return !StringUtil.isEmpty(contentEncoding) && StringUtil.indexOfIgnoreCase(contentEncoding, "gzip")!=-1; 1713 } 1714 1715 public static Object getOutput(InputStream is, String contentType, String contentEncoding, boolean closeIS) { 1716 if(StringUtil.isEmpty(contentType))contentType="text/html"; 1717 1718 // Gzip 1719 if(Http4.isGzipEncoded(contentEncoding)){ 1720 try { 1721 is=new GZIPInputStream(is); 1722 } 1723 catch (IOException e) {} 1724 } 1725 1726 try { 1727 // text 1728 if(HTTPUtil.isTextMimeType(contentType)) { 1729 String[] tmp = HTTPUtil.splitMimeTypeAndCharset(contentType,null); 1730 //String mimetype=tmp[0]; 1731 String charset=tmp[1]; 1732 1733 if(StringUtil.isEmpty(charset,true)) { 1734 Config config = ThreadLocalPageContext.getConfig(); 1735 if(config!=null)charset=config.getWebCharset(); 1736 } 1737 1738 try { 1739 return IOUtil.toString(is, charset); 1740 } catch (IOException e) {} 1741 } 1742 // Binary 1743 else { 1744 try { 1745 return IOUtil.toBytes(is); 1746 } 1747 catch (IOException e) {} 1748 } 1749 } 1750 finally{ 1751 if(closeIS)IOUtil.closeEL(is); 1752 } 1753 1754 return ""; 1755 } 1756 1757 public static URL locationURL(HttpUriRequest req, HttpResponse rsp) { 1758 URL url=null; 1759 try { 1760 url = req.getURI().toURL(); 1761 } catch (MalformedURLException e1) { 1762 return null; 1763 } 1764 1765 Header h = HTTPResponse4Impl.getLastHeaderIgnoreCase(rsp, "location"); 1766 if(h!=null) { 1767 String str = h.getValue(); 1768 try { 1769 return new URL(str); 1770 } catch (MalformedURLException e) { 1771 try { 1772 return new URL(url.getProtocol(), url.getHost(), url.getPort(), mergePath(url.getFile(), str)); 1773 1774 } catch (MalformedURLException e1) { 1775 return null; 1776 } 1777 } 1778 } 1779 return null; 1780 } 1781 1782 1783 } 1784 1785 class Executor4 extends Thread { 1786 1787 final Http4 http; 1788 private final DefaultHttpClient client; 1789 final boolean redirect; 1790 Throwable t; 1791 boolean done; 1792 //URL redirectURL; 1793 HTTPResponse4Impl response; 1794 private HttpRequestBase req; 1795 private HttpContext context; 1796 1797 public Executor4(Http4 http,DefaultHttpClient client, HttpContext context, HttpRequestBase req, boolean redirect) { 1798 this.http=http; 1799 this.client=client; 1800 this.context=context; 1801 this.redirect=redirect; 1802 this.req=req; 1803 } 1804 1805 @Override 1806 public void run(){ 1807 try { 1808 response=execute(context); 1809 done=true; 1810 } 1811 catch (Throwable t) { 1812 this.t=t; 1813 } 1814 finally { 1815 SystemUtil.notify(http); 1816 } 1817 } 1818 1819 public HTTPResponse4Impl execute(HttpContext context) throws IOException { 1820 return response=new HTTPResponse4Impl(null,context,req,client.execute(req,context)); 1821 } 1822 }