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    }